日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

C++ 笔记(14)— 指针(指针声明、取地址、取值、new/delete、NULL指针、指针运算、指针数组、数组指针、指针传递给函数、从函数返回指针)

發布時間:2023/11/27 生活经验 55 豆豆

1. 聲明指針

指針是一個變量,其值為另一個變量的地址,即,內存位置的直接地址。就像其他變量或常量一樣,您必須在使用指 針存儲其他變量地址之前,對其進行聲明。

指針變量聲明的一般形式為:

type *var-name;
  • type 是指針的基類型,它必須是一個有效的 C++ 數據類型;
  • var-name 是指針變量的名稱。用來聲明指針的星號 * 與乘法中使用的星號是相同的。但是,在這個語句中,星號是用來指定一個變量是指針。

以下是有效的指針聲明:

int    *ip;    /* 一個整型的指針 */
double *dp;    /* 一個 double 型的指針 */
float  *fp;    /* 一個浮點型的指針 */
char   *ch;    /* 一個字符型的指針 */

與大多數變量一樣,除非對指針進行初始化,否則它包含的值將是隨機的。您不希望訪問隨機的內存地址,因此將指針初始化為 NULLNULL 是一個可以檢查的值,且不會是內存地址:

int *ip = NULL;

未初始化的指針可能導致程序訪問非法內存單元,進而導致程序崩潰。

2. 獲取變量地址 &

2.1 & 作用于普通變量

每一個變量都有一個內存位置,每一個內存位置都定義了 & 運算符訪問的地址,它表示了在內存中的一個地址。

#include <iostream>
using namespace std;int main ()
{int  a;char b[10];cout << &a << endl;	// 0x7ffe06ed398ccout << &b << endl;	// 0x7ffe06ed3990return 0;
}

2.1 & 作用于數組

short int height[10]; //int型的數組(short int 每個數據2字節)
cout <<  "height       "<< height << endl <<  "height+1     "<< height + 1 << endl<<  "&height[0]   " <<  &height[0] << endl<<  "&height+1    "<< &height + 1<< endl<<  "height+9     "<< height+9 << endl<< "height+10    " << height + 10 << endl;

輸出結果:

height       0x7fffce72dfc0
height+1     0x7fffce72dfc2
&height[0]   0x7fffce72dfc0
&height+1    0x7fffce72dfd4
height+9     0x7fffce72dfd2
height+10    0x7fffce72dfd4
  • 數組名 height 表示數組首元素地址;
  • 數組名 height+1 表示數組第二個元素地址;
  • &height[0] 表示數組首個元素的地址,其與 height 是相等的;
  • &height+1 表示先取數組開始的地址,然后再加 1 ,也就是說 c0 + 10*2 ,表示成 16 進制為 c0+14=d4;
  • height+9 表示數組最后一個元素,c0+9*2= c0+12=d2;
  • height+10 表示整個數組結束之后的第一個地址,與 &height+1 相等;

3. 使用指針存儲地址

知道如何聲明指針以及如何獲取變量的地址,還知道指針是用于存儲內存地址的變量。現在將它們聯系起來,使用指針來存儲用 & 獲取的地址。

#include <iostream>
using namespace std;int main ()
{int age = 10;int *page = &age;cout << "age address is " << &age << endl;   // 0x7ffc43a7b75ccout << "page is " << page << endl;  // 0x7ffc43a7b75cint dogAge = 3;page = &dogAge;  // 將不同的內存地址賦給指針變量,讓它指向不同的值cout << "page is " << page << endl;  // 0x7ffef77b240creturn 0;
}

4. 使用 * 訪問指針指向的數據

指針的本質是變量,可以是各種數據類型,定義一個指針 *ip ,其中 ip 需要賦于一個地址(可以用 & 符號獲取其他變量的地址再賦值給 ip ),而 *ip 是一個具體的值,即讀取地址后獲得的值。

如果有合法的指針 p ,要訪問它包含的地址處存儲的值,可使用 *p

#include <iostream>
using namespace std;int main ()
{int age = 10;int *page = &age;cout << "age address is " << &age << endl;   // 0x7ffc43a7b75ccout << "page is " << page << endl;  // 0x7ffc43a7b75ccout << "page point value is " << *page << endl; // 10int dogAge = 3;page = &dogAge;  // 將不同的內存地址賦給指針變量,讓它指向不同的值cout << "page is " << page << endl;  // 0x7ffef77b240ccout << "page point value is " << *page << endl; // 3return 0;
}
  • & 符號的意思是取地址,也就是返回一個對象在內存中的地址。
  • * 符號的意思是取得一個指針所指向的對象。 也就是如果一個指針保存著一個內存地址,那么它就返回在那個地址的對象。

簡單點就是: & 是取址, * 是取值。

5. sizeof() 用于指針

指針是包含內存地址的變量。因此無論指針指向哪種類型的變量(整型、浮點型、字符型,還是其他的數據類型),其內容都是一個地址(一個代表內存地址的長的十六進制數)。

在特定的系統中,存儲地址所需的字節數是固定的。因此,將 sizeof( ) 用于指針時,結果取決于編譯程序時使用的編譯器和針對的操作系統,與指針指向的變量類型無關。

不同數據類型的指針之間唯一的不同是,指針所指向的變量或常量的數據類型不同。

#include <iostream>
using namespace std;int main ()
{cout << "sizeof(char) = " << sizeof(char) << endl;cout << "sizeof(int) = " << sizeof(int) << endl;cout << "sizeof(double) = " << sizeof(double) << endl;cout << "sizeof(*char) = " << sizeof(char *) << endl;cout << "sizeof(*int) = " << sizeof(int *) << endl;cout << "sizeof(*double) = " << sizeof(double *) << endl;return 0;
}

輸出結果:

sizeof(char) = 1
sizeof(int) = 4
sizeof(double) = 8
sizeof(*char) = 8
sizeof(*int) = 8
sizeof(*double) = 8

輸出表明,雖然 sizeof(char) 為 1 字節,而 sizeof(double) 為 8 字節,但 sizeof(char*)sizeof(double*) 都是 8 字節。這是因為不管指針指向的內存單元是 1 字節還是 8 字節,存儲指針所需的內存量都相同。

備注:使用的是 64 位編譯器,并在 64 位系統上運行該程序,可能發現將 sizeof 用于指針的結果為 64 位,即 8 字節。

6. 使用 new 和 delete 動態地分配和釋放內存

使用 new 來分配新的內存塊。通常情況下,如果成功, new 將返回指向一個指針,指向分配的內存,否則將引發異常。使用 new 時,需要指定要為哪種數據類型分配內存:

Type* Pointer = new Type;

需要為多個元素分配內存時,還可指定要為多少個元素分配內存:

Type* Pointer = new Type[numElements];

因此,如果需要給整型分配內存,可使用如下語法:

int* pointToAnInt = new int; 
int* pointToNums = new int[10];

使用 new 分配的內存最終都需使用對應的 delete 進行釋放:

Type* Pointer = new Type; 
delete Pointer;

這種規則也適用于為多個元素分配的內存:

Type* Pointer = new Type[numElements]; 
delete[] Pointer; 

注意:

  • 對于使用 new[…] 分配的內存塊,需要使用 delete[] 來釋放;對于使用 new 為單個元素分配的內存,需要使用 delete 來釋放。
  • 不能將運算符 delete 用于任何包含地址的指針,而只能用于 new 返回的且未使用 delete 釋放的指針。
#include <iostream>
using namespace std;int main ()
{int *a = new int;*a = 10;cout << "a is " << a << " *a is " << *a << endl;int *b = new int[10];cout << "b is " << b << " *b is " << *b << endl;delete a;delete[] b;return 0;
}

7. 未初始化指針、NULL 指針

聲明一個指針變量并不會自動分配任何內存。在對指針執行間接訪問前 , 指針必須進行初始化、或者使它指向現有的內存、或者給它分配動態內存。

對未初始化的指針變量執行間接訪問操作是非法的,而且這種錯誤常常難以檢測。其結果常常是一個不相關的值被修改。這種錯誤是很難被調試發現的。

7.1 未初始化指針

下面這個代碼段說明了一個極為常見的錯誤 :

int main() 
{int *a;*a = 10;cout << *a << endl;return 0;
}

