#include <stdio.h>
#include <stdlib.h>
int main()
{
char buf[100];
snprintf(buf, sizeof(buf), "C %s %s %u %zu", "aaa","bbb",0,0);
printf("%s\n", buf);
}
cc -o test test.c
C aa bbb 0 140733193388032
c ++ -o test test.c
C aa bbb 0 0
两者的区别是什么??
Linux xxx 3.10.0-514.26.1.el7.x86_64#1 SMP Thu Jun 29 16:05:25 UTC 2017 x86_64 x86_64 x86_64 GNU / Linux
cc(GCC)4.8.5 20160623(Red Hat 4.8.5-11)
c ++(GCC)4.8.5 20160623(Red Hat 4.8.5-11)
答案 0 :(得分:2)
您使用了错误的说明符 - %zu
应该使用类型为size_t
的打印打印值,但您尝试打印int
类型的值。从技术上讲,您的程序格式错误,其行为未定义。
现在,关于这里发生了什么,为什么你会看到这些结果。您必须了解关于System V x86_64 ABI的以下事项:
int
为4个字节,而size_t
为8个字节。rdi
,rsi
,rdx
,rcx
,r8
,r9
传递,并传递更多值在堆栈中以相反的顺序。请记住,140733193388032是0x7fff00000000(低4个字节是0)。
在您的情况下由编译器生成的代码是:
mov DWORD PTR [rsp], 0 ; <-- seventh arg passed on stack
mov r9d, 0 ; <-- sixth arg
mov r8d, OFFSET FLAT:.LC0 ; <-- fifth arg
mov ecx, OFFSET FLAT:.LC1 ; <-- fourth arg
mov edx, OFFSET FLAT:.LC2 ; <-- third arg
mov esi, 100 ; <-- second arg
mov rdi, rax ; <-- first arg
mov eax, 0
call snprintf
注意如何使用4字节mov
指令写入最后一个参数。这意味着高4字节未初始化并包含一些垃圾值。但是因为您使用指定的%zu
来打印它,snprintf
占用8个字节,在较低的4个字节中打印零,从较高的4个字节打印垃圾。这也意味着只有第7个和更多参数才会注意到这种行为。
为什么gcc和gc ++的输出不同?因为不同的运行时在启动时执行不同的代码。使用C运行时,它只是随机地在此内存位置的高字节处有零,而C ++运行时在那里存储了一些非零值。
要避免此类错误,请使用-Wformat
参数或使用-Wall -Wextra
编译代码。这会给你以下警告:
1.c: In function ‘main’:
1.c:7:47: warning: format ‘%zu’ expects argument of type ‘size_t’, but argument 7 has type ‘int’ [-Wformat=]
snprintf(buf, sizeof(buf), "C %s %s %u %zu", "aaa","bbb",0,0);
~~^
%u