詳解C++數(shù)組和數(shù)組名問題(指針、解引用)
一、指針
1.1 指針變量和普通變量的區(qū)別
指針:指針的實質(zhì)就是個變量,它跟普通變量沒有任何本質(zhì)區(qū)別。指針完整的應該叫指針變量,簡稱為指針。 是指向的意思。指針本身是一個對象,同時指針無需在定義的時候賦值。
1.2 為什么需要指針
指針的出現(xiàn)是為了實現(xiàn)間接訪問。在匯編中都有間接訪問,其實就是CPU的尋址方式中的間接上。
間接訪問(CPU的間接尋 址)是CPU設計時決定的,這個決定了匯編語言必須能夠實現(xiàn)問接尋又決定了匯編之上的C語言也必須實現(xiàn)簡介尋址。
1.3 指針使用三部曲
三部曲:定義指針變量、關聯(lián)指針變量、解引用
(1)當我們int *p定義一個指針變量p時,因為p是局部變量,所以也道循C語言局部變量的一般規(guī)律(定義局部變量并且未初始化,則值是隨機的),所以此時p變量中存儲的是一個隨機的數(shù)字。
(2)此時如果我們解引用p,則相當于我們訪問了這個隨機數(shù)字為地址的內(nèi)存空間。那這個空間到底能不能訪問不知道(也許行也許不行),所以如果直接定義指針變量未綁定有效地址就去解引用幾平必死無疑。
(3)定義一個指針變量,不經(jīng)綁定有效地址就去解引用,就好象拿一個上了鏜的槍隨意轉了幾圈然后開了槍。
(4)指針綁定的意義就在于讓指針指向一個可以訪問、應該訪問的地方(就好象拿著槍瞄準且標的過程一樣),指針的解引用是為了間接訪問目標變量(就好象開槍是為了打中目標一樣)
int val = 43; int * p = &val; // &在右值為取值符 cout << *p << endl; //輸出 43
二、整形、浮點型數(shù)組
前言
- 在很多用到數(shù)組名字的地方,編譯器都會自動地將其替換為一個指向該數(shù)組首元素的指針。
- 所以,在大多數(shù)表達式中,使用數(shù)組名其實是在使用一個指向該數(shù)組首元素的指針。
2.1 數(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的構成是很相似的。它們實際存的都是一個地址,都會指向一個對象(或多個對象的第一個對象)。所以說數(shù)組名其實是種特殊的指針。
- 為什么說是特殊呢?
一維數(shù)組
int a[] = { 0,1,2,3,4 };
int * p1 = a;
int *p = &a[0];
//指針p是 int * 的,而首元素是有地址的,所以取址,是允許的
//int * p1 = &a; //錯誤
//理解:int (*p1)[5] = &a; //正確
/*
但它倆自身又有不同:
指針 p1 本身是一個對象,在內(nèi)存中是為其分配了空間的;
數(shù)組名 a 在內(nèi)存空間中沒有分配到空間(這將導致&a操作的效果可能和預想的不大一樣)。
可以理解為a指向一個含有5個整數(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 理解復雜的數(shù)組的聲明
上述提到數(shù)組名是指向一個數(shù)組的指針,因此解釋一下一些復雜的數(shù)組聲明。加深理解
int * ptr[10]; //ptr是含有10個 整形指針 的數(shù)組 int & ref[10] = /* ? */ //錯誤,不存在引用的數(shù)組 int (*parray) [5] = &a; //parray指向一個含有5個整數(shù)的數(shù)組 /* 同時也是上述數(shù)組名的解釋 *parray意味著parray是一個指針; 右邊是[5]表明是指向大小為10的數(shù)組; 左邊int表明數(shù)組中元素為int. */ int (&array)[5] = a; //array引用一個含有5個整數(shù)的數(shù)組 int * (&array) [10] = ptrs; //array是數(shù)組的引用, 該數(shù)組是含有10個指針的數(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既然是種特殊的指針,那么其打印時就會是存的地址。&a的類型是int(*)[5](讀法從小括號里往外,首先是指針,然后是大小為5的數(shù)組,然后數(shù)組元素類型是int),從局部變量中看到其類型也可寫成int[5] *:即指向大小為5的int數(shù)組的指針。由于數(shù)組名沒有內(nèi)存分配空間&a[0]就是取一個int對象的地址,這個int對象是數(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就應該增加20。sizeof(a)為20,因為數(shù)組總的字節(jié)大小為20。sizeof(&a)為4,因為&a是一種指針,指針在32位系統(tǒng)中占4字節(jié)。
2.4 對數(shù)組名以及取值符&的理解
數(shù)組中每個元素都是對象,即占有特定類型的內(nèi)存空間。(對象,占有一塊數(shù)組類型的內(nèi)存空間。因為對象是指一塊能存儲數(shù)據(jù)并且具有某種類型的內(nèi)存空間。)
數(shù)組名可以轉化為這個數(shù)組對象的首個元素的地址。
這里我們不去討論一維數(shù)組,直接從二維說起。所謂二維數(shù)組也是數(shù)組,只不過它的元素也是一個數(shù)組。
首先我們寫一個二維數(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個元素的數(shù)組,每個元素是一個包含10個 int 的數(shù)組。
既然說到數(shù)組名是其首個對象的地址那么來驗證一下,測試數(shù)組名,以及對數(shù)組名求地址:
void test01(){
cout << (long long)a << endl; // 140273290059808
cout << (long long)(a+1) << endl; // 140273290059848
// 相差40個字節(jié)
}
(用long long 型一眼就能看出是40個字節(jié))
a 與 a + 1 正好相差40個字節(jié),說明:
(1)數(shù)組名a 是(首元素{1,2,3,4,5,5,6,7,8,8})這一整行對象的地址,即首元素地址;
(2)所以在a+1偏移了一個元素大小——40字節(jié)。
void test01(){
cout << (long long)&a << endl; // 140273290059808
cout << (long long)(&a+1) << endl; // 140273290059888
// 相差80個字節(jié)
}
&a 與 &a + 1 正好相差80個字節(jié),說明:
(1)取址符取得是整個對象的地址,&a 是對二維數(shù)組求址,針對的是整個對象;
(2) &a+1 偏移一位就變成了整個二維數(shù)組的尾地址,c++中的尾地址是對象所在地址的下一位。&a+1 正好比 a 多了 80 個字節(jié)。
在上面也提到數(shù)組名會自動轉換成一個特殊指針(兩個表格當中的總結),接下來將理解這個指針到底是什么?
從指針解引用方面理解:
void test03(){
cout << *a << endl; // 0x7f051ce02020
//為了驗證,我們偏移一下
cout << *(a + 1) << endl; // 0x7f051ce02048
// 正好相差40個字節(jié)
}
*a 把數(shù)組名解引用之后是首元素(因為數(shù)組名是指向首元素的特殊指針),而首元素也是一個有10個元素的數(shù)組,現(xiàn)在 *a 是代表這個對象,輸出它就是此數(shù)組的首元素——1 的地址.。
cout << *(*a) << endl; //1 **a 即可
第二層解掉:*(*a) 自然就是第一個 int 型的元素。
cout << *a[0] << endl; // 1
因為指針指向數(shù)組對象時,可以用下標訪問下一個位置,又 a 是指針指向了數(shù)組,下一個偏移為 0,即 * a = * (a + 0)
// cout << (a[0])[0] << endl; cout << a[0][0] << endl;// 1
基于上述, *a 也就是a[0],也會自動轉化為指向自己的首個對象(10個元素的第一個元素的位置)的指針。所以 a[0] 可以用下標訪問數(shù)組對象(10個元素)內(nèi)其他元素:a[0][0] == 1
我們多搞幾個案例:
// 要是訪問當前行的下一個元素呢?將這個首地址
cout << *(*a + 1) << endl;// 2 即 *((*a) + 1)
// 請注意這里的指針是 (*a),
cout << (*a)[1] << endl; // 2
// 同理(*a)相當于*(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)
// 第二行第二個元素呢?
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個 int 組成數(shù)組
PA10_i :是一個指針類型, 指向一個數(shù)組對象,這個數(shù)組對象有10個int型的對象。編譯器會識別為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表示這個是數(shù)組類型,2表示是兩個對象組成的數(shù)組,每個對象(A10_i)是由有10個對象的數(shù)組,這10個對象是int型的。
PA2_A10_i:是一個指針類型, 指向一個數(shù)組對象,這個數(shù)組對象有2個數(shù)組型的對象。編譯器會識別為int(*)[2][10]
三、字符數(shù)組數(shù)組名
c++對待字符數(shù)組名不會輸出其地址,會直接輸出字符
#include <iostream>
using namespace std;
int main()
{
//int a[5]={1,2,34,4,5}; //如果不是char型,cout<<"a="<<a<<endl;
// 輸出的為int數(shù)組首地址。不會輸出數(shù)組中的值。
char a[5]="aaaa"; //cout重載了char[],可以輸出整個字符串數(shù)組
cout<<"a="<<a<<endl;
return 0;
}
詳細參考這篇博客
到此這篇關于詳解C++數(shù)組和數(shù)組名問題(指針、解引用)的文章就介紹到這了,更多相關C++數(shù)組和數(shù)組名內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C/C++ break和continue區(qū)別及使用方法
這篇文章主要介紹了C/C++ break和continue區(qū)別及使用方法的相關資料,需要的朋友可以參考下2017-07-07

