![](/img/trans.png)
[英]Does %p print the address of the variable as a pointer, or does it print what it points to?
[英]What is the correct way to print the address of a variable already deallocated from the heap?
我正在制作一個日志記錄實用程序,該程序可以跟蹤我創建的庫中的分配和釋放。
我的程序沒有崩潰,但是我仍然對我的方法持懷疑態度。
void my_free(struct my_type *heap)
{
if (!heap)
logger("Fatal error: %s", "Null pointer parameter");
// I leave this here on purpose for the application to crash
// in case heap == NULL
free(heap->buffer);
void *address = heap;
free(heap);
logger("Heap at %p successfully deallocated", address);
// since heap->buffer is always allocated/deallocated together with
// heap I only need to track down the heap address
}
管理指向可能釋放的對象的指針的最佳實踐是將每個指針uintptr_t
為仍然有效的uintptr_t
(指向釋放之前的對象),並使用這些uintptr_t
值進行打印和其他用途。
根據C 2018 6.2.4 2,“指針指向的對象(或剛過去的對象)到達其生存期結束時,其值將變得不確定。”此規則的主要原因是允許使用C實現,其中字節指針中的“地址”不包含直接存儲器地址,而是包含用於在各種數據結構中查找地址信息的信息。 釋放對象后,盡管指針中的字節沒有更改,但是那些結構中的數據可能會更改,然后嘗試使用指針可能會以各種方式失敗。 值得注意的是,嘗試打印值可能會失敗,因為以前的地址在數據結構中不再可用。 但是,由於存在該規則,因此甚至更少的奇異C實現也可以利用它進行優化,因此C編譯器可以實現free(x); printf("%p", (void *) x);
free(x); printf("%p", (void *) x);
等同於free(x); printf("%p", (void *) NULL);
free(x); printf("%p", (void *) NULL);
, 例如。
要解決此問題,您可以將指針另存為uintptr_t
值,並將該uintptr_t
值用於進一步使用,包括打印:
#include <inttypes.h>
#include <stdint.h>
...
uintptr_t ux = (uintptr_t) (void *) x;
free(x);
…
printf("%" PRIxPTR, ux);
請注意,為了嚴格遵守,我們首先將指針轉換為void *
,然后uintptr_t
為uintptr_t
。 這僅僅是因為C標准沒有明確指定將任何指針轉換為uintptr_t
的行為,而是指定了將指針轉換為對象的行為為void *
以及將void *
轉換為uintptr_t
。
根據C標准 :
轉換說明符及其含義是:
...
p
該參數應為指向void的指針。 指針的值以實現定義的方式轉換為一系列打印字符。
因此,要完全符合C標准,您需要將heap
為void *
:
logger("Heap at %p successfully deallocated", ( void * ) address);
您在指針上使用過free()
的事實並不意味着您無法在大多數實現中打印指針的值(有關其他答案,請參閱其他答案的注釋,這可能是正確的)-這僅意味着您無法取消引用指針。
您的代碼很好。
如果您只想打印指針本身的值,則指針是否有效1都無關緊要。 畢竟,您可以使用%p
毫無問題地打印NULL
的值, 根據定義 ,該值是無效的指針值。
顯然,如果在釋放內存后嘗試使用*heap
或heap[i]
做某事,則會遇到未定義的行為,並且任何事情都可能發生,但是您可以檢查或打印出所有想要的heap
(或address
)而無需問題。
void *address = &heap;
->> void *address = heap;
在您的代碼中,如果本地函數參數不是它的值,則會得到該地址。
由於我的評分有限,因此我無法對其他人的帖子發表評論,因此在不是答案而是評論的情況下使用“答案”。
Eric Postpischil的上述答案/評論需要同行評審。 我不同意或理解他的理由。
“ ...當對象被釋放時,盡管指針中的字節沒有更改,但是那些結構中的數據可能會更改,然后嘗試使用指針可能會以各種方式失敗。值得注意的是,嘗試打印值可能會失敗因為.. “
注意標准C free()調用的原型:void free(void * ptr)。 在調用的地方,ptr 值在免費調用之前和之后將保持不變-通過value傳遞它。
“用”指針會/可能以不同的方式失敗,如果使用我們指的是free()調用后, 尊重它。 打印指針的值不會影響它。 即使使用實現定義了指針值表示形式,也可以通過使用%p調用printf來打印指針的(值)。
“ * ...但是,因為該規則存在,所以甚至更少的奇異C實現也可以利用它進行優化,因此C編譯器可以實現free(x); printf(“%p”,(void *)x);等價於free(x); printf(“%p”,(void )NULL);,例如。... “
即使這樣做(編譯器),我也無法輕易看到這將優化什么,即調用特殊的最小化printf並將指針的值設置為0? 還考慮類似的東西:
extern void my_free(void * ptr); //在某處定義。.my_free(myptr); printf(“%p”,myptr);
正如您在此處建議的那樣,它沒有什么要優化的,並且它(編譯器)無法推斷出myptr的值 。
因此,不,我不同意您在現階段對C標准的解釋。
(我)編輯:
除非使用內存指針作為結構實現某些“奇異”實現,並且將其強制轉換為“用戶”的void * ..否則,在嘗試打印用戶時,用戶的堆值將為無效或null。那么每個接受void *作為指向內存的C調用都需要區分哪些void *指向沒有指向的堆-例如memcpy,memset,-然后這變得非常繁忙...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.