簡體   English   中英

打印已經從堆中重新分配的變量的地址的正確方法是什么?

[英]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_tuintptr_t 這僅僅是因為C標准沒有明確指定將任何指針轉換為uintptr_t的行為,而是指定了將指針轉換為對象的行為為void *以及將void *轉換為uintptr_t

根據C標准

轉換說明符及其含義是:

...

p

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

因此,要完全符合C標准,您需要將heapvoid *

logger("Heap at %p successfully deallocated", ( void * ) address);

您在指針上使用過free()的事實並不意味着您無法在大多數實現中打印指針的值(有關其他答案,請參閱其他答案的注釋,這可能是正確的)-這僅意味着您無法取消引用指針。

您的代碼很好。

如果您只想打印指針本身的值,則指針是否有效1都無關緊要。 畢竟,您可以使用%p毫無問題地打印NULL的值, 根據定義 ,該值是無效的指針值。

顯然,如果在釋放內存后嘗試使用*heapheap[i]做某事,則會遇到未定義的行為,並且任何事情都可能發生,但是您可以檢查或打印出所有想要的heap (或address )而無需問題。


  1. “有效”是指“指向該對象生命周期內的對象”。

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.

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