簡體   English   中英

兩個指針位於同一地址但指向不同的事物

[英]Two pointers residing at the same address but pointing at different things

這是代碼:

#define _GNU_SOURCE
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define DebugPointer(p)                                         \
    {                                                           \
        __typeof__(p) p1 = p;                                   \
        printf("%8s %20s: %p\n", #p, "address", (void*)&(p1));  \
        printf("%8s %20s: %p\n", #p, "points to", (void*)(p1)); \
    }


// memory-safe asprintf, implemented as a macro
#define Sasprintf(write_to, ...)            \
    {                                       \
        char* tmp = (write_to);             \
        printf("==== before asprintf\n");   \
        DebugPointer(tmp);                  \
        DebugPointer(write_to);             \
        asprintf(&(write_to), __VA_ARGS__); \
        printf("==== after  asprintf\n");   \
        DebugPointer(tmp);                  \
        DebugPointer(write_to);             \
        free(tmp);                          \
    }

int main() {
    char* s = NULL;
    int n = 0;
    Sasprintf(s, "%s", "aa\n");
    Sasprintf(s, "%s%s", s, "bb\n");
    Sasprintf(s, "%s%s", s, "cc\n");
    printf("string: %s\n", s);
    free(s);
}

Sasprintf宏摘自21世紀的C書。 我試圖了解它是如何工作的。 結果看起來有些奇怪:

==== before asprintf
     tmp              address: 0x7ffe8b767cd8
     tmp            points to: (nil)
       s              address: 0x7ffe8b767cd8
       s            points to: (nil)
==== after  asprintf
     tmp              address: 0x7ffe8b767cd8
     tmp            points to: (nil)
       s              address: 0x7ffe8b767cd8
       s            points to: 0x560969cae6e0
==== before asprintf
     tmp              address: 0x7ffe8b767cd8
     tmp            points to: 0x560969cae6e0
       s              address: 0x7ffe8b767cd8
       s            points to: 0x560969cae6e0
==== after  asprintf
     tmp              address: 0x7ffe8b767cd8
     tmp            points to: 0x560969cae6e0
       s              address: 0x7ffe8b767cd8
       s            points to: 0x560969cae700
==== before asprintf
     tmp              address: 0x7ffe8b767cd8
     tmp            points to: 0x560969cae700
       s              address: 0x7ffe8b767cd8
       s            points to: 0x560969cae700
==== after  asprintf
     tmp              address: 0x7ffe8b767cd8
     tmp            points to: 0x560969cae700
       s              address: 0x7ffe8b767cd8
       s            points to: 0x560969cae6e0
string: aa
bb
cc

看起來兩個指針tmps駐留在同一地址,但指向不同的事物。 為什么?

#define DebugPointer(p)                                         \
    {                                                           \
        __typeof__(p) p1 = p;                                   \
        printf("%8s %20s: %p\n", #p, "address", (void*)&(p1));  \
        printf("%8s %20s: %p\n", #p, "points to", (void*)(p1)); \
    }

因此,您正在打印p1的地址,該地址對於DebugPointer每次調用都是本地的,因此每次調用該地址時,該地址DebugPointer相同。

p1的目的似乎是為了避免對宏參數p多次求值。 為了實現您的打印目標,您可以更改宏以使地址居前。

#define DebugPointer(p)                                         \
    do {                                                        \
        __typeof__(&(p)) p1 = &(p);                             \
        void *p2 = *p1;                                         \
        printf("%8s %20s: %p\n", #p, "address", (void*)p1);     \
        printf("%8s %20s: %p\n", #p, "points to", p2);          \
    } while(0)

這是我在網上找到的Sasprintf定義:

#define Sasprintf(write_to,  ...) {           \
    char *tmp_string_for_extend = (write_to); \
    asprintf(&(write_to), __VA_ARGS__);       \
    free(tmp_string_for_extend);              \
}

它使用臨時存儲原始指針,該指針應該由上一次對Sasprintf調用分配(或初始化為NULL )。 asprintf調用可能會更改其指向的位置。 現在,宏可以在原始指針上free調用。

為了避免對write_to參數進行多次求值,可以預先獲取其地址。

#define Sasprintf(write_to,  ...) do { \
    char **dest = &(write_to);         \
    free(*dest);                       \
    asprintf(dest, __VA_ARGS__);       \
} while(0)

問題出在您的打印例程中:

#define DebugPointer(p)                                         \
    {                                                           \
        __typeof__(p) p1 = p;                                   \
        printf("%8s %20s: %p\n", #p, "address", (void*)&(p1));  \
        printf("%8s %20s: %p\n", #p, "points to", (void*)(p1)); \
    }

你宣布一個新的變量名為p1並打印變量的地址。 在這種情況下,可以算出該變量每次進入作用域時都具有相同的地址。

擺脫多余的變量以顯示您感興趣的實際變量的地址。

#define DebugPointer(p)                                        \
    {                                                          \
        printf("%8s %20s: %p\n", #p, "address", (void*)&(p));  \
        printf("%8s %20s: %p\n", #p, "points to", (void*)(p)); \
    }

兩個指針tmps共享同一位置; 但是,他們在不同的時間這樣做。

上面的原因是此聲明:

__typeof__(p) p1 = p;

它將p的副本復制到p1p1是封閉代碼塊局部的變量。 DebugPointer宏被擴展了兩次,每次在臨時作用域內創建一個稱為p1的新局部變量。

這兩個變量編譯器會p1從來沒有在同一時間同時存在,所以它重用的第一個實例的空間p1存儲的二審值p1

注意:現在您可能想知道為什么首先需要p1 這樣做的原因是您要為其應用地址&運算符。 您可以改寫&(p) ,但是這樣的宏將是脆弱的:它可以在您的示例( demo )中運行,但是調用DebugPointer(tmp+1)會導致編譯錯誤。

暫無
暫無

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

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