簡體   English   中英

printf("%p") 並轉換為 (void *)

[英]printf("%p") and casting to (void *)

在最近的一個問題中,有人提到在使用 printf 打印指針值時,調用者必須將指針強制轉換為 void *,如下所示:

int *my_ptr = ....

printf("My pointer is: %p", (void *)my_ptr);

對於我的生活,我無法弄清楚為什么。 我發現了這個問題,這幾乎是一樣的。 問題的答案是正確的 - 它解釋了整數和指針的長度不一定相同。

這當然是真的,但是當我已經有一個指針時,就像上面的例子一樣,為什么我應該從int *void * 什么時候 int * 與 void * 不同? 事實上, (void *)my_ptr什么(void *)my_ptr生成任何不同於my_ptr機器代碼?

更新:多個知識淵博的響應者引用了該標准,稱傳遞錯誤的類型可能會導致未定義的行為。 如何? 我希望printf("%p", (int *)ptr)printf("%p", (void *)ptr)生成完全相同的堆棧幀。 兩個調用何時會生成不同的堆棧幀?

%p轉換說明符需要一個void *類型的參數。 如果您不傳遞類型為void *的參數,則函數調用會調用未定義的行為。

來自 C 標准(C11,7.21.6.1p8 格式化輸入/輸出函數):

“p - 參數應是指向 void 的指針。”

C 中的指針類型不需要具有相同的大小或相同的表示形式。

具有不同指針類型表示的實現示例是 Cray PVP,其中指針類型的表示對於void *char *為 64 位,但對於其他指針類型為 32 位。

請參閱“Cray C/C++ 參考手冊”,“9.1.2.2”中的表 3.http://docs.cray.com/books/004-2179-003/004-2179-003-manual.pdf

在 C 語言中,所有指針類型的表示形式都可能不同。 所以,是的, int *void *不同。 一個能夠說明這種差異的現實平台可能很難(或不可能)找到,但在概念層面上,差異仍然存在。

換句話說,在一般情況下,不同的指針類型有不同的表示。 int *void *不同,也與double *不同。 就 C 語言而言,您的平台對void *int *使用相同的表示形式這一事實只不過是巧合。

該語言聲明某些指針類型需要具有相同的表示形式,其中包括void *char * ,指向不同結構類型的指針,或者說, int *const int * 但這些只是一般規則的例外。

其他人已經充分解決了將int *傳遞給具有固定數量參數原型函數的情況,該函數需要不同的指針類型。

printf不是這樣的函數。 它是一個可變參數函數,因此默認參數提升用於其匿名參數(即格式字符串之后的所有內容),如果每個參數的提升類型與格式效應器預期的類型不完全匹配,則行為未定義。 特別是,即使int *void *具有相同的表示,

int a;
printf("%p\n", &a);

有未定義的行為。

這是因為調用框架布局可能取決於每個參數的確切具體類型。 為指針和非指針類型指定不同參數區域的 ABI 在現實生活中已經出現(例如,摩托羅拉 68000 希望您盡可能將指針保留在地址寄存器中,將非指針保留在數據寄存器中)。 我不知道有任何現實世界的 ABI 隔離了不同的指針類型,但這是允許的,聽到一個我也不會感到驚訝。

c11: 7.21.6 格式化輸入/輸出函數 (p8):

p參數應是指向void指針 指針的值以實現定義的方式轉換為打印字符序列。

實際上,除了古老的大型機/小型機外,不同的指針類型極不可能具有不同的大小 但是,它們具有不同的類型,並且根據printf的規范,使用格式說明符的錯誤類型參數調用它會導致未定義的行為 這意味着不要這樣做。

回答問題:

兩個調用何時會生成不同的堆棧幀?

編譯器可能會注意到行為未定義,並發出異常、非法指令等。編譯器不需要嘗試生成堆棧幀、函數調用或其他任何東西。

有關編譯器在 UB 的另一種情況下執行此操作的示例, 請參見此處 它不是生成帶有空參數的尊重指令,而是生成ud2非法指令。

當根據語言標准未定義行為時,對編譯器的行為沒有要求。

使用 printf 打印指針值時,調用者必須將指針強制轉換為 void *

對於所有指針,即使強制轉換為void *也不夠。

C 有兩種指針:指向對象的指針和指向函數的指針。

任何對象指針都可以毫無問題地轉換為void*

printf("My pointer is: %p", (void *)my_ptr); // OK when my_ptr points to an object

未定義指向函數指針到void *轉換。

考慮 2021 年的系統,其中void *是 64 位,函數指針是 128 位。


C 確實指定(我的重點)

任何指針類型都可以轉換為整數類型。 除了前面指定的之外,結果是實現定義的。 如果結果不能以整數類型表示,則行為未定義。 結果不必在任何整數類型的值范圍內。 C17dr § 6.3.2.3 6

要打印函數指針可以嘗試:

printf("My function pointer is: %ju", (uintmax_t) my_function_ptr); // Selectively OK

C 缺少真正通用的指針,也缺少打印函數指針的干凈方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM