如何在C语言中使用printf打印内存地址

67

我的代码是:

#include <stdio.h>
#include <string.h>

void main()
    {
    char string[10];
    int A = -73;
    unsigned int B = 31337;

    strcpy(string, "sample");

    // printing with different formats
    printf("[A] Dec: %d, Hex: %x, Unsigned: %u\n", A,A,A);
    printf("[B] Dec: %d, Hex: %x, Unsigned: %u\n", B,B,B);
    printf("[field width on B] 3: '%3u', 10: '%10u', '%08u'\n", B,B,B);

    // Example of unary address operator (dereferencing) and a %x
    // format string 
    printf("variable A is at address: %08x\n", &A);
我正在使用Linux Mint终端编译,但当我尝试使用gcc编译时,出现以下错误信息:
basicStringFormatting.c: In function ‘main’:
basicStringFormatting.c:18:2: warning: format ‘%x’ expects argument
of type ‘unsigned int’, but argument 2 has type ‘int *’ [-Wformat=]
printf("variable A is at address: %08x\n", &A);

我所尝试做的就是打印变量A在内存中的地址。


19
void main()应该改成int main(void)。如果你的书、教程或者导师告诉你使用void main(),那么找一个更好的。 - Keith Thompson
2
@KeithThompson 这有什么关系吗? - Prakhar Agrawal
5
这很重要,因为1989年的 ANSI C 标准引入了 void 关键字,并规定主函数可以定义为 int main(void) { /* ... */ }。它没有提到 void main(),符合标准的实现可能会拒绝它。有许多书籍和教程建议使用 void main();这强烈表明作者对语言不是很熟悉。(这是针对托管实现的情况。对于自由实现,程序入口点是实现定义的,程序不在操作系统下运行。) - Keith Thompson
3
请参考PrakharAgrawal所说的comp.lang.c FAQ,从11.12a问题开始阅读。 - Keith Thompson
显示剩余2条评论
2个回答

129
使用格式说明符%p
printf("variable A is at address: %p\n", (void*)&A);

标准要求%p占位符的参数类型为void*。由于printf是一个变参函数,在C语言中,对于非变参函数,在T *void *之间会有隐式转换,但对于printf这种变参函数,没有这种隐式转换。因此需要进行强制类型转换。引用标准:7.21.6 格式化输入/输出函数(C11草案):“p”占位符的参数应该是一个指向void的指针。指针的值按照实现定义的方式转换成一系列打印字符。
而你使用的是%x,它期望的参数类型是unsigned int,而&A的类型是int *。你可以在手册中了解有关printf格式说明符的信息。在printf中格式说明符不匹配会导致未定义行为

12
重要的是将其转换为 void * - Iharob Al Asimi
2
我在一个小应用程序中测试了一下,打印变量的地址,使用强制转换和不使用强制转换得到了相同的结果。但是很明显,标准的声明(_p参数必须是指向void的指针_)留给解释的余地很少。谢谢。 - ryyker
所有指针都可以转换为 void * 吗?我认为答案是肯定的,除了函数指针可能不行。 - chux - Reinstate Monica
@Blue Moon 这确实存在一个问题,即如何打印函数指针(OP的潜在内存地址)。将其转换为 uintmax_t 并希望一切顺利? - chux - Reinstate Monica
1
@chux 没有标准的方法来打印函数指针的地址。将其转换为 void * 并使用 %p 打印或者像你说的那样转换为 uintmax_t 并打印可能在实践中有效。但是没有这样的保证它会工作(也不符合标准)。标准将函数指针的表示留给了实现定义,并且不能保证函数指针值是某种整数类型。因此,将其转换为任何整数类型可能无法正常工作。 - P.P
显示剩余3条评论

-2
一种使用带长度修饰符的 %x 以打印 intunsigned int 的方法是使用 malloc 来避免编译器抱怨类型转换的问题:
unsigned int* D = malloc(sizeof(unsigned int)); // Allocates D
unsigned int D_address = *((unsigned int*) &D); // D address for %08x without warning
*D = 75; // D value
printf("variable D is at address: %p / 0x%08x with value: %u\n", D, D_address, *D);

或者您可以使用gcc编译器的-w标志来抑制这些强制转换警告。

编辑以适用于64位地址:

unsigned long* D = malloc(sizeof(unsigned long)); // Allocates D
unsigned long D_address = *((unsigned long*) &D); // D address for %016lx without warning
*D = ULONG_MAX; // D value
printf("variable D is at address: %p / 0x%016lx with value: %lu\n", D, D_address, *D);

5
请注意:这个假设了32位指针,不支持64位指针。 - Dan Korn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接