7

c编程书说我的字符串必须以空结尾才能使用 printf 打印,但以下程序仍然打印字符串,尽管它不是以空结尾的!

#include <stdio.h>
#include <stdlib.h>

int main(){
    int i ;
    char str[10] ;
    for(i = 0 ; i < 10 ; i++ ) {
        str[i] = (char)(i+97) ;
    }

    printf("%s",str) ;
}

我正在使用代码块 IDE。

4

5 回答 5

10

读取超出数组范围是未定义的行为,实际上很不幸没有崩溃,如果您运行它足够多的时间或在可能(或可能不会)崩溃的函数中调用它,您应该始终终止字符串,或使用宽度说明符:

printf("%.10s", str);
于 2012-12-01T04:38:58.113 回答
5

的第 10 个元素之后的内容str恰好是空值。该 null 超出了数组的定义边界,但 C 没有数组边界检查。在你的情况下,这就是它的结果,这只是运气。

于 2012-12-01T04:37:06.090 回答
1

根据 C 标准,printf 函数打印字符串中的字符,直到找到一个空字符。否则在定义的数组索引之后它会做什么没有定义。我已经测试了你的代码。并在打印“abcdefghij”后打印一些垃圾值。

于 2012-12-01T04:44:38.290 回答
1

如果您在该调用之前执行其他操作,则您的堆栈区域将包含未使用的数据之外的其他数据。设想:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int use_stack(void) {
   char str[500];
   memset(str, 'X', sizeof(str));
   printf("Filled memory from %p to %p.\n", &str, &str+sizeof str);
}

void print_stuff() {
    int i;
    char str[16]; // changed that so that 10..15 contain X
    for(i = 0; i < 10; i++) {
        str[i] = (char)(i+97);
    }

    printf("%s<END>",str); // have a line break before <END>? Then it comes from i.
    printf("&str: %p\n", &str);
    printf("&i: %p\n", &i);
    // Here you see that i follows str as &i is &str + 16 (0x10 in hex)
}

int main() {
    use_stack();
    print_stuff();
}

您的堆栈区域将充满Xes 并printf()会看到它们。

在您的情况和环境中,堆栈在程序启动时巧合地“干净”。

编辑:这可能会也可能不会发生。如果编译器将变量i立即放在数组之后,您的数据仍然会被NUL终止,因为第一个字节是i(您碰巧打印的值 - 在您的情况下它可能是一个 libne 中断 - 而第二个字节是一个NUL字节。即使是这种情况,您的代码也会调用 UB(未定义的行为)。

hexdump如果你的输出包含一个0A字符,你能看看(通过管道程序输出或类似的方法)吗?如果是这样,我的猜测是正确的。我刚刚测试了它,在我的编译器(gcc)上似乎是这样。

如前所述,您不应该依赖任何东西。

EDIT2:如果您之前看到换行符<END>,我的猜测是正确的。如果您查看现在正在打印的指针,您可以比较它们在内存中的地址。

于 2012-12-01T05:57:40.990 回答
0

因为在调试模式下,*(str+10)整个未使用的空间都有一个初始化值'0',所以看起来它是 0 终止的。

bash-3.2$ clang -O0 t.c -o t #compile in debug mode
bash-3.2$ ./t
abcdefghij
bash-3.2$ clang -O2 t.c -o t #compile with optimization
bash-3.2$ ./t
abcdefghij2÷d=
于 2012-12-01T04:37:23.240 回答