C:为什么用%s打印一个null char打印“(null)”?

时间:2012-12-03 10:31:12

标签: c gcc

为什么用%s打印空字符('\ 0',0)实际打印“(null)”字符串?

喜欢这段代码:

char null_byte = '\0';
printf("null_byte: %s\n", null_byte);

...打印:

null_byte: (null)

...它甚至在Valgrind下运行没有错误,我得到的是编译器警告warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat](注意:我在32位Ubuntu上使用gcc 4.6.3)

6 个答案:

答案 0 :(得分:9)

这是未定义的行为,但它会在您的实现上发生:

  • 您传递的int值由%s读取为空指针
  • %sprintf的处理有特殊情况代码来识别空指针并打印(null)

标准不要求这些。需要[*]的部分是varargs中使用的char作为int传递。

[*]嗯,这是必需的,因为在您的实施中,char的所有值都可以表示为int。如果您使用的是char未签名且宽度与int相同的有趣实现,则会将其作为unsigned int传递。我认为有趣的实现符合标准。

答案 1 :(得分:4)

嗯,对于初学者来说,你做错了。 '\0'是一个字符,应使用%c而不是%s打印。我不知道这是否是为了实验目的。

\0的实际二进制值是,0,你试图将值0转换为char *指针,这将导致无效的引用和崩溃。您的编译器通过对%s值的特殊处理来阻止它。

Valgrind不会捕获它,因为它运行在生成的二进制文件上,而不是源代码(您需要一个静态分析器)。由于编译器已将该调用转换为安全的“空指针”文本,因此valgrind不会看到任何错误。

答案 2 :(得分:1)

null_byte包含0.当您在printf中使用%s时,您正在尝试打印字符串,这是char(char *)的地址。你在代码中做的是,你将地址0(NULL)作为字符串的地址传递,这就是输出为空的原因。 编译器警告您,您将错误的类型传递给%s修饰符。尝试printf(“null_byte:%s \ n”,& null_byte);

答案 3 :(得分:0)

您的printf语句正在尝试打印字符串,因此将值null_bye解释为值为null的char *。注意警告。要么这样做

printf("null_byte: %s\n", &null_byte);

或者

printf("null_byte: %c\n", null_byte);

答案 4 :(得分:0)

由于printf是可变参数,因此通常会在null_byte上执行参数提升,因此会将其提升(转换)为int,值为0

printf然后读取char *指针,0 int被解释为空指针。您的C标准库具有将空字符串打印为(null)的功能。

答案 5 :(得分:0)

在XV6中为其他答案添加了一个实现示例,这是对Unix v6的有益的重新实现,如果将零值传递给%s,它将显示(null)

void printf(int fd, const char *fmt, ...) {
    uint *ap = (uint*)(void*)&fmt + 1;
    ...
    if(state == '%') {
        ...
        if(c == 's') {
            s = (char*)*ap;
            ap++;
            if(s == 0)
                s = "(null)";
            while(*s != 0){
                putc(fd, *s);
                s++;
            }
        }
    }
}