关于存储持续时间的数组初始化(字符、字符串、其他)的差异

differences in array initialization(char, string, other) regarding storage duration

在此question评论中说:

char arr[10] = { 'H', 'e', 'l', 'l', 'o', '[=10=]'}; and char arr[10] = "Hello"; are strictly the same thing. – Michael Walz

这让我开始思考。

我知道 "Hello" 是字符串文字。字符串文字以静态存储持续时间存储并且是不可变的。

但如果两者真的相同,那么 char arr[10] = { 'H', 'e', 'l', 'l', 'o', '[=10=]'}; 也会创建一个类似的字符串文字。

是否 char b[10]= {72, 101, 108, 108, 111, 0}; 也创建了具有静态存储持续时间的 "string" 文字?因为理论上是一样的。

char a = 'a'; is the same thing as char a; ...; a = 'a';, so your thoughts are correct 'a' is simply written to a

有区别吗:

How/where 是否定义了差异?

编辑: 我发现我还不够清楚,我对文字的内存 usage/storage 持续时间特别感兴趣。我将按原样保留问题,但希望在此编辑中更清楚地说明问题的重点。

我相信您的问题高度依赖于实现(硬件和编译器方面)。不过,总的来说:数组是放在RAM里面的,不管是全局的还是不全局的。

I know that "Hello" is string literal. String literals are stored with static storage duraction and are immutable.

是的,这会将字符串 "Hello" 保存在 ROM(只读存储器)中。您的数组在运行时加载了文字。

But if both are are really the same then char arr[10] = { 'H', 'e', 'l', 'l', 'o', '[=12=]'}; would also create a similar string literal.

是的,但在这种情况下,单个字符放在 ROM 中。您初始化的数组在运行时加载了字符文字。

Does char b[10]= {72, 101, 108, 108, 111, 0}; also create a "string" literal with static storage duration? Because theoretically it is the same thing.

如果您使用 UTF-8,那么是的,因为 char == uint8_t 这些就是值。

Are there differences between: char a = 'a'; char a = {'a'}; How/where are the differences defined?

我相信不会。

回复编辑

你是指字符串文字的存储生命周期吗?看看 this.

因此字符串文字具有静态存储持续时间。它会在程序的整个生命周期中保留,硬编码在内存中。

I know that "Hello" is string literal. String literals are stored with static storage duraction and are immutable.

是的。但是使用 char arr[10] = "Hello";,您将字符串文字复制到数组 arr 并且不需要 "keep" 字符串文字。因此,如果一个实现选择在将字符串文字复制到 arr 之后将其完全删除,那是完全有效的。

But if both are are really the same then char arr[10] = { 'H', 'e', 'l', 'l', 'o', '[=21=]'}; would also create a similar string literal.

同样,没有需要到make/store一个字符串文字。

只有当你直接有一个指向字符串文字的指针时,它通常会存储在某个地方,例如: char *string = "Hello, world!\n";

即便如此,根据 "as-if" 规则,实现也可以选择不这样做。例如,

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

static const char *str = "Hi";
int main(void)
{
   char arr[10];
   strcpy(arr, str);
   puts(arr);
}

"Hi" 可以去掉,因为它只用于将它复制到 arr 中,不能在任何地方直接访问。所以消除字符串文字(以及 strcpy 调用)就好像你有 "char arr[10] = "Hi"; 并且不会影响 observable 行为。

基本上,只要满足与字符串文字关联的属性,C 标准就不需要将字符串文字存储在任何地方。

Are there differences between: char a = 'a'; char a = {'a'}; How/where are the differences defined?

是的。 C11, 6.7.9 说:

The initializer for a scalar shall be a single expression, optionally enclosed in braces. [..]

根据 syntax,甚至: char c = {'a',}; 也是有效且等价的(尽管我不推荐这个 :)。

I know that "Hello" is string literal. String literals are stored with static storage duraction and are immutable.

是的,但是字符串文字也是C语言中的语法项。 char arr[10] = { 'H', 'e', 'l', 'l', 'o', '[=10=]'}; 不是 字符串文字,它是一个初始化列表。然而,初始化列表确实表现得好像它具有静态存储持续时间,显式 [=11=] 之后的剩余元素被设置为零等。

初始化列表本身以某种方式存储在 ROM 内存中。如果您的变量 arr 也有静态存储持续时间,它将在 .data 段中分配并在程序启动之前从 ROM 初始化列表中初始化。如果 arr 具有自动存储持续时间(本地),则在调用包含 arr 的函数时,它会在 运行 时间内从 ROM 初始化。

存储初始化列表的 ROM 内存可能与用于字符串文字的 ROM 内存相同,也可能不同。通常有一个称为 .rodata 的段,这些东西在那里结束,但它们也可能在其他一些段中结束,例如代码段 .text

编译器喜欢将字符串文字存储在特定的内存段中,因为这意味着它们可以执行称为 "string pooling" 的优化。这意味着如果您在程序中多次使用字符串文字 "Hello" ,编译器将为它使用相同的内存位置。它可能不一定对初始化列表进行同样的优化。


关于初始化列表中的 'a'{'a'},这只是 C 语言中的语法问题。 C11 6.7.6/11:

The initializer for a scalar shall be a single expression, optionally enclosed in braces. The initial value of the object is that of the expression (after conversion); the same type constraints and conversions as for simple assignment apply,

用简单的英语来说,这意味着 "non-array" (标量)可以用大括号或不用大括号初始化,它具有相同的含义。除此之外,适用与常规分配相同的规则。

在抽象机中,char arr[10] = "Hello";表示arr是通过复制字符串文字"Hello"中的数据来初始化的,它在别处有自己的存在;而另一个版本与任何其他变量一样只有初始值——不涉及字符串文字。

但是,两个版本的可观察行为 是相同的:创建arr 时设置了指定的值。这就是代码相同的另一张贴者的意思;根据标准,如果两个程序具有相同的可观察行为,则它们是相同的。允许编译器为两个版本生成相同的程序集。


你的第二个问题与第一个问题完全不同;但是 char a = 'a';char a = {'a'}; 是相同的。可以选择将单个初始值设定项括在大括号中。