int main()
{
int* p;
p = (int*)malloc(sizeof(int));
*p = 10;
return 0;
}
在C99和C2011規則,調用malloc
沒有可見的聲明是一個約束違反,這意味着符合編譯器必須發出診斷。 (這與C接近說有些東西是「非法的」)。如果你的編譯器沒有警告這個調用,你應該找出使用它的選項。
根據C90規則,調用沒有可見聲明的函數會導致編譯器假定該函數實際上返回類型爲int
的結果。由於malloc
實際上定義爲返回類型爲void*
,因此行爲未定義;編譯器不需要進行診斷,但標準完全沒有說明在調用評估時會發生什麼。
什麼通常發生在實踐中是,編譯器生成的代碼彷彿malloc
被定義爲返回一個int
結果。例如,malloc
可能會將其64位void*
結果放在某個特定的CPU寄存器中,並且調用代碼可能爲假定該寄存器包含一個32位的int
。 (這不是一種類型轉換;它只是錯誤的代碼,錯誤地將一種類型的值看作是不同類型的值。)(可能是垃圾)int
值然後是轉換爲到int*
並存儲在p
中。你可能失去高位或返回指針的低位32位 - 但這只是一個可以出錯的任意方式。
或malloc
可能會將其64位結果壓入堆棧,調用者可能只彈出堆棧中的32位,導致堆棧未對齊,從而導致所有後續執行不正確。由於歷史原因,C編譯器通常不使用這種調用約定,但標準允許它。
如果int
,void*
和int*
都發生在相同的大小(因爲他們經常是在32位系統),代碼是可能的工作 - 但即使這不能保證。例如,調用約定可能會使用一個寄存器來返回int
結果,而另一個返回指針結果。再次,大多數現有的C調用約定允許使用這樣的假設的舊的錯誤代碼。
調用malloc
需要#include <stdlib.h>
,即使有些編譯器可能不會執行該要求。添加#include
(並放下演員)要比花時間思考如果沒有時會發生什麼更容易。
首先,它被(隱式)轉換爲「int」,從而從8個字節截斷爲4個字節。除非原始8字節值的第一個(更重要的)4個字節包含全零,否則某些信息會丟失。將結果轉換回'int *'不會檢索到這些信息,並留下一個「假」指針。 –
當編譯器遇到'int * p'聲明時,它會在聲明時確定此指針需要8個字節(例如)的存儲空間。接下來,在運行時,執行malloc時,它會返回一個默認的'int',因爲沒有函數原型聲明存在(由於缺少stdlib.h)。當新分配的內存的地址(8個字節)存儲在一個int(4個字節)並返回給您時,可能會失去重要性。現在,在這一點上,如果您嘗試將int轉換爲int *並將其分配給您的指針'p',那麼可能會出現意想不到的行爲。 – iammowgli