2012-07-31 147 views
-1

下面是一個簡單的代碼片斷:使用malloc分配沒有分配的內存,怎麼樣?

int main() 
{ 
int *p; 
p=(int*)malloc(sizeof(int));//allocate m/y 4 1 int 
printf("P=%p\tQ=%p",p,p+2); 
} 

在一個樣品運行,它給我的輸出如下:

P=0x8210008 Q=0x8210010 

P的起始地址-P = 0x8210008,下一個字節是0x8210009 ,下一個字節是0x821000A,下一個字節是0x821000B。因此int的4個字節在那裏結束。 我們還沒有使用malloc分配更多的內存。 那麼p + 2是如何將我們導向0x8210010的,它是P(0x8210008)之後的8個字節。

+3

在C中,你永遠不會通過像這樣的測試獲得很遠的學習;由於*未定義的行爲*你會發現很多不一致 - 你都不應該指望它們。 – Dave 2012-07-31 05:05:51

+1

請首先使用SO搜索工具:[我可以使用更多的內存,而不是使用malloc()分配多少內存,爲什麼?](http://stackoverflow.com/questions/3509714/i-can-使用多內存而不是使用多少分配與malloc-why) – 2012-07-31 09:56:53

回答

4

首先,事實證明你已經打印的地址並不意味着在該地址被分配內存。您只需添加數字並生成其他數字。

其次,你通過加2得到的數字的原因是比基地址大8,而不是比基地址大2,這是因爲,當你在C中增加指針的整數時,算法是根據指向元素,而不是內存中的字節(除非指向的元素是字節)。假設你有一個int數組,例如int x[8],並且你有一個指向x[3]的指針。向該指針添加兩個會生成一個指向x[5]的指針,而不是指向x[3]開頭以外的兩個字節的指針。記住C是抽象是很重要的,C標準指定了抽象內部發生的事情。在C抽象內部,指針運算對元素的數量起作用,而不是原始內存地址。 C實現(編譯器和將C代碼轉化爲程序執行的工具)需要對原始內存地址執行任何操作,以便實現由C標準指定的抽象。通常,這意味着編譯器在將元素添加到指針時將整數乘以元素的大小。所以兩個乘以四(在一臺機器上,其中int是四個字節),並且八個結果被添加到基地址。

三,你不能依賴這種行爲。 C標準只針對指向數組內對象的指針定義指針運算,包括數組末尾的一個虛構對象。另外,指向單個對象的指針就像一個元素的數組一樣。因此,如果您有一個指向p的指向int的指針,則允許您計算p+0p+1,因爲它們指向數組中唯一的對象(p+0),而虛擬對象指向數組中最後一個元素之外的一個對象(p+1 )。你是而不是允許計算p-1p+2,因爲這些都在數組之外。請注意,這不是取消引用指針(嘗試讀取或寫入計算出的地址處的內存)的問題:即使僅僅計算,該地址也會導致C標準未定義的行爲:您的程序可能會崩潰,它可能會崩潰給你「正確」的結果,或者它可以刪除你帳戶中的所有文件,所有這些行爲都符合C標準。

單純計算出界限地址不會產生這種奇怪的行爲。但是,該標準允許它,因爲一些計算機處理器具有不尋常的地址方案,這需要比簡單算術更多的工作。也許在平坦地址空間之後的第二常用地址方案是基地址和偏移方案。在這種方案中,四字節指針的高16位可能包含基地址,低16位可能包含偏移量。對於給定的基地址b和偏移量o,相應的虛擬地址可能是4096 * b + o。 (這種方案僅能夠尋址字節,許多不同的基址和偏移值可以指相同的地址,例如,基址0和偏移量4096是指與基址1和偏移量0相同的地址。 )使用base-and-offset方案時,編譯器可以通過僅添加到偏移量並忽略基數來實現指針算術。 (這樣的C實現可以支持高達65536字節的數組,只能通過偏移量尋址的範圍)。在這種實現中,如果您有指向int p且編碼爲0x0000fffc(基址0,偏移量65532)的指針,並且int是四個字節,則p+2將具有值0x00000004,而不是八個更大的值(0x00010004)。

這是一個例子,其中指針算術產生的值不會在平地址機器上產生。很難想象一個實現,其中根據C標準無效的指針運算會導致崩潰。但是,考慮一個實現,必須由進程手動交換內存,因爲處理器沒有硬件來支持虛擬內存。在這種實現中,指針可能包含內存中用於描述磁盤位置和用於管理內存交換的其他信息的結構地址。在這樣的實現中,執行指針運算可能需要讀取內存中的結構,因此執行無效指針運算可能會讀取無效地址。

1
+0

不,它被稱爲未定義的行爲。 – 2012-07-31 04:00:52

+0

@R它不是未定義的,如果P = 0x8210008的地址比p + 2的地址必然是0x8210010 – AlexDev 2012-07-31 04:02:39

+3

否則不會。指針運算僅在數組內定義,並且直到位於最後一個數組元素的「過去」位置。由於所討論數組的長度爲1,所以加2會超出這些界限,從而調用未定義的行爲。 – 2012-07-31 04:06:05

4

C很樂意讓你做任何你喜歡的指針算法。僅僅因爲p+2看起來像任何其他地址並不意味着它是有效的。事實上,在這種情況下,事實並非如此。

每次看到指針算術時都要非常小心,不要超出分配範圍。

+0

這不能回答提問者的問題,因爲它不能解釋爲什麼他們得到的值比基地址大8而不是2大。首先,如果他們打印了p + 1,他們將得到一個更大的地址4,因爲向指針添加一個整數產生該類型的下一個對象的地址,而不是內存中下一個字節的地址,所以C實現實現根據需要進行地址算術以實現此目的。其次,如其他註釋中所述,加2會超出編譯器所需的指針算術範圍,所以行爲不確定。 – 2012-07-31 08:01:47

5

因爲它將它視爲指針的整數元素偏移量。您已經爲單個整數分配了一個數組。當您要求p+2時,它與&p[2]相同。如果你想從一開始兩個字節,你需要將其轉換爲char*第一:

char *highWordAddr = (char*)p + 2;