程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> C語言:內存地址分析 & sizeof和strlen用法總結

C語言:內存地址分析 & sizeof和strlen用法總結

編輯:關於C語言

C語言:內存地址分析 & sizeof和strlen用法總結


還是在大學時代接觸的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


直接看這些結果,放佛沒有什麼規律可言。不過大家可以參照《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
,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是計算數組初始化時的長度


 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved