C++ 學習 指針上

🙋 繼續C++ Primer 第五版的學習

注 后面還會有關于指針進一步的學習 本篇為基礎篇
🌿可以先看看這兩篇 或許可以進一步加深一下對指針的理解

指針和數組

指針簡介

🌈 上一次講了 C++中的引用,現在總結一下指針和引用的主要區別。

1?? 指針是一個變量,其存儲的是另一個變量的地址。而引用是一個別名,它和被它引用的變量共享同一內存地址。
2??指針可以被賦予空值(即NULL),而引用必須在定義時初始化,并且不能被賦予空值。
3??對指針的操作不會影響其指向的變量,除非通過指針進行賦值或解引。而對引用的操作會影響其引用的變量。

🌞下面就開始指針的學習叭~

指針

指針(pointer)是“指向(point to)”另外一種類型的復合類型。與引用類似,指針也實現了對其他對象的間接訪問。然而指針與引用相比又有很多不同點。

其一,指針本身就是一個對象,允許對指針賦值和拷貝,而且在指針的生命周期內它可以先后指向幾個不同的對象

其二,指針無須在定義時賦初值。和其他內置類型一樣,在塊作用域內定義的指針如果沒有被初始化,也將擁有一個不確定的值。

定義指針類型的方法將聲明符寫成* d的形式,其中d是變量名。如果在一條語句中定義了幾個指針變量,每個變量前面都必須有符號*:

int *ip1, *ip2;//ip1和ip2都是指向int型對象的指針
double dp,*dp2;// dp2是指向double型對象的指針,dp是double型對象

獲取對象的地址

指針存放某個對象的地址,要想獲取該地址,需要使用取地址符(操作符&):

int ival = 42;
int *p = &ival; //p存放變量ival的地址,或者說p是指向變量ival的指針

第二條語句把p定義為一個指向int 的指針,隨后初始化p令其指向名為ival的int對象。因為引用不是對象,沒有實際地址,所以不能定義指向引用的指針。

一般而言,所有指針的類型都要和它所指向的對象嚴格匹配。

double dval;
double *pd = &dval;//正確:初始值是double型對象的地址
double *pd2 = pd; //正確:初始值是指向 double對象的指針
int *pi = pd;
//錯誤:指針pi的類型和pd的類型不匹配
pi = &dval;
//錯誤:試圖把double型對象的地址賦給int型指針

因為在聲明語句中指針的類型實際上被用于指定它所指向對象的類型,所以二者必須匹配。如果指針指向了一個其他類型的對象,對該對象的操作將發生錯誤。

指針的值

指針的值(即地址)應屬下列4種狀態之一:

1.指向一個對象。

2.指向緊鄰對象所占空間的下一個位置。

3.空指針,意味著指針沒有指向任何對象。

4.無效指針,也就是上述情況之外的其他值。

試圖拷貝或以其他方式訪問無效指針的值都將引發錯誤。編譯器并不負責檢查此類錯誤,這一點和試圖使用未經初始化的變量是一樣的。訪問無效指針的后果無法預計,因此程序員必須清楚任意給定的指針是否有效。

盡管第2種和第3種形式的指針是有效的,但其使用同樣受到限制。顯然這些指針沒有指向任何具體對象,所以試圖訪問此類指針(假定的)對象的行為不被允許。如果這樣做了,后果也無法預計。

利用指針訪問對象

如果指針指向了一個對象,則允許使用解引用符(操作符*)來訪問該對象:

int ival = 42;
int *p = &ival; // p存放著變量ival的地址,或者說p是指向變量ival的指針*
*cout<<*p;//由符號*得到指針p所指的對象,輸出42

對指針解引用會得出所指的對象,因此如果給解引用的結果賦值,實際上也就是給指針所指的對象賦值:

*p = 0;
//由符號*得到指針p所指的對象,即可經由p為變量ival賦值
cout<<*p;//輸出0

如上述程序所示,為*p賦值實際上是為p所指的對象賦值。解引用操作僅適用于那些確實指向了某個對象的有效指針。

關鍵概念:某些符號有多重含義

像&和*這樣的符號,既能用作表達式里的運算符,也能作為聲明的一部分出現,符號的上下文決定了符號的意義:

int i= 42;
int &r =i;//&緊隨類型名出現,因此走聲明的一部分,r是一個引用
int *p;//*緊隨類型名出現,因此是聲明的一部分,p是一個指針
p = &i;//&出現在表達式中,是一個取地址符
*p= i;//*出現在表達式中,是一個解引用符
int &r2= *p;//&是聲明的一部分,*是一個解引用符

