当没有空终止符时,C如何识别字符数组的结尾?

tha*_*era -4 c

由于一个C字符阵列需要一个空终止,下面的代码打印4个一个 S和一些乱码.

char y[4] = {'a', 'a', 'a', 'a'};
printf("y = %s\n", y);
Run Code Online (Sandbox Code Playgroud)

输出:

y = aaaa?
Run Code Online (Sandbox Code Playgroud)

但是,以下代码不会生成垃圾字符.

char y[4] = {'a', 'a', 'a', 'a'};
char z[4] = {'b', 'b', 'b'};

printf("y = %s\n", y); 
printf("z = %s\n", z);
Run Code Online (Sandbox Code Playgroud)

输出:

y = aaaa
z = bbb
Run Code Online (Sandbox Code Playgroud)

我知道第四个字符z是使用null终止符自动初始化的.我猜也是如此,yz在内存中彼此相邻分配.

但是,在这种情况下,C如何正确打印4 a而不是前者?它是否确定下一个字节已经分配给另一个变量,所以它应该再停止打印了?

nne*_*neo 5

printf("y = %s\n", y);在这种情况下,是未定义的行为.换句话说,你很幸运 - 也许你在击中NUL之前打印了一些不可见的字符,也许在阵列之后有一个零因为堆栈对齐,也许星星是正确的.


我通常不会详细说明"它是UB,不要碰",但我觉得很奇怪.

看,这是你的程序:

#include <stdio.h>
int main() {
    char y[4] = {'a', 'a', 'a', 'a'};
    char z[4] = {'b', 'b', 'b'};

    printf("y = %s\n", y); 
    printf("z = %s\n", z);
}
Run Code Online (Sandbox Code Playgroud)

现在我要用我的特殊编译器标志编译它:

$ cc -O3 so15727258.c -o so15727258 -fstack-protector-all -Wall
Run Code Online (Sandbox Code Playgroud)

并运行它:

$ ./so15727258
y = aaaa?>A??0F
z = bbb
Run Code Online (Sandbox Code Playgroud)

哎呀,哈哈,那是垃圾.更好的是,它是随机垃圾,因为堆栈保护器,所以它甚至(简单)不确定.活泉!


还是不相信?特殊的编译器标志对你来说太奇怪了吗?尝试

#include <stdio.h>

int bar() {
    char x[4096];
}

int foo() {
    char y[4] = {'a', 'a', 'a', 'a'};
    char z[4] = {'b', 'b', 'b'};
    printf("y = %s\n", y); 
    printf("z = %s\n", z);
}

int main() {
    bar();
    foo();
}
Run Code Online (Sandbox Code Playgroud)

编译它:

$ cc so15727258.c -o so15727258 -Wall
Run Code Online (Sandbox Code Playgroud)

并运行它:

$ ./so15727258
y = aaaa?????ic?
z = bbb
Run Code Online (Sandbox Code Playgroud)

还是总垃圾!请记住 - 这些例子都是说明性的.当然这是未定义的行为,所以你可能得到一些完全不同的结果,然后回到这里并告诉我"但是XYZ有效".这是未定义行为的定义.