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"&<&因為編譯器為了防止他人推算出函數的返回地址從而進行修改,使用了棧隨機化技術。主要是為了防止軟體被攻擊的一種策略
推薦閱讀:
※為什麼單機遊戲的啟動時間一般都比較長?有沒有辦法進行縮短?
※python2.7.9 安裝錯誤 there is a problem with...該怎麼解決?
※如何利用python讀取特定目錄下的特定文件的倒數兩行?
※能不能寫出一個程序,通過自身的遞歸迭代,自己就產生了智能呢?
※有哪些動漫作品是與編程有關的?