這個聲明創建了一個名叫 a 的指針變量 , 后面那條賦值語句把 12 存儲在 a 所指向的內存位置 。但是究竟 a 指向哪里呢 ? 我們聲明了這個變量 , 但從未對它進行初始化, 所以我們沒有辦法預測 12 這個值將存儲于什么地方 。

聲明一個指向整型的指針不會 “ 創建 ” 用于存儲整型值的內存空間 。

所以 , 如果程序執行這個賦值操作 , 會發生什么情況呢 ? 如果你運氣好, a 的初始值會是個非法地址 , 這樣賦值語句將會出錯 , 從而終止程序 。 在 UNIX 系統上 , 這個錯誤被稱為 “ 段違例( Segmentation fault (core dumped) ) ” 或 “ 內存錯誤 ( memory fault ) ” 。 它提示程序試圖訪問一個并未分配給程序的內存位置 。

所以,在你對指針進行間接訪問之前,必須非常小心,確保它們已被初始化 !

7.2 NULL指針

標準定義了 NULL 指針,它作為一個特殊的指針變量 ,表示不指向任何東西。要使一個指針變量為 NULL , 你可以給它賦一個零值 。 為了測試一個指針變量是否為 NULL ,你可以將它與零值進行比較。

之所以選擇零這個值是因為一種源代碼約定 。 就機器內部而言 ,NULL 指針的實際值可能與此不同 。在這種情況下 ,編譯器將負責零值和內部值之間的翻譯轉換 。

在變量聲明的時候,如果沒有確切的地址可以賦值,為指針變量賦一個 NULL 值是一個良好的編程習慣。賦為 NULL 值的指針被稱為空指針。

在大多數的操作系統上,程序不允許訪問地址為 0 的內存,因為該內存是操作系統保留的。然而,內存地址 0 有特別重要的意義,它表明該指針不指向一個可訪問的內存位置。

通過一個指針訪問它所指向的地址的過程稱為間接訪問 (indirection) 或解引用指針 (dereferencing
thepointer) 。 這個用于執行間接訪問的操作符是單目操作符 *

對指針進行解引用操作可以獲得它所指向的值。但從定義上看, NULL 指針并未指向任何東西。因此,對一個 NULL 指針進行解引用操作是非法的。在對指針進行解引用操作之前,你首先必須確保它并非 NULL 指針 。

int main() 
{int *a = NULL;cout << *a << endl;return 0;
}

程序會報錯:Segmentation fault (core dumped)

NULL 指針是一個定義在標準庫中的值為零的常量。請看下面的程序:

#include <iostream>
using namespace std;int main ()
{int *a = NULL;cout << " a is " << a << endl; //  a is 0return 0;
}

內存地址 0 有特別重要的意義,它表明該指針不指向一個可訪問的內存位置。但按照慣例,如果指針包含空值(零值),則假定它不指向任何東西。

如需檢查一個空指針,可以使用 if 語句,如下所示:

if(ptr)     /* 如果 ptr 非空,則完成 */
if(!ptr)    /* 如果 ptr 為空,則完成 */

NULL 指針就是不指向任何東西的指針。它可以賦值給一個指針,用于表示那個指針并不指向任何值 。 對 NULL 指針執行間接訪問操作的后果因編譯器而異,兩個常見的后果分別是返回內存位置零的值以及終止程序 。

8. 指針運算

有效的指針運算包括

  • 相同類型指針之間的賦值運算 ;
  • 指針同整數之間的加法或減法運算 ;
  • 指向相同數組中元素的兩個指針間的減法或比較運算 ;
  • 將指針賦值為 0 或指針與 0 之間的比較運算 。

其他所有形式的指針運算都是非法的 , 例如

  • 兩個指針間的加法 、 乘法 、除法 、 移位或屏蔽運算 ;
  • 指針同 floatdouble 類型之間的加法運算 ;
  • 不經強制類型轉換而直接將指向一種類型對象的指針賦值給指向另一種類型對象的指針的運算 ( 兩個指針之一是 void* 類型的情況除外 )。

可以對指針執行算術運算。可以對指針進行四種算術運算: ++--+-
假設 ptr 是一個指向地址 1000 的整型指針,是一個 32 位的整數,對該指針執行下列的算術運算:

ptr++

