什么解释了 C 编译器 w.r.t 字符串初始化的这种行为?

What explains this behavior of C compiler w.r.t string initialization?

以下代码不生成空终止符

/* Case 1 */
#include <stdio.h>
void main () {
    char wbuf[16] = "0123456789abcdef";
    printf("%s\n", wbuf);
}

查看拆机相关部分

0x4005b4 <main+23>:  movabs [=12=]x3736353433323130,%rax
0x4005be <main+33>:  mov    %rax,-0x20(%rbp)
0x4005c2 <main+37>:  movabs [=12=]x6665646362613938,%rax
0x4005cc <main+47>:  mov    %rax,-0x18(%rbp)
0x4005d0 <main+51>:  lea    -0x20(%rbp),%rax --->prinft related
0x4005d4 <main+55>:  mov    %rax,%rdi
0x4005d7 <main+58>:  callq  0x400470 <puts@plt>

而以下代码生成一个:

/* Case 2 */
#include <stdio.h>
void main () {
    char wbuf[17] = "0123456789abcdef";
    printf("%s\n", wbuf);
}

再次查看拆机的相关部分

0x4005b4 <main+23>:  movabs [=14=]x3736353433323130,%rax
0x4005be <main+33>:  mov    %rax,-0x20(%rbp)
0x4005c2 <main+37>:  movabs [=14=]x6665646362613938,%rax
0x4005cc <main+47>:  mov    %rax,-0x18(%rbp)
0x4005d0 <main+51>:  movb   [=14=]x0,-0x10(%rbp) >>>>>> Null terminator comes here
0x4005d4 <main+55>:  lea    -0x20(%rbp),%rax  --->prinft related
0x4005d8 <main+59>:  mov    %rax,%rdi
0x4005db <main+62>:  callq  0x400470 <puts@plt>

我会假设一个像 char wbuf[xxx] = "yyyy" 这样的字符串初始化来添加一个空终止符,就像在第二种情况下一样。然而,以下问题仍然存在:

  1. 为什么编译器在第一种情况下不警告 "too long initialization" 而不是默默地避免空终止符。多一个字符,如 char wbuf[16] = "0123456789abcdef[=16=]" 会触发 警告:字符数组的初始化字符串太长 .
  2. 这种行为会在编译器之间发生变化吗?我正在使用 gcc ubuntu 并使用 O0 选项编译
  3. 为什么它在 1 和 2 中表现不一致,例如它可能有 在 case1 中盲目地写了 16 并添加了 Null 终止符 ?

`

来自 C11 standard 关于初始化的第 6.7.9 节(强调我的):

An array of character type may be initialized by a character string literal or UTF−8 string literal, optionally enclosed in braces. Successive bytes of the string literal (including the terminating null character if there is room or if the array is of unknown size) initialize the elements of the array.

所以编译器完全符合规范。

在 C 语言中 char 带有字符串文字初始值设定项的数组可用于定义常规的零终止字符串以及所谓的 固定宽度字符串 (请参阅difference fixed width strings and zero-terminated strings)。固定宽度字符串的想法在很大程度上被遗忘了,但它在 C 语言和 Unix 的早期发挥了作用 OS.

固定宽度字符串约定允许 8 char 的数组包含长度为 8 的固定宽度字符串(没有零终止符)

char fws_hello[8] = "Hello!!!";
/* `fws_hello` is a valid fixed-width string for width 8 */

最有可能的是,在支持此类字符串的语言中允许将终止零设为 "fall off the end of the array"。

另一种语言特性——全有或全无的初始化方法——也可以看作是满足固定宽度字符串的需要

char fws_hi[8] = "Hi";
/* `fws_hi` is padded with zeros all the way to the very end of 
    the array, which makes it a valid fixed-width string for width 8 */

我猜 C++ 不再需要这种字符串格式,这使得它能够收紧其初始化规则并防止终止零丢失。