還是在大學時代接觸的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 ,&a); printf(%p ,&b); printf(%p ,&c); printf(%p ,&d); printf(%p ,str); printf(%p ,&str); printf(%p ,*(&str)); // 可以看出,&str地址中存放的內容是str的地址 printf(%p ,str2); printf(%p ,&str2); return 0; }
0x100001028
0x10000102c
0x7fff5fbff744
0x7fff5fbff740
0x100000f78
0x7fff5fbff738
0x100000f78
0x7fff5fbff75e
0x7fff5fbff75e
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 ,sizeof(a));sizeof計算的是數組字節大小,輸出結果為 40。
2. 傳遞的是地址
void foo(int a[]) { printf(%ld ,sizeof(a)); }
foo(a);注意:這裡傳遞的a是一個指針,即a數組所在的地址。只是在定義函數foo的時候,故意將形參寫成int a[] 數組的形式。這樣在使用的時候,放佛就是在操作數組一樣。但其實這是一個誤區,它實際傳遞的就是指針。所以foo函數完全可以定義成這種形式
void foo(int *a) { printf(%ld ,sizeof(a)); }所以說上述兩種對foo函數的定義是等價的。現在我們再來分析函數foo中打印的sizeof(a) 就容易多了。因為a是一個地址,所以在32位系統中它占4個字節,故打印結果為4。
3. 用sizeof來分析 字符數組
char str[5] = hello; printf(%s ,str);分析打印結果?
打印結果有三種可能:1. hello; 2. hello加一些亂碼; 3. 程序直接奔潰
分析:因為定義的str數組長度為5,所以數組沒有結尾。
1. 如果內存中str數組後面的字節正好是 00, 則表示str數組結束,輸出hello。
2. 如果str數組後面的字節不是 00, 則會繼續往內存後面尋找,過了很多字節後,終於找到 00 了,所以會輸出 hello加一些亂碼。
3. 如果str數組後面的字節不是 00, 則會繼續往內存後面尋找,且正好找到了系統禁止訪問的內存區域,則程序直接奔潰。
字符數組分配內存的機制結論:
如果數組定義為:char str[5] = hello, 則內存分配中,不會加上'', 所以 sizeof(str) 結果為5
如果數組定義為:char str2[] = hello, 則內存分配中會加上'',所以 sizeof(str) 結果為6
如果數組定義為:char str3[7] = hello, 則內存分配中會加上'',所以 sizeof(str) 結果為7
上面的str,str2,str3 經過strlen計算後的結果均為5。而sizeof的值是一直變化的。
說明strlen 是計算的字符串實際的長度,sizeof是計算數組初始化時的長度