在printf中输出变量地址

时间:2018-01-16 20:56:00

标签: c pointers

如果我在Visual C ++ 2017中构建并运行以下程序:

31519768560270096 7338768 000F1055
31519785740139284 7338772 000F1055
31519802920008472 7338776 000F1055

我看到输出如下:

%llu

我无法联系。

为什么sizeof(int)的输出如此不同?我平台上的%p是4。

为什么%u的输出全部相同?它们是不同变量的地址。

只有%llu的输出似乎是一致的 - 连续的3个数组元素的内存位置,每个元素有4个字节。但这些输出既不匹配%p也不匹配public class DatabaseLogger { readonly static log4net.ILog logger = log4net.LogManager.GetLogger("ADONetAppender"); public static void LogDebug(string message) { logger.Debug(message); } }

4 个答案:

答案 0 :(得分:5)

您的代码使用错误的格式说明符导致undefined behaviour。结果毫无意义。

由程序员为所提供的参数使用正确的格式说明符,在C中没有中间的“编译器魔术”。如果你不遵守规则那么你的程序不再被任何说规则和任何事情都可能发生,在你的情况下,它表现为意想不到的输出。

答案 1 :(得分:4)

Oups!你试图用%llu打印不长的东西。这足以调用Undefined Behavior,您不能再期望程序提供正确的输出。

在幕后,常见的编译器实现了堆栈上的推送参数,printf函数从那里获取它们。在32位架构上,指针只使用4个字节,因此您的调用实际上将12个字节压入堆栈。

但是%llu使用8个字节, 2个传递的参数。因此%u正确显示最后一个,%p打印堆栈中的后续内容。如果将long long unsigned转换为十六进制,则开始更加明显:31519768560270096是0x006FFB10006FFB10,实际上是具有0x6FFB10或十进制的2个初始参数7338768

TL / DR:除了你真的知道原因之外,永远不要使用%p以外的任何东西来打印地址(即使在这种情况下,也要试着克制......)

答案 2 :(得分:3)

让我们以十六进制转储第一个参数:

>>> hex(31519768560270096 )
'0x6ffb10006ffb10'  <=== twice the same 0x6ffb10 32-bit value
>>> hex(31519785740139284 )
'0x6ffb14006ffb14'  <=== same here, increased by 4

如你所见,使用第一个参数错误的格式说明符使得printf从变量参数区读取太多,所以第二个参数实际上是第三个参数(它们具有相同的值),但最后最后一个参数(指针)读取外部变量参数区域。这就是为什么它没有变化

无论如何,这只是为了解释你的特定编译器实现的幕后情况,但不要永远,因为它是未定义的行为。

答案 3 :(得分:2)

为每个printf说明符使用匹配类型。由于OP的代码不这样做,结果是未定义的行为(UB)。解释UB通常不如学习如何避免它那么有价值。

@jxh建议合理,使用更好的编译器或选项。

printf("%llu %u %p\n",
   // a + i, a + i, a + i);
   (unsigned long long) (a + i), (unsigned) (a + i), (void*) (a + i));

这可能无法为3 (a+i)打印相同的文字。我希望%u是一个截断值,但至少这些不是UB,只是实现定义的行为

在打印对象指针的值时,使用最后一个表单获得最佳结果。

printf("%p\n", (void*) (a + i));