詳解C++數(shù)組和數(shù)組名問題(指針、解引用)
一、指針
1.1 指針變量和普通變量的區(qū)別
指針:指針的實(shí)質(zhì)就是個(gè)變量,它跟普通變量沒有任何本質(zhì)區(qū)別。指針完整的應(yīng)該叫指針變量,簡稱為指針。 是指向的意思。指針本身是一個(gè)對(duì)象,同時(shí)指針無需在定義的時(shí)候賦值。
1.2 為什么需要指針
指針的出現(xiàn)是為了實(shí)現(xiàn)間接訪問。在匯編中都有間接訪問,其實(shí)就是CPU的尋址方式中的間接上。
間接訪問(CPU的間接尋 址)是CPU設(shè)計(jì)時(shí)決定的,這個(gè)決定了匯編語言必須能夠?qū)崿F(xiàn)問接尋又決定了匯編之上的C語言也必須實(shí)現(xiàn)簡介尋址。
1.3 指針使用三部曲
三部曲:定義指針變量、關(guān)聯(lián)指針變量、解引用
(1)當(dāng)我們int *p定義一個(gè)指針變量p時(shí),因?yàn)閜是局部變量,所以也道循C語言局部變量的一般規(guī)律(定義局部變量并且未初始化,則值是隨機(jī)的),所以此時(shí)p變量中存儲(chǔ)的是一個(gè)隨機(jī)的數(shù)字。
(2)此時(shí)如果我們解引用p,則相當(dāng)于我們訪問了這個(gè)隨機(jī)數(shù)字為地址的內(nèi)存空間。那這個(gè)空間到底能不能訪問不知道(也許行也許不行),所以如果直接定義指針變量未綁定有效地址就去解引用幾平必死無疑。
(3)定義一個(gè)指針變量,不經(jīng)綁定有效地址就去解引用,就好象拿一個(gè)上了鏜的槍隨意轉(zhuǎn)了幾圈然后開了槍。
(4)指針綁定的意義就在于讓指針指向一個(gè)可以訪問、應(yīng)該訪問的地方(就好象拿著槍瞄準(zhǔn)且標(biāo)的過程一樣),指針的解引用是為了間接訪問目標(biāo)變量(就好象開槍是為了打中目標(biāo)一樣)
int val = 43; int * p = &val; // &在右值為取值符 cout << *p << endl; //輸出 43
二、整形、浮點(diǎn)型數(shù)組
前言
- 在很多用到數(shù)組名字的地方,編譯器都會(huì)自動(dòng)地將其替換為一個(gè)指向該數(shù)組首元素的指針。
- 所以,在大多數(shù)表達(dá)式中,使用數(shù)組名其實(shí)是在使用一個(gè)指向該數(shù)組首元素的指針。
2.1 數(shù)組名其實(shí)是特殊的指針
int main() { int a[] = { 0,1,2,3,4 }; printf("%x\n", a); printf("%x\n", &a); printf("%x\n", &a[0]); }
- 從局部變量表可以看出,數(shù)組a和指針p的構(gòu)成是很相似的。它們實(shí)際存的都是一個(gè)地址,都會(huì)指向一個(gè)對(duì)象(或多個(gè)對(duì)象的第一個(gè)對(duì)象)。所以說數(shù)組名其實(shí)是種特殊的指針。
- 為什么說是特殊呢?
一維數(shù)組
int a[] = { 0,1,2,3,4 }; int * p1 = a; int *p = &a[0]; //指針p是 int * 的,而首元素是有地址的,所以取址,是允許的 //int * p1 = &a; //錯(cuò)誤 //理解:int (*p1)[5] = &a; //正確 /* 但它倆自身又有不同: 指針 p1 本身是一個(gè)對(duì)象,在內(nèi)存中是為其分配了空間的; 數(shù)組名 a 在內(nèi)存空間中沒有分配到空間(這將導(dǎo)致&a操作的效果可能和預(yù)想的不大一樣)。 可以理解為a指向一個(gè)含有5個(gè)整數(shù)的數(shù)組的指針,故 &a的類型為int(*)[5],不能用來初始化int */
整理:
指針 | 類型 |
---|---|
a | int * |
&a | int (*) [5] |
二維數(shù)組
int ia[3][4]; int (*p)[4] = ia; //ia 的類型就是 int(*)[4] int (*p)[3][4] = &ia; //&ia的類型就是 int(*)[3][4]
整理:
2.2 理解復(fù)雜的數(shù)組的聲明
上述提到數(shù)組名是指向一個(gè)數(shù)組的指針,因此解釋一下一些復(fù)雜的數(shù)組聲明。加深理解
int * ptr[10]; //ptr是含有10個(gè) 整形指針 的數(shù)組 int & ref[10] = /* ? */ //錯(cuò)誤,不存在引用的數(shù)組 int (*parray) [5] = &a; //parray指向一個(gè)含有5個(gè)整數(shù)的數(shù)組 /* 同時(shí)也是上述數(shù)組名的解釋 *parray意味著parray是一個(gè)指針; 右邊是[5]表明是指向大小為10的數(shù)組; 左邊int表明數(shù)組中元素為int. */ int (&array)[5] = a; //array引用一個(gè)含有5個(gè)整數(shù)的數(shù)組 int * (&array) [10] = ptrs; //array是數(shù)組的引用, 該數(shù)組是含有10個(gè)指針的數(shù)組
2.3 數(shù)組名a、數(shù)組名取地址&a、數(shù)組首元素地址&a[0]、指向數(shù)組首元素的指針*p
int main() { int a[] = { 0,1,2,3,4 }; printf("%x\n", a); printf("%x\n", &a); printf("%x\n", &a[0]); int *p = &a[0]; decltype(a) t; decltype(&a) tt; cout << p << endl; printf("%x,%x\n", a + 1, p + 1); printf("%x\n", &a + 1); cout << sizeof(a) << " " << sizeof(&a) << endl; }
輸出
a
既然是種特殊的指針,那么其打印時(shí)就會(huì)是存的地址。&a
的類型是int(*)[5]
(讀法從小括號(hào)里往外,首先是指針,然后是大小為5的數(shù)組,然后數(shù)組元素類型是int
),從局部變量中看到其類型也可寫成int[5] *
:即指向大小為5的int數(shù)組的指針。由于數(shù)組名沒有內(nèi)存分配空間&a[0]
就是取一個(gè)int對(duì)象的地址,這個(gè)int
對(duì)象是數(shù)組首元素。綜上所述,就造成了a &a &a[0]
三者打印出來的地址是一樣的。p
,指向數(shù)組首元素的指針。a + 1,p + 1
都是按照元素大小的字節(jié)數(shù)(4字節(jié)),增加4。&a + 1
,前面說了 &a的類型是指向大小為5的int
數(shù)組的指針,大小為5的int
數(shù)組所占字節(jié)數(shù)為20,所以&a + 1
就應(yīng)該增加20。sizeof(a)
為20,因?yàn)閿?shù)組總的字節(jié)大小為20。sizeof(&a)
為4,因?yàn)?code>&a是一種指針,指針在32位系統(tǒng)中占4字節(jié)。
2.4 對(duì)數(shù)組名以及取值符&的理解
數(shù)組中每個(gè)元素都是對(duì)象,即占有特定類型的內(nèi)存空間。(對(duì)象,占有一塊數(shù)組類型的內(nèi)存空間。因?yàn)閷?duì)象是指一塊能存儲(chǔ)數(shù)據(jù)并且具有某種類型的內(nèi)存空間。)
數(shù)組名可以轉(zhuǎn)化為這個(gè)數(shù)組對(duì)象的首個(gè)元素的地址。
這里我們不去討論一維數(shù)組,直接從二維說起。所謂二維數(shù)組也是數(shù)組,只不過它的元素也是一個(gè)數(shù)組。
首先我們寫一個(gè)二維數(shù)組留作使用
#include<iostream> using namespace std; int a[][10]={ {1,2,3,4,5,5,6,7,8,8}, {10,12,32,42,51,15,16,71,121,18} };
簡單說明一下數(shù)組:數(shù)組a 是包含2個(gè)元素的數(shù)組,每個(gè)元素是一個(gè)包含10個(gè) int 的數(shù)組。
既然說到數(shù)組名是其首個(gè)對(duì)象的地址那么來驗(yàn)證一下,測試數(shù)組名,以及對(duì)數(shù)組名求地址:
void test01(){ cout << (long long)a << endl; // 140273290059808 cout << (long long)(a+1) << endl; // 140273290059848 // 相差40個(gè)字節(jié) }
(用long long
型一眼就能看出是40個(gè)字節(jié))
a
與 a + 1
正好相差40個(gè)字節(jié),說明:
(1)數(shù)組名a 是(首元素{1,2,3,4,5,5,6,7,8,8}
)這一整行對(duì)象的地址,即首元素地址;
(2)所以在a+1
偏移了一個(gè)元素大小——40字節(jié)。
void test01(){ cout << (long long)&a << endl; // 140273290059808 cout << (long long)(&a+1) << endl; // 140273290059888 // 相差80個(gè)字節(jié) }
&a
與 &a + 1
正好相差80個(gè)字節(jié),說明:
(1)取址符取得是整個(gè)對(duì)象的地址,&a
是對(duì)二維數(shù)組求址,針對(duì)的是整個(gè)對(duì)象;
(2) &a+1
偏移一位就變成了整個(gè)二維數(shù)組的尾地址,c++中的尾地址是對(duì)象所在地址的下一位。&a+1
正好比 a
多了 80 個(gè)字節(jié)。
在上面也提到數(shù)組名會(huì)自動(dòng)轉(zhuǎn)換成一個(gè)特殊指針(兩個(gè)表格當(dāng)中的總結(jié)),接下來將理解這個(gè)指針到底是什么?
從指針解引用方面理解:
void test03(){ cout << *a << endl; // 0x7f051ce02020 //為了驗(yàn)證,我們偏移一下 cout << *(a + 1) << endl; // 0x7f051ce02048 // 正好相差40個(gè)字節(jié) }
*a 把數(shù)組名解引用之后是首元素(因?yàn)閿?shù)組名是指向首元素的特殊指針),而首元素也是一個(gè)有10個(gè)元素的數(shù)組,現(xiàn)在 *a
是代表這個(gè)對(duì)象,輸出它就是此數(shù)組的首元素——1 的地址.。
cout << *(*a) << endl; //1 **a 即可
第二層解掉:*(*a)
自然就是第一個(gè) int 型的元素。
cout << *a[0] << endl; // 1
因?yàn)橹羔樦赶驍?shù)組對(duì)象時(shí),可以用下標(biāo)訪問下一個(gè)位置,又 a 是指針指向了數(shù)組,下一個(gè)偏移為 0,即 * a = * (a + 0)
// cout << (a[0])[0] << endl; cout << a[0][0] << endl;// 1
基于上述, *a 也就是a[0],也會(huì)自動(dòng)轉(zhuǎn)化為指向自己的首個(gè)對(duì)象(10個(gè)元素的第一個(gè)元素的位置)的指針。所以 a[0]
可以用下標(biāo)訪問數(shù)組對(duì)象(10個(gè)元素)內(nèi)其他元素:a[0][0] == 1
我們多搞幾個(gè)案例:
// 要是訪問當(dāng)前行的下一個(gè)元素呢?將這個(gè)首地址 cout << *(*a + 1) << endl;// 2 即 *((*a) + 1) // 請(qǐng)注意這里的指針是 (*a), cout << (*a)[1] << endl; // 2 // 同理(*a)相當(dāng)于*(a + 0) 即a[0] cout << a[0][1] << endl; //2 // 如果訪問下一行呢? cout << **(a+1) << endl; cout << *a[1] << endl; cout << a[1][0] << endl; // *(a[1] + 0) // 第二行第二個(gè)元素呢? cout << *(*(a+1) +1 ) << endl; cout << *(a[1] +1) << endl; cout << a[1][1] << endl;
查看數(shù)組名類型理解
cout << typeid(*a).name()<< endl; // A10_i cout << typeid(&a[0]).name()<< endl; // PA10_i
A10_i :是由10個(gè) int 組成數(shù)組
PA10_i :是一個(gè)指針類型, 指向一個(gè)數(shù)組對(duì)象,這個(gè)數(shù)組對(duì)象有10個(gè)int型的對(duì)象。編譯器會(huì)識(shí)別為int(*)[10]
cout << typeid(a).name()<< endl; // A2_A10_i cout << typeid(&a).name()<< endl; // PA2_A10_i
A2_A10_i:由多維數(shù)組是數(shù)組的數(shù)組。A表示這個(gè)是數(shù)組類型,2表示是兩個(gè)對(duì)象組成的數(shù)組,每個(gè)對(duì)象(A10_i)是由有10個(gè)對(duì)象的數(shù)組,這10個(gè)對(duì)象是int型的。
PA2_A10_i:是一個(gè)指針類型, 指向一個(gè)數(shù)組對(duì)象,這個(gè)數(shù)組對(duì)象有2個(gè)數(shù)組型的對(duì)象。編譯器會(huì)識(shí)別為int(*)[2][10]
三、字符數(shù)組數(shù)組名
c++對(duì)待字符數(shù)組名不會(huì)輸出其地址,會(huì)直接輸出字符
#include <iostream> using namespace std; int main() { //int a[5]={1,2,34,4,5}; //如果不是char型,cout<<"a="<<a<<endl; // 輸出的為int數(shù)組首地址。不會(huì)輸出數(shù)組中的值。 char a[5]="aaaa"; //cout重載了char[],可以輸出整個(gè)字符串?dāng)?shù)組 cout<<"a="<<a<<endl; return 0; }
詳細(xì)參考這篇博客
到此這篇關(guān)于詳解C++數(shù)組和數(shù)組名問題(指針、解引用)的文章就介紹到這了,更多相關(guān)C++數(shù)組和數(shù)組名內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解計(jì)數(shù)排序算法及C語言程序中的實(shí)現(xiàn)
技術(shù)排序算法與我們普通接觸的冒泡排序和快速排序等基于元素比較的算法不同,在編程中通過C語言的數(shù)組能夠清除地表達(dá)出來,這里我們就來詳解計(jì)數(shù)排序算法及C語言程序中的實(shí)現(xiàn)2016-07-07Qt中CQGUI框架之陰影圓角窗口實(shí)現(xiàn)
這篇文章主要介紹了Qt中CQGUI框架之陰影圓角窗口實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03C語言中g(shù)etchar()與putchar()函數(shù)詳解
本文主要介紹了C語言中g(shù)etchar()與putchar()函數(shù)詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01C++踩坑實(shí)戰(zhàn)之構(gòu)造和析構(gòu)函數(shù)
不論是構(gòu)造函數(shù),還是析構(gòu)函數(shù),都是C++、C#語言相對(duì)于其他語言而言特殊的地方,它是為了方便類中對(duì)象的初始化,這篇文章主要給大家介紹了關(guān)于C++踩坑實(shí)戰(zhàn)之構(gòu)造和析構(gòu)函數(shù)的相關(guān)資料,需要的朋友可以參考下2021-07-07C語言的動(dòng)態(tài)內(nèi)存管理的深入了解
這篇文章主要為大家詳細(xì)介紹了語言C的動(dòng)態(tài)內(nèi)存管理,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-02-02