cc和c ++之间的snprintf格式“%zu”的奇怪结果

时间:2017-10-27 09:48:48

标签: printf

#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

  

两者的区别是什么??

的env

  • 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)

1 个答案:

答案 0 :(得分:2)

您使用了错误的说明符 - %zu应该使用类型为size_t的打印打印值,但您尝试打印int类型的值。从技术上讲,您的程序格式错误,其行为未定义。

现在,关于这里发生了什么,为什么你会看到这些结果。您必须了解关于System V x86_64 ABI的以下事项:

  • 类型int为4个字节,而size_t为8个字节。
  • 函数参数通过寄存器rdirsirdxrcxr8r9传递,并传递更多值在堆栈中以相反的顺序。
  • 堆栈上的参数应该对齐8个字节。

请记住,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