使用较小的字符串文字初始化字符数组

12
如果我写下以下代码:

If I write:

char arr[8] = "abc";

arr[4]可能有什么规范吗?我用Clang做了一些测试,似乎数组中剩余的字符被设为null。此外,char arr[8] = "";会将每个字节都归零。不确定这是否是编译器方便、标准行为、纯属巧合还是我的理解有误。

void a()
{
    char arr[8] = "abc";    /* breakpoint here, line 3 */
    strcpy(arr, "1234567");
}
int main()
{
    a();
    a();
    return 0;
}

调试器记录:

断点1,在str.c的第3行执行函数a()
3           char arr[8] = "abc";
(gdb) s
当前语言为:auto,目前为 minimal 模式。
4           strcpy(arr, "1234567");
(gdb) p arr
$1 = "abc\000\000\000\000"
(gdb) c      
继续执行。
断点1,在str.c的第3行执行函数a() 3 char arr[8] = "abc"; (gdb) p arr $2 = "1234567" (gdb) s 4 strcpy(arr, "1234567"); (gdb) p arr $3 = "abc\000\000\000\000"

@Lundin - 实际上,arr[3] 被设置为 null-termination,所以他问 arr[4] 是正确的。 - James Caccese
@JamesCaccese 似乎有一个打字错误...不管怎样,我在我发布的答案中已经解释了一切。 - Lundin
@Lundin - 是谁打错字了,是你还是@sidyll?你上面的评论说arr[4]被设置为null终止符是错误的。arr[3]是因为""而被设置为null终止符,arr[4]因为没有显式初始化而被设置为零。你下面的回答是正确的,但是你的评论有一个偏差,会让人感到困惑。由于你无法编辑评论,所以最好删除它。 - James Caccese
4个回答

16

这是标准行为。

arr[3] 被初始化为0,因为终止符0是字符串字面量的一部分。

其余元素也被初始化为0 -- 参考 ISO/IEC 9899:1999, 6.7.8, 21:

如果大括号括起来的初始化列表中元素或成员少于聚合体的元素或成员数量,或者用于初始化已知大小数组的字符串字面量中字符数比数组元素数量少,则聚合体的其余部分应隐式地按具有静态存储期的对象方式初始化。

而具有静态存储的 char 对象会被初始化为0。


感谢提供标准参考。 - sidyll
但是这仅适用于大括号包含的列表,而被质疑的代码并没有使用这种形式,不是吗? - kusma
1
不,字符串字面值中的字符数不能更少。 - undur_gongor

8
char arr[8] = "abc";

是完全等价于

char arr[8] = {'a', 'b', 'c', '\0'};

ISO C 6.7.8 §21规定:
如果大括号内的初始化列表中的元素或成员少于聚合体中的元素或成员,或者用于初始化已知大小的数组的字符串文字中的字符数少于数组中的元素数,则聚合体的其余部分应隐式初始化为具有静态存储期的对象的值。
简单来说,这意味着你数组末尾的所有值都将被设置为0。因此,标准保证您的代码等价于以下内容:
char arr[8] = {'a', 'b', 'c', '\0', 0, 0, 0, 0};

当然,'\0'也是零值。

这个规则适用于所有的数组,而不仅仅是字符串。同样,当初始化结构体时只显式设置其中几个成员(6.7.8 §18)时也适用。


这就是为什么你可以写出这样的代码:

char arr[8] = "";

在这个例子中,数组的第一个元素被显式地初始化为 '\0',而其他的元素则隐式地被初始化为零。编译器将其转换为:
char arr[8] = {0, 0, 0, 0, 0, 0, 0, 0};

“具有静态存储期的对象”是什么意思?我无法理解规范中的这一部分。您能否请解释一下? - Константин Ван
2
@КонстантинВан 为了后世留存:它指的是全局变量或那些被定义为静态的对象(无论是文件静态还是函数静态)。这些对象在程序启动之前就已经被创建;启动代码包含命令来“清零”这些对象。这意味着所有指针都是空指针,所有数字都是零,所有字符数组都有零字节,因此strlen(arr)返回0。实际上这是可取的;之所以不对具有“自动存储期限”的对象(局部变量)执行此操作,是因为它会发生每次代码被传递时,而不是只发生一次静态变量。 - Peter - Reinstate Monica

3
这是标准行为。如果在声明中初始化了数组的前缀,那么未明确初始化的数组的每个元素都会被初始化为默认值(对于char,默认值为'\0')。其他类型也适用此规则。
int a[10] = {1};

a[1]a[9]全部清零。


0

根据标准,所有超出规定范围的索引都将被设置为零/空。更多信息请参见此SO帖子


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接