本人錄制技術視訊位址:https://edu.csdn.net/lecturer/1899 歡迎觀看。
還是在大學時代接觸的C語言,當時學習數組、指針等概念時,怎一個“暈”字了得。最近在學習之餘,瘋狂地惡補了相關知識,故總結之,如有錯誤,請大家多多指點。
一、 記憶體位址分析
1) 先來看一個最基礎的例子:
int a[4];
提問:&a[0], a, &a, a+1, &(a+1), &a+1 分别表示什麼?
咋一看,真的不知所措; 我們可以圖解來分析它(假設下面的操作均在32為系統上面)。
先來對上圖進行簡單的說明工作:
1. 紫色區域就是數組a在記憶體中存儲的具體的值,因為定義的時候是a[4], 是以記憶體存儲區域有四塊“具體數值”内容。
2. 假設數組中的第一個元素在記憶體中的位址為0x8000, 是以各個元素的位址如上圖所标注的,即他們表示數組a中各個元素的記憶體位址。
3. &a所指向的是整個數組在記憶體中的位址。
下面,我們再對剛才提出的問題進行一一解答。
1. &a[0] : 對應上圖,它的值就是0x8000, 即表示數組a的第一個元素的位址。
2. a : 和&a[0] 是等價的;注意a是一個指針常量,即它的值不可以被改變。是以數組名可以了解為是一個指針常量,它所表示的值便是數組中第一個元素的位址。
3. &a : 它表示的是整個數組的位址,雖然它的值和a及&a[0] 是一緻的;但表達的含義卻是不一緻的;
4. a+1 : 因為a表示的是數組中第一個元素的位址,是以a+1表示第二個元素的位址,也就是0x8004。
5. &(a+1) : 這個表達式是錯誤的。隻有整個數組的位址才可以寫成&a, 而a+1 表示第二個元素的位址,此時再取位址(&)是不正确的。
6. &a+1 : &a表示整個數組的位址,再加上1表示加上整個數組位元組長度的位址,是以它的值就是上圖中标注的0x8010。
2) 我們再來看一個位址分布執行個體:
int a = 3;
int b;
int main(int argc, const char * argv[]) {
// insert code here...
int c = 4;
int d;
char *str = "hello world";
char str2[] = "good idea";
printf("%p\n",&a);
printf("%p\n",&b);
printf("%p\n",&c);
printf("%p\n",&d);
printf("%p\n",str);
printf("%p\n",&str);
printf("%p\n",*(&str)); // 可以看出,&str位址中存放的内容是str的位址
printf("%p\n",str2);
printf("%p\n",&str2);
return 0;
}
在我電腦上面的列印結果為:
0x100001028
0x10000102c
0x7fff5fbff744
0x7fff5fbff740
0x100000f78
0x7fff5fbff738
0x100000f78
0x7fff5fbff75e
0x7fff5fbff75e
直接看這些結果,放佛沒有什麼規律可言。不過大家可以參照《C語言:連結屬性與存儲類型》這一節的内容進行分析。在這一節中,我最後給出了一張記憶體配置設定圖,如下:
1. a是一個已初始化的全局變量,是以它的位址比較小。
2. b是一個未初始化的全局變量,是以它配置設定的記憶體位址比a大,列印出的結果,他們相差4個位元組,是以在記憶體配置設定上是連續的。
3. c,d 均是配置設定在棧存儲區的,是以他們的記憶體位址看起來比a,b的大了許多。
4. str存放的是一個字元串常量,它存儲在隻讀資料區。是以它的列印值比a的還要小。
5. &str是一個指向字元串常量的指針,它是配置設定在棧上面的,是以它的值和c,d比較接近。
6. *(&str) 列印出來的結果和str是一緻的。即&str位址中存放的内容是str的位址。
7. str2 和 &str2 已經在第一個例子中進行了分析,這裡就不在重複說明了。
二、 sizeof 和 strlen
sizeof在c語言中使用比較廣泛。
1. 直接實參傳遞數組名
int a[10];
printf("%ld\n",sizeof(a));
sizeof計算的是數組位元組大小,輸出結果為 40。
2. 傳遞的是位址
void foo(int a[]) {
printf("%ld\n",sizeof(a));
}
然後調用的時候:
foo(a);
注意:這裡傳遞的a是一個指針,即a數組所在的位址。隻是在定義函數foo的時候,故意将形參寫成int a[] 數組的形式。這樣在使用的時候,放佛就是在操作數組一樣。但其實這是一個誤區,它實際傳遞的就是指針。是以foo函數完全可以定義成這種形式
void foo(int *a) {
printf("%ld\n",sizeof(a));
}
是以說上述兩種對foo函數的定義是等價的。現在我們再來分析函數foo中列印的sizeof(a) 就容易多了。因為a是一個位址,是以在32位系統中它占4個位元組,故列印結果為4。
3. 用sizeof來分析 字元數組
char str[5] = "hello";
printf("%s\n",str);
分析列印結果?
列印結果有三種可能:1. hello; 2. hello加一些亂碼; 3. 程式直接奔潰
分析:因為定義的str數組長度為5,是以數組沒有\0結尾。
1. 如果記憶體中str數組後面的位元組正好是 00, 則表示str數組結束,輸出hello。
2. 如果str數組後面的位元組不是 00, 則會繼續往記憶體後面尋找,過了很多位元組後,終于找到 00 了,是以會輸出 hello加一些亂碼。
3. 如果str數組後面的位元組不是 00, 則會繼續往記憶體後面尋找,且正好找到了系統禁止通路的記憶體區域,則程式直接奔潰。
字元數組配置設定記憶體的機制結論:
如果數組定義為:char str[5] = "hello", 則記憶體配置設定中,不會加上'\0', 是以 sizeof(str) 結果為5
如果數組定義為:char str2[] = "hello", 則記憶體配置設定中會加上'\0',是以 sizeof(str) 結果為6
如果數組定義為:char str3[7] = "hello", 則記憶體配置設定中會加上'\0\0',是以 sizeof(str) 結果為7
上面的str,str2,str3 經過strlen計算後的結果均為5。而sizeof的值是一直變化的。
說明strlen 是計算的字元串實際的長度,sizeof是計算數組初始化時的長度