在聲明語句中,&和*用于組成復合類型;在表達式中,它們的角色又轉變成運算符。在不同場景下出現的雖然是同一個符號,但是由于含義截然不同,所以我們完全可以把它當作不同的符號來看待。

空指針

空指針(null pointer)不指向任何對象,在試圖使用個指針之前代碼可以首先檢查它是否為空。以下列出幾個生成空指針的方法:

int *pl = nullptr;
//等價于int *pl = 0;
int *p2 =0;
//直接將p2初始化為字面常量0
//需要首先#include cstdlib
int *p3 = NULL;
//等價于int *p3 = 0;

得到空指針最直接的辦法就是用字面值nullptr來初始化指針,這也是CH+11新標準剛剛引入的一種方法。nullptr是一種特殊類型的字面值,它可以被轉換成(參見2.1.2節,第32頁)任意其他的指針類型。另一種辦法就如對p2的定義一樣,也可以通過將指針初始化為字面值0來生成空指針。

過去的程序還會用到一個名為NULL 的預處理變量(preprocessor variable)來給指針賦值,這個變量在頭文件cstdlib中定義,它的值就是0。

把int變量直接賦給指針是錯誤的操作,即使int變量的值恰好等于0也不行。

int zero= 0;
pi = zero;
//錯誤:不能把int 變量直接賦給指針

初始化指針

使用未經初始化的指針是引發運行時錯誤的一大原因。
和其他變量一樣,訪問未經初始化的指針所引發的后果也是無法預計的。通常這一行為將造成程序崩潰,而且一旦崩潰,要想定位到出錯位置將是特別棘手的問題。

在大多數編譯器環境下,如果使用了未經初始化的指針,則該指針所占內存空間的當前內容將被看作一個地址值。訪問該指針,相當于去訪問一個本不存在的位置上的本不存在的對象。糟糕的是,如果指針所占內存空間中恰好有內容,而這些內容又被當作了某個地址,我們就很難分清它到底是合法的還是非法的了。

因此建議初始化所有的指針,并且在可能的情況下,盡量等定義了對象之后再定義指向它的指針。如果實在不清楚指針應該指向何處,就把它初始化為nullptr或者0,這樣程序就能檢測并知道它沒有指向任何具體的對象了。

賦值和指針

指針和引用都能提供對其他對象的間接訪問,然而在具體實現細節上二者有很大不同,其中最重要的一點就是引用本身并非一個對象。一旦定義了引用,就無法令其再綁定到另外的對象,之后每次使用這個引用都是訪問它最初綁定的那個對象。

指針和它存放的地址之間就沒有這種限制了。和其他任何變量(只要不是引用)一樣,給指針賦值就是令它存放一個新的地址,從而指向一個新的對象:

int i =42;
int *pi = 0;
//pi被初始化,但沒有指向任何對象
int *pi2 = &i;
//pi2被初始化,存有i的地址
int *pi3;
//如果pi3定義于塊內,則pi3的值是無法確定的
pi3 = pi2;
// pi3和pi2指向同一個對象i
pi2 = 0;
//現在pi2不指向任何對象了

有時候要想搞清楚一條賦值語句到底是改變了指針的值還是改變了指針所指對象的值不太容易,最好的辦法就是記住賦值永遠改變的是等號左側的對象。當寫出如下語句時,

pi = &ival;
//pi的值被改變,現在pi指向了ival

意思是為pi賦一個新的值,也就是改變了那個存放在pi 內的地址值。相反的,如果寫出如下語句:

*pi = 0;
//ival的值被改變,指針pi并沒有改變

則* pi(也就是指針pi指向的那個對象)發生改變。

其他指針操作

只要指針擁有一個合法值,就能將它用在條件表達式中。和采用算術值作為條件(參見2.1.2節,第32頁)遵循的規則類似,如果指針的值是0,條件取false:

int ival = 1024;
int *pi = 0;// pi合法,是一個空指針
int *pi2 = &ival;// pi2是一個合法的指針存放著ival的地址
if (pi)//pi的值是0,因此條件的值是false...
if (pi2)// pi2指向ival,因此它的值不是0,條件的值是true

任何非0指針對應的條件值都是true。

對于兩個類型相同的合法指針,可以用相等操作符(==)或不相等操作符(!=)來比較它們,比較的結果是布爾類型。如果兩個指針存放的地址值相同,則它們相等;反之它們不相等。這里兩個指針存放的地址值相同(兩個指針相等)有三種可能:它們都為空、都指向同一個對象,或者都指向了同一個對象的下一地址。

需要注意的是,一個指針指向某對象,同時另一個指針指向另外對象的下一地址,此時也有可能出現這兩個指針值相同的情況,即指針相等。

因為上述操作要用到指針的值,所以不論是作為條件出現還是參與比較運算,都必須使用合法指針,使用非法指針作為條件或進行比較都會引發不可預計的后果。

void* 指針

void*是一種特殊的指針類型,可用于存放任意對象的地址。一個void * 指針存放著一個地址,這一點和其他指針類似。不同的是,我們對該地址中到底是個什么類型的對象并不了解:

double obj= 3.14, *pd = &obj;
//正確:void*能存放任意類型對象的地址
void *pv = &obj;
// obj可以是任意類型的對象
pv = pd;
//pv可以存放任意類型的指針

利用void指針能做的事兒比較有限: 拿它和別的指針比較、作為函數的輸入或輸出,或者賦給另外一個 void * 指針。**不能直接操作 void指針所指的對象,因為我們并不知道這個對象到底是什么類型,也就無法確定能在這個對象上做哪些操作。**

概括說來,以void * 的視角來看內存空間也就僅僅是內存空間,沒辦法訪問內存空間中所存的對象,關于這點將在19.1.1節(第726頁)有更詳細的介紹,4.11.3節(第144頁)將講述獲取void*指針所存地址的方法。

練習

//練習2.18:編寫代碼分別更改指針的值以及指針所指對象的值。
//練習2.19:說明指針和引用的主要區別。
//練習2.20:請敘述下面這段代碼的作用。
int i = 42;
int *pl = &i;
*p1 = *p1 * *pl;
//練習2.21:請解釋下述定義。在這些定義中有非法的嗎?如果有,為什么?
int i = 0;
(a) double* dp = &i; (b)int *ip = i; (c) int *p = &i;
//練習2.22:假設p是一個int型指針,請說明下述代碼的含義。
if (p)// ...
if(*p)// ...
//練習2.23:給定指針p,你能知道它是否指向了一個合法的對象嗎?如果能,敘述判斷的思路;如果不能,也請說明原因。
//練習2.24:在下面這段代碼中為什么p合法而lp非法?
int i = 42;
void *p = &i;
long * lp = &i;

答案

答案🏮

1??練習2.18:編寫代碼分別更改指針的值以及指針所指對象的值。

int a = 10;
int *p = &a;
int *q = p;// 更改指針的值
p = q; // 現在p和q指向相同的地址// 更改指針所指對象的值
*p = 20; // 現在a的值變為20

2??練習2.19:說明指針和引用的主要區別。

  1. 指針是一個變量,其存儲的是另一個變量的地址。而引用是一個別名,它和被它引用的變量共享同一內存地址。
  2. 指針可以被賦予空值(即NULL),而引用必須在定義時初始化,并且不能被賦予空值。
  3. 對指針的操作不會影響其指向的變量,除非通過指針進行賦值或解引。而對引用的操作會影響其引用的變量。

3??練習2.20:請敘述下面這段代碼的作用。

int i = 42;
int *pl = &i;
*p1 = *p1 * *pl;

它首先定義了一個整型變量i并賦值為42,然后定義了一個指向i的指針pl。接著,它嘗試通過p1修改p1所指向的變量的值,使其變為p1所指向的值與i值的乘積。

就是說 先定義并初始化了一個變量i,然后pl指向i,也就是*pl就是i的值,即42,然后 *pl= *p1 * *pl; 于是 *pl=42 *42 ;又由于然后pl指向i,所以i此時也被更改為42 * 42

4??練習2.21:請解釋下述定義。在這些定義中有非法的嗎?如果有,為什么?

int i = 0;
(a) double* dp = &i; (b)int *ip = i; (c) int *p = &i;

(a) 非法。因為i是整型變量,而double類型和整型不兼容。

(b) 非法。因為i是一個整型變量,不能直接作為指針賦值。

? 合法

5??練習2.22:假設p是一個int型指針,請說明下述代碼的含義。

if (p)// ...
if(*p)// ...
  • if(p) 檢查p是否不為NULL,即p是否指向了一個有效的內存地址。
  • if(*p) 檢查p所指向的內存地址的內容是否不為零(或其他非真值)。

6??練習2.23:給定指針p,你能知道它是否指向了一個合法的對象嗎?如果能,敘述判斷的思路;如果不能,也請說明原因。

不能。判斷指針是否指向一個合法的對象,需要檢查指針是否為NULL,以及它所指向的地址是否有效。然而,這并不總是安全的,因為指針可能指向一個有效的地址,但該地址的內容可能是不合法的或者已經被釋放。

所以不論是作為條件出現還是參與比較運算,都必須使用合法指針,使用非法指針作為條件或進行比較都會引發不可預計的后果。

7??練習2.24:在下面這段代碼中為什么p合法而lp非法?

int i = 42;
void *p = &i;
long * lp = &i;

void*是一種特殊的指針類型,可用于存放任意對象的地址。一個void * 指針存放著一個地址。在這段代碼中,p是合法的,因為它被初始化為指向i的地址,這是一個有效的整型地址。

lp是非法的,因為intlong類型不兼容。雖然lp被賦予了i的地址,但這個地址對于long類型的數據是不正確的,因此這種用法是非法的。

🌈ok 完結~ ~ 有幫助點個贊叭~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/15249.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/15249.shtml
英文地址,請注明出處:http://en.pswp.cn/web/15249.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

uniapp微信小程序解決open-type獲取用戶頭像,返回臨時路徑問題!

解決 open-type 為 chooseAvatar&#xff0c;返回臨時路徑問題 文章目錄 解決 open-type 為 chooseAvatar&#xff0c;返回臨時路徑問題效果圖Demo獲取頭像回調數據結構效果圖解決方式上傳到服務器轉base64 基于微信小程序獲取頭像昵稱規則調整后&#xff0c;當小程序需要讓用戶…

深入了解FreeRTOS:實時操作系統的核心概念和應用

前言&#xff1a; 在當今數字化世界中&#xff0c;嵌入式系統扮演著至關重要的角色&#xff0c;從工業自動化到智能設備&#xff0c;無所不在。而實時操作系統&#xff08;RTOS&#xff09;則是這些系統的核心引擎&#xff0c;它們負責管理任務、資源和時間&#xff0c;確保系統…

RmlUi 初試,hello world

前言 最近在研究GUI的各個方面&#xff0c;最后被導向了web render&#xff0c;真的是一言難盡。 這里就其中一個比較有意思的項目 RmlUi 淺試一下&#xff0c;沒想要還挺麻煩&#xff01;這里留下note以供后人參考。 環境搭建 Windows VS2022 pre-binary library 需要指…

高通Android 12/13 設置和獲取ADB狀態

/*** 設置ADB狀態** param isEnable*/public void setADB(boolean isEnable) {Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.ADB_ENABLED, isEnable ? 1 : 0);}/*** 獲取ADB狀態** return*/public boolean getADB() {return Settings.Global.getIn…

虛擬化技術[3]之網絡虛擬化

網絡虛擬化 網絡虛擬化簡介核心層網絡虛擬化接入層網絡虛擬化虛擬機網絡虛擬化案例: VMware網絡虛擬化技術虛擬網絡接口卡虛擬交換機vSwitch分布式交換機端口組VLAN 網絡虛擬化簡介 傳統的數據中心&#xff1a;服務器之間操作系統和上層軟件異構、接口與數據格式不統一&#x…

鏈表相交-力扣

在做這道題時&#xff0c;首先想到的解法是遍歷第一個鏈表&#xff0c;將其全部添加到哈希表中&#xff0c;然后遍歷第二個鏈表&#xff0c;如果能夠再哈希表中查到元素&#xff0c;則返回這個元素&#xff0c;否則返回NULL。 但在實際寫代碼時&#xff0c;第一次寫默認為鏈表相…

Redis實現MQ

MQ的提出 上游發出請求后阻塞等待下游給到反饋&#xff0c;否則整個流程將一直阻塞。 提出mq之后&#xff1a;即有producer mq consumer 三者 MQ的特點 異步解耦 在有了 mq 后&#xff0c;producer 不需要過分關心 consumer 的身份信息&#xff0c;只需要把消息按照指定的協議…

Python 潮流周刊#52:Python 處理 Excel 的資源

本周刊由 Python貓 出品&#xff0c;精心篩選國內外的 250 信息源&#xff0c;為你挑選最值得分享的文章、教程、開源項目、軟件工具、播客和視頻、熱門話題等內容。愿景&#xff1a;幫助所有讀者精進 Python 技術&#xff0c;并增長職業和副業的收入。 本期周刊分享了 12 篇文…

基于hive的酒店價格數據可視化分析系統設計和實現

摘要 本文基于Django框架和Hive技術&#xff0c;設計和實現了一種酒店價格數據可視化分析系 統&#xff0c;旨在為酒店管理者提供直觀、清晰的數據洞察和決策支持。在研究中&#xff0c;首先深入分 析了酒店價格數據可視化分析系統的背景和意義&#xff0c;認識到對于酒店行…

3.Redis之Redis的環境搭建redis客戶端介紹

1.版本的選取 安裝 Redis&#xff1a;Redis 5 系列~~ 在 Linux 中進行安裝~~ Redis 官方是不支持 Windows 版本的~~ 微軟維護了一個 Windows 版本的 Redis 分支 Centos和Ubuntu.Docker 2.如何進行安裝&#xff1f;&#xff1f;&#xff1f; 1.ubuntu 2.centos yum instal…

arcgisPro將一個圖層的要素復制到另一個圖層

1、打開兩個圖層&#xff0c;如下&#xff0c;其中一個圖層中有兩個要素&#xff0c;需要將其中一個要素復制到另一個圖層中&#xff0c;展示如下&#xff1a; 2、選中待復制要素&#xff0c;點擊復制按鈕&#xff0c;如下&#xff1a; 3、下拉粘貼按鈕列表&#xff0c;選擇【選…

利用oracle默認事務隔離級別(提交讀)提升多表聯查速度

利用oracle默認事務隔離級別(提交讀)提升查詢速度) 背景介紹&#xff1a; 數據量大查詢緩慢&#xff0c;添加太多條件&#xff0c;使用IN走了全表查詢導致查詢速度緩慢。 解決方案&#xff1a; 版本一&#xff1a; 新建臨時表&#xff0c;在查詢是將數據插入到臨時表中&#…

Python 根據點云索引提取點云

點云索引濾波 一、介紹1.1 概念1.2 參數設置二、代碼示例三、結果示例一、介紹 1.1 概念 點云索引濾波 是一種常用的點云濾波方法,根據給定的索引列表獲取點云中的索引點,或著根據給定的索引列表獲取點云中的非索引點。 1.2 參數設置 核心函數: def select_by_index(self, …

Ubuntu22.04虛擬機設置靜態IP

虛擬機設置靜態IP 按下電腦的 “win”鍵&#xff0c;在彈出的輸入框中輸入“控制面板”&#xff0c;選中控制面板 1.選擇 “網絡和Internet” 2.選擇 “網絡和共享中心” 3.選擇 “更改適配器設置” 4.選擇 “VMnet8”&#xff0c;雙擊打開 5.選擇 “屬性” 找到 “Internet …

【idea】idea2024最新版本下載_安裝_破解

1、下載 下載地址&#xff1a;下載 IntelliJ IDEA – 領先的 Java 和 Kotlin IDE 下載完成&#xff1a; idea破解腳本下載鏈接&#xff1a;https://pan.baidu.com/s/1L5qq26cRABw8XuEn_CngKQ 提取碼&#xff1a;6666 下載完成&#xff1a; 2、安裝 1、雙擊idea的安裝包&…

《計算機網絡微課堂》1-6 計算機體系結構

常見的計算機網絡體系結構 從本節課開始&#xff0c;我們要用 4 次課的時間來介紹有關計算機網絡體系結構的知識&#xff0c;具體包含以下內容&#xff1a; 一&#xff0c;常見的計算機網絡體系結構二&#xff0c;計算機網絡體系結構分層的必要性三&#xff0c;計算機網絡體系…

給我瞅瞅呀

專業 流程&#xff08;一條龍服務&#xff09; 需求溝通-需求分析-產品架構-ue原型-ui設計-產品研發-產品測試-產品交付-產品運維 保障 1、按需定制&#xff0c;簽訂功能清單&#xff0c;根據功能報價 2、價格透明&#xff0c;簽訂合同保障&#xff0c;保障客戶合法權益 3、源…

python(4) : pip安裝使用國內源

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests

低代碼應用:云原生與Kubernetes的應用實戰

隨著云原生技術的發展&#xff0c;低代碼開發平臺&#xff08;Low-Code Development Platforms, LCDPs&#xff09;在企業級應用開發中扮演著越來越重要的角色。本文將探討低代碼平臺如何與Kubernetes結合&#xff0c;實現高效、靈活且可擴展的企業級應用開發。 低代碼平臺概述…

監控員工電腦屏幕的五大軟件(電腦監控軟件大盤點)

監控員工電腦屏幕是企業為了提升工作效率、確保信息安全和合規性而采取的一種常見做法。以下是五款在2024年備受推薦的員工電腦屏幕監控軟件&#xff0c;每款軟件都具有其獨特的功能和優勢&#xff1a; 1. 域智盾 域智盾是一款全面的終端管理系統&#xff0c;集成了實時屏幕監…