[英]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
看起來兩個指針tmp
和s
駐留在同一地址,但指向不同的事物。 為什么?
#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)); \
}
兩個指針tmp
和s
共享同一位置; 但是,他們在不同的時間這樣做。
上面的原因是此聲明:
__typeof__(p) p1 = p;
它將p
的副本復制到p1
, p1
是封閉代碼塊局部的變量。 DebugPointer
宏被擴展了兩次,每次在臨時作用域內創建一個稱為p1
的新局部變量。
這兩個變量編譯器會p1
從來沒有在同一時間同時存在,所以它重用的第一個實例的空間p1
存儲的二審值p1
。
注意:現在您可能想知道為什么首先需要p1
。 這樣做的原因是您要為其應用地址&
運算符。 您可以改寫&(p)
,但是這樣的宏將是脆弱的:它可以在您的示例( demo )中運行,但是調用DebugPointer(tmp+1)
會導致編譯錯誤。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.