为什么用%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)
答案 0 :(得分:9)
这是未定义的行为,但它会在您的实现上发生:
int
值由%s
读取为空指针%s
对printf
的处理有特殊情况代码来识别空指针并打印(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++;
}
}
}
}