C語言中連續定義兩個變數,為什麼地址是這樣的?

環境:64位系統,Xcode的clang編譯器

以前聽老師分析內存,說變數分配的內存是從下到上。那麼後定義的變數地址要小一些。

在知道兩個變數的類型後,可以知道連續定義變數的地址相差多少!

補:可能是以前老師講數組時那麼分析的吧,不知道我記沒記錯。所以我把當時做的筆記附在最後,大家幫我看看筆記是否正確,謝謝。

如上面兩個變數是連續定義的,c佔1位元組、a佔據4個位元組。

但是為什麼地址不是間隔4?

其實我以前連續定義了2個int類型的變數,再查看地址,的確是相差4。所以我感到奇怪,像題圖那種情況,Xcode會有特殊的處理機制嗎?

註:我正在學習結構體,老師提到了對齊(補齊)演算法(不懂)。

不知道定義的變數會不會有對齊演算法?以前沒有注意過變數地址的問題。

因為剛剛學習結構體,所以想到了這個問題。

在結構體中我理解的是:

struct Student

{

int age;// 4

char *name;//8

};

struct Student stus[3];

printf("%ld
", sizeof(stus));

//根據char *類型佔據的位元組倍數計算,8+4不是8的倍數,所以結果是8乘2得16(不知道是我理解的是不是正確?)

關於結構體,我想問的就是如何解釋sizeof(結構體變數)的計算方式

上面那段代碼中結構體變數只有兩個成員分別佔用4位元組和8位元組,但結構體大小是16位元組。通過指針間接取值時,是取成員age時,取4位元組。取成員name時,取8位元組嗎?

補充一個問題:老師講指針時說,指針有類型是為了方便取值、間接改值。結構體變數的大小有特殊計算,如指向結構體的指針,操作指針是怎麼正確獲取成員的值,難道是根據每個結構體成員分別佔據的大小,來取值的?

附上筆記:


不要試圖去固化理解編譯器的行為,尤其是C語言規範中沒有定義的行為

這些行為包括但不限於:變數分布,++/--混用的時候的優先順序,函數調用時參數的計算次序等等

你這個問題里,局部變數在棧上的排列次序是語言標準中沒有規定的內容,所以:

它們怎麼排列,間隔是多大,完全看編譯器的設計者,換句話說,可能是任何形式。

如果你們老師非要說局部變數地址一定是從下到上,那麼,你們老師錯了

舉幾個例子:

1. 在比較老的編譯器里,如果沒有對變數取地址的操作,那麼有些局部變數是通過寄存器保存的,不佔棧上內存,根本不存在內存中如何排列的問題,比如TurboC 2.0這種。

2. 在一些較新的編譯器里,局部變數有些是根據使用頻率來排列的,這樣可以降低cache的miss率,所以怎麼排列完全根據使用頻率。

3. 大部分主流編譯器的局部變數地址確實是從下到上,但也有反過來的。從下到上排列的好處之一是編譯器處理一個函數的時候,可以動態增長棧的長度,不需要先判斷局部變數有多少個。

4. 還有一些非主流編譯器會調整變數的排列次序,使得其在各個變數基本對齊的情況下佔用的棧空間最小,對於一些嵌入式設備來說,非常有用。

所以,你的第一個問題,如果你非要研究編譯器,也可以,但這不是常態,不同編譯器行為可能不同。同一款編譯器在不同平台上也可能不同。

第一個問題里,a的地址範圍是XXXX6BE8-XXXX6BEB,中間空出3個位元組,然後是c的地址,正好對齊到4位元組。至於為什麼c的地址是XXXX6BEF,而不是XXX6BEC,這個可能就是編譯器的特性吧。

第二個問題:結構體占的位元組大小,這個確實會有對齊的情況,但除非你用packed之類的編譯參數指定,否則在不同的硬體平台上,效果可能也不完全一樣。


變數是不需要對齊得那麼緊密的的,VC++的debug會在變數中間插入垃圾空間來檢查你有沒有跑飛了什麼的。


確實是對齊的緣故。

「變數分配的內存是從下到上」這話聽起來有點奇怪。。。

實際做法按照定義的順序分配地址,地址是從上往下分配的。

所以首先設置好c的地址。

接著,a的地址必須是4的整數倍,從c的地址0xef往下,第一個4的整數倍是0xec,但是它們之間只有3個空位,所以沒法用。

於是a必須再往下找下一個4的整數倍,即0xe8。

貼另一段代碼

int main(){

char c="A";

char d="A";

char e="A";

char f="A";

int a=10;

printf("a=%p
",a);

printf("c=%p
",c);

printf("d=%p
",d);

printf("e=%p
",e);

printf("f=%p
",f);

}

f和a的距離就是4了。


我是這樣理解的:你定義一個變數A、系統就隨便在RAM里找一塊剛好能用的放下來、再定義一個B、再找一塊、如果還要用一個指針去定義剛剛A放哪了、然後放下B、再移動指針、我覺得這樣多此一舉、因為你是分開定義的、你訪問完A不一定要訪問B、所以這個功能有點多餘、乾脆就隨便放、數組是用下標來找的、第i個剛好是偏移了(i-1)*sizeof(數據類型)、很好找、如果每個元素都是隨便找一個位置放下來的話估計就不這麼好找了、挺符合C++不是必要盡量不加的思想的。。。


題主 自己動手,豐衣足食啊~ &>_&<

以下http://googe.com來的

計算機原理學習(3)-- 內存工作原理

CPU如何操作內存?

組成原理說明------地址對齊

由底層和邏輯說開去 —— 內存對齊機制深入剖析


C語言從來沒規定連續定義兩個變數地址應該怎樣

所以問題根本不成立


老師分析錯了

相差多少大V[V大?] 已經解答

我說說順序的問題

只有數組元素 結構體成員

之間

有順序排列的關係

而你這裡定義的2個變數 沒有先後順序

a可以在c 之前

也可以在c 之後


與語言無關,全是編譯器的行為,變數在內存中的分配是編譯器在支配。


是不是和內存對齊有關。。


應該只有數組的地址是連續分配的,不同變數是不一定的。


為了提高效率的變數對齊。在vs中x64是8位元組對齊。


不管什麼類型的指針,存的都是地址,所以都是佔4位元組空間。想了解這些細節的東西下個ida或ollydbg把exe拖進去看下反彙編代碼就一清二楚了。

經評論提醒沒注意題主是64位系統,按照64位系統目標編譯的話指針存放需要8位元組。


以下程序在VS2015和VS6.0的運行結果分別如圖:

#include &

int main()

{

printf("Hello wrold!
");

int a = 10;

int b = 10;

int c = 10;

int d[3];

d[0] = a;

d[1] = b;

d[2] = c;

printf("a的地址是%p
b的地址是%p
c的地址是%p
", a, b, c);

printf("a的地址是%p
b的地址是%p
c的地址是%p
", d[0], d[1], d[2]);

printf("d[0]的地址是%p
d[1]的地址是%p
d[2]的地址是%p
", d[0], d[1], d[2]);

system("pause");

return 0;

}

以下程序可檢測CPU是big endian還是little endian

bool IsBigEndian()

{

int value = 0x1234;

char lowAdd = *(char *)value;

if( lowAdd == 0x12)

{

cout&<&<"true"&<& return true;

}

cout&<&<"false"&<& return false;

}

然而並無卵用。

VS2015依然是從小到大。。


因為編譯器為了防止他人推算出函數的返回地址從而進行修改,使用了棧隨機化技術。主要是為了防止軟體被攻擊的一種策略


推薦閱讀:

為什麼單機遊戲的啟動時間一般都比較長?有沒有辦法進行縮短?
python2.7.9 安裝錯誤 there is a problem with...該怎麼解決?
如何利用python讀取特定目錄下的特定文件的倒數兩行?
能不能寫出一個程序,通過自身的遞歸迭代,自己就產生了智能呢?
有哪些動漫作品是與編程有關的?

TAG:程序員 | 編程語言 | 編程 | C編程語言 | 程序 |