在執行完上述的運算之后, ptr 將指向位置 1004,因為 ptr 每增加一次,它都將指向下一個整數位置,即當前位置往后移 4 個字節。這個運算會在不影響內存位置中實際值的情況下,移動指針到下一個內存位置。

如果 ptr 指向一個地址為 1000 的字符,上面的運算會導致指針指向位置 1001,因為下一個字符位置是在 1001。

在指針值上可以執行一些有限的算術運算。你可以把一個整型值加到一個指針上,也可以從一個指針減去一個整型值 。在這兩種情況下 ,這個整型值會進行調整,原值將乘以指針目標類型的長度 。這樣,對一個指針加 1 將使它指向下一個變量 , 至于該變量在內存中占兒個字節的大小則與此無關。

8.1 指針 ++ 和指針 - -

我們喜歡在程序中使用指針代替數組,因為變量指針可以遞增,而數組不能遞增,因為數組是一個常量指針。

#include <iostream>
using namespace std;int main ()
{int a[3] = {1,2,3};int *p = a;for(int i=0; i<3; i++){cout << " *p " << *p << endl; p++;}cout << "#################" << endl;int *q = &a[2];for(int i=2; i>=0; i--){cout << " *q " << *q << endl; q--;}return 0;
}

輸出結果:

 *p 1*p 2*p 3
#################*q 3*q 2*q 1

8.2 指針比較

指針可以用關系運算符進行比較,如 ==<> 。如果 p1p2 指向兩個相關的變量,比如同一個數組中的不同元素,則可對 p1p2 進行大小比較。

#include <iostream>
using namespace std;int main ()
{int a[3] = {1,2,3};int *p = a;int *q = &a[2];while (p<=q){if (p==q){cout << "p==q: *p is " << *p << " *q is " << *q << endl;}if (p<q){cout << "p<q: *p is " << *p << " *q is " << *q << endl;}if (p>q){cout << "p>q: *p is " << *p << " *q is " << *q << endl;}p++;q--;}return 0;
}

輸出:

p<q: *p is 1 *q is 3
p==q: *p is 2 *q is 2

然而 , 指針運算只有作用于數組中其結果才是可以預測的。對任何并非指向數組元素的指針執行算術運算是非法的 ( 但常常很難被檢測到 )。 如果一個指針減去一個整數后,運算結果產生的指針所指向的位置在數組第一個元素之前 , 那么它也是非法的。加法運算稍有不同, 如果結果指針指向數組最后一個元素后面的那個內存位置仍是合法 ( 但不能對這個指針執行間接訪問操作 ) , 不過再往后就不合法了。

如果兩個指針都指向同一個數組中的元素 , 那么它們之間可以相減 。 指針減法的結果經過調整( 除以數組元素類型的長度 ), 表示兩個指針在數組中相隔多少個元素。如果兩個指針并不是指向同一個數組的元素,那么它們之間進行相減就是錯誤的 。

任何指針之間都可以進行比較, 測試它們相等或不相等 。如果兩個指針都指向同一個數組中的元素,那么它們之間還可以執行 << =>> = 等關系運算 ,用于判斷它們在數組中的相對位置。 對兩個不相關的指針執行關系運算,其結果是未定義的 。

警告總結:

  • 錯誤地對一個未初始化的指針變量進行解引用。
  • 錯誤地對一個 NULL 指針進行解引用 。
  • 向函數錯誤地傳遞 NULL 指針。
  • 未檢測到指針表達式的錯誤,從而導致不可預料的結果 。
  • 對一個指針進行減法運算,使它非法地指向了數組第 1 個元素的前面的內存位置。

如果指針并不指向任何有意義的東西 ,就把它設置為 NULL

9. 指針作用于數組

聲明

int a[10];

定義了一個長度為 10 的數組 a 。換句話說,它定義了一個由 10 個對象組成的集合,這 10 個對象
存儲在相鄰的內存區域中,名字分別為a[0] 、a[1] 、… a[9] 。

a[i] 表示該數組的第 i 個元素。如果 pa 的聲明為

int *pa;

則說明它是一個指向整型對象的指針,那么,賦值語句

pa = &a[0];

則可以將指針 pa 指向數組 a 的第 0 個元素, 也就是說,pa 的值為數組元素 a[0] 的地址。

這樣,賦值語句

x = *pa;

將把數組元素 a[0] 中的內容復制到變量 x 中。

如果 pa 指向數組中的某個特定元素,那么,根據指針運算的定義,pa+1 將指向下一個元素,pa+i 將指向 pa 所指向數組元素之后的第 i 個元索,而 pa-i 將指向 pa 所指向數組元素之前的第 i 個元素。因此,如果指針 pa 指向 a[0] , 那么 *( pa+i) 引用的是數組元素 a[i] 的內容,pa+i 是數組元素 a[i] 的地址,*(pa+i) 引用的是數組元素 a[i] 的內容。


無論數組 a 中元素的類型或數組長度是什么,上面的結論都成立。“指針加1”就意味著,pa+1 指向 pa 所指向的對象的下一個對象。相應地,pa+i 指向 pa 所指向的對象之后的第 i 個對象。

下標和指針運算之間具有密切的對應關系。根據定義,數組類型的變量或表達式的值是該數組第 0 個元素的地址。執行賦值語

pa = &a[0];

后,paa 具有相同的值。因為數組名所代表的就是該數組最開始的一個元素的地址,所以,
賦值語句 pa=&a[0] 也可以寫成下列形式:

pa = a;

對數組元素 a[i] 的引用也可以寫成 *( a+i) 這種形式。對第一次接觸這種寫法的人來說,可能會覺得很奇怪。在計算數組元素 a[i] 的值時,C++ 語言實際上先將其轉換為 *( a+i) 的形式,然后再進行求值,因此在程序中這兩種形式是等價的。

如果對這兩種等價的表示形式分別施加地址運算符 & , 便可以得出這樣的結論:&a[i]a+i 的含義也是相同的。a+ia 之后第 i 個元素的地址。相應地,如果 pa 是一個指針,那么,在表達式中也可以在它的后面加下標 pa[i]*(pa+i) 是等價的。簡而言之,一個通過數組和下標實現的表達式可等價地通過指針和偏移量實現。

但是,我們必須記住,數組名和指針之間有一個不同之處。指針是一個變量,因此,在 C/C++ 語言中,語句 pa=apa++ 都是合法的。但數組名不是變量,因此,類似于 a=paa++ 形式的語句是非法的。

當把數組名傳遞給一個函數時,實際上傳遞的是該數組第一個元素的地址。在被調用函數中,該參數是一個局部變量,因此,數組名參數必須是一個指針,也就是一個存儲地址值的變量。我們可以利用該特性編寫strlen 函數的另一個版本,該函數用于計算一個字符串的長度。

int strlen(char *s)
{for(int n=0; *s != '\0'; s++){n++;}return n;
}

因為 s 是一個指針,所以對其執行自增運算是合法的。執行 s++ 運算不會影響到 strlen 函數的調用者中的字符串, 它僅對該指針在 strlen 函數中的私有副本進行自增運算。因此,類似于下面這樣的函數調用:

strlen("hello, world"); 	/*字符串常量*/
strlen(array);				/*字符數組array有100個元素*/
strlen(ptr);				/* Ptr是一個指向char *類型對象的指針 */

都可以正確地執行。

在函數定義中,形式參數

char s[];

char *s;

是等價的。我們通常更習慣于使用后一種形式, 因為它比前者更直觀地表明了該參數是一個指針。

指針和數組是密切相關的。事實上,指針和數組在很多情況下是可以互換的。例如,一個指向數組開頭的指針,可以通過使用指針的算術運算或數組索引來訪問數組。

#include <iostream>
using namespace std;int main ()
{int var[3] = {10,100,200};int *p=var;for (int i = 0; i < 3; i++){//cout << *(var++) << endl;    //編譯錯誤,因為var是數組名(也是數組首元素地址),不是一個變量,不可修改其值cout << *(var+i) << endl; //編譯正確,因為沒有對var進行賦值,只是通過與i相加訪問變量地址cout<<  *(p++)<<endl;        ///編譯正確,p為指針變量可賦值}return 0;
}

輸出結果:

10
10
100
100
200
200

把指針運算符 * 應用到 var 上是完全可以的,但修改 var 的值是非法的。這是因為 var 是一個指向數組開頭的常量,不能作為左值。

由于一個數組名對應一個指針常量,只要不改變數組的值,仍然可以用指針形式的表達式。例如,下面是一個有效的語句,把 var[2] 賦值為 500:

*(var + 2) = 500;

10. 指針數組

可能有一種情況,我們想要讓數組存儲指向 intchar 或其他數據類型的指針。下面是一個指向整數的指針數組的聲明:

int *ptr[MAX];

在這里,把 ptr 聲明為一個數組,由 MAX 個整數指針組成。因此, ptr 中的每個元素,都是一個指向 int 值的指針。

char *names[MAX] 是指針數組,它的本質是存儲指針的數組,即存儲 char 類型的指針的數組,數組內的每個元素都是一個指針,并且指向一個存儲 char 類型的地址。

下面的實例用到了三個整數,它們將存儲在一個指針數組中,如下所示

#include <iostream>using namespace std;
const int MAX = 3;int main ()
{int  var[MAX] = {10, 100, 200};int *ptr[MAX];for (int i = 0; i < MAX; i++){ptr[i] = &var[i]; // 賦值為整數的地址}for (int i = 0; i < MAX; i++){cout << "Value of var[" << i << "] = ";cout << *ptr[i] << endl;}return 0;
}

輸出結果:

Value of var[0] = 10
Value of var[1] = 100
Value of var[2] = 200

詳解指針數組的結合過程:

int *ptr[3];

由于 C++ 運算符的優先級中, * 小于 [] ,所以 ptr 先和 [] 結合成為數組,然后再和 int * 結合形成數組的元素類型是 int * 類型,得到一個叫一個數組的元素是指針,簡稱指針數組。
這相當于聲明了一個數組 , 該數組有 3 個元素,其中每個元素都是一個指向整型對象的指針。

int *(ptr[3]);

這個和上面的一樣,優先級順序是 * 小于 ()() 等于 []ptr 先和 [] 結合成為數組,然后再和 int * 結合形成數組的元素類型是 int * 類型,得到一個叫一個數組的元素是指針,簡稱指針數組。

11. 數組指針

int (*ptr)[3];

這個就不一樣了,優先級順序是 * 小于 ()() 等于 []()[] 的優先級一樣,但是結合順序是從左到右,所以先是 () 里的 *ptr 結合成為一個指針,然后是 (*ptr)[] 相結合成為一個數組,最后叫一個指針 ptr 指向一個數組,簡稱數組指針。

12. 指向指針的指針

指向指針的指針是一種多級間接尋址的形式,或者說是一個指針鏈。通常,一個指針包含一個變量的地址。當我們定義一個指向指針的指針時,第一個指針包含了第二個指針的地址,第二個指針指向包含實際值的位置。

一個指向指針的指針變量必須如下聲明,即在變量名前放置兩個星號。例如,下面聲明了一個指向 int 類型指針的指針:

int **var;

當一個目標值被一個指針間接指向到另一個指針時,訪問這個值需要使用兩個星號運算符,如下面實例所示:

#include <iostream>
using namespace std;int main ()
{int  var;int  *ptr;int  **pptr;var = 3000;// 獲取 var 的地址ptr = &var;// 使用運算符 & 獲取 ptr 的地址pptr = &ptr;// 使用 pptr 獲取值cout << "var 值為 :" << var << endl;cout << "*ptr 值為:" << *ptr << endl;cout << "**pptr 值為:" << **pptr << endl;return 0;
}

輸出結果:

var 值為 :3000
*ptr 值為:3000
**pptr 值為:3000

13. 傳遞指針給函數

C++ 允許您傳遞指針給函數,只需要簡單地聲明函數參數為指針類型即可。

#include <iostream>
using namespace std;int get(int *p);int main ()
{int a = 1;cout << "a 值為 :" << a << endl;cout << "&a = " << &a << endl;get(&a);cout << "a 值為 :" << a << endl; return 0;
}int get(int *p)
{cout << "p = " << p << endl;*p = 10;return *p;
}

輸出結果:

a 值為 :1
&a = 0x7ffcb10db964
p = 0x7ffcb10db964
a 值為 :10

14. 從函數返回指針

C++ 允許從函數返回指針。為了做到這點,必須聲明一個返回指針的函數,如下所示:

int * func();

另外,C++ 不支持在函數外返回局部變量的地址,除非定義局部變量為 static 變量。

#include <iostream>
using namespace std;int * get();int main ()
{int * ret=NULL;ret = get();for(int i=0; i<3; i++){   cout << "&ret is " << ret << endl;cout << "i is " << i << " value is " << *ret << endl;ret++;}return 0;
}int * get()
{static int a[3] = {1,2,3};return a;
}

輸出結果:

&ret is 0x601070
i is 0 value is 1
&ret is 0x601074
i is 1 value is 2
&ret is 0x601078
i is 2 value is 3

15 const 用于指針

通過將變量聲明為 const 的,可確保變量的取值在整個生命周期內都固定為初始值。這種變量的值不能修改,因此不能將其用作左值。

指針也是變量,因此也可將關鍵字 const 用于指針。然而,指針是特殊的變量,包含內存地址,還可用于修改內存中的數據塊。因此, const 指針有如下三種。

  • 指針包含的地址是常量,不能修改,但可修改指針指向的數據:
int daysInMonth = 30;
int* const pDaysInMonth = &daysInMonth;
*pDaysInMonth = 31; // OK! Data pointed to can be changed
int daysInLunarMonth = 28;
pDaysInMonth = &daysInLunarMonth; // Not OK! Cannot change address!
  • 指針指向的數據為常量,不能修改,但可以修改指針包含的地址,即指針可以指向其他地方:
int hoursInDay = 24;
const int* pointsToInt = &hoursInDay;
int monthsInYear = 12;
pointsToInt = &monthsInYear; // OK!
*pointsToInt = 13; // Not OK! Cannot change data being pointed to
int* newPointer = pointsToInt; // Not OK! Cannot assign const to non-const
  • 指針包含的地址以及它指向的值都是常量,不能修改(這種組合最嚴格):
int hoursInDay = 24;
const int* const pHoursInDay = &hoursInDay;
*pHoursInDay = 25; // Not OK! Cannot change data being pointed to
int daysInMonth = 30;
pHoursInDay = &daysInMonth; // Not OK! Cannot change address

將指針傳遞給函數時,這些形式的 const 很有用。函數參數應聲明為最嚴格的 const 指針,以確保函數不會修改指針指向的值。這可禁止程序員修改指針及其指向的數據。

16. 指針常見錯誤

C++沒有自動垃圾收集器對程序已分配但不能使用的內存進行清理。使用指針來管理內存資源時,程序員很容易犯錯。

16.1 內存泄露

這可能是C++應用程序最常見的問題之一:運行時間越長,占用的內存越多,系統越慢。如果在使用 new 動態分配的內存不再需要后,程序員沒有使用配套的 delete 釋放,通常就會出現這種情況。

確保應用程序釋放其分配的所有內存是程序員的職責。絕不能讓下面這樣的情形發生:

int* a = new int[5]; // initial allocation
// use a
...
// forget to release using delete[] a;
...
// make another allocation and overwrite
a = new int[10]; // leaks the previously allocated memory

16.2 指針指向無效的內存單元

使用運算符 * 對指針解除引用,以訪問指向的值時,務必確保指針指向了有效的內存單元,否則程序要么崩潰,要么行為不端。這看起來合乎邏輯,但一個非常常見的導致應用程序崩潰的原因就是無效指針。

16.3 懸浮指針(也叫迷途或失控指針)

使用 delete 釋放后,任何有效指針都將無效。為了避免再次使用已經 delete 的指針,很多程序員在初始化指針或釋放指針后將其設置為 NULL ,并在使用運算符 * 對指針解除引用前檢查它是否有效(將其與 NULL 比較)。

#include <iostream>
using namespace std;int main ()
{int *a = NULL;cout <<a << endl;	// 0if(a){cout <<"valid" << endl;}else{cout <<"invalid" << endl;	// invalid}return 0;
}

參考:
https://www.runoob.com/w3cnote/c-constant-pointer.html

總結

以上是生活随笔為你收集整理的C++ 笔记(14)— 指针(指针声明、取地址、取值、new/delete、NULL指针、指针运算、指针数组、数组指针、指针传递给函数、从函数返回指针)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。