天天看點

C語言:記憶體位址分析 & sizeof和strlen用法總結

本人錄制技術視訊位址:https://edu.csdn.net/lecturer/1899 歡迎觀看。

還是在大學時代接觸的C語言,當時學習數組、指針等概念時,怎一個“暈”字了得。最近在學習之餘,瘋狂地惡補了相關知識,故總結之,如有錯誤,請大家多多指點。

一、 記憶體位址分析

1) 先來看一個最基礎的例子:

int a[4];
           

提問:&a[0],  a,  &a,  a+1,  &(a+1),  &a+1 分别表示什麼?

咋一看,真的不知所措; 我們可以圖解來分析它(假設下面的操作均在32為系統上面)。

C語言:記憶體位址分析 & sizeof和strlen用法總結

先來對上圖進行簡單的說明工作:

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語言:連結屬性與存儲類型》這一節的内容進行分析。在這一節中,我最後給出了一張記憶體配置設定圖,如下:

C語言:記憶體位址分析 & sizeof和strlen用法總結

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是計算數組初始化時的長度

繼續閱讀