【问题标题】:differences in array initialization(char, string, other) regarding storage duration关于存储持续时间的数组初始化(字符、字符串、其他)的差异
【发布时间】:2018-09-22 06:31:51
【问题描述】:

在这个question 中,cmets 中说:

char arr[10] = { 'H', 'e', 'l', 'l', 'o', '\0'};char arr[10] = "Hello"; 是完全一样的。 ——迈克尔·沃尔兹

这让我开始思考。

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

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

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

char a = 'a';char a; ...; a = 'a';是一回事,所以你的想法是正确的'a'只是写给a

之间有区别吗:

  • char a = 'a';
  • char a = {'a'};

如何/在哪里定义差异?

编辑: 我发现我没有说得足够清楚,以至于我对文字的内存使用/存储持续时间特别感兴趣。我将保持原样保留问题,但希望在此编辑中更清楚地说明问题的重点。

【问题讨论】:

  • 关于b的初始化,如果系统使用ASCII,那么是的,也是一样的。
  • @Someprogrammerdude 这是否意味着任何数组初始值设定项都将以静态持续时间存储?
  • @KamiKaze 之间有区别吗:char a = 'a';字符 a = {'a'};差异是如何/在哪里定义的? 实际上你不需要关心。在初始化期间,数组初始化器的内容被复制到数组中,无论如何都无法访问数组初始化器的原始数据。
  • @MichaelWalz 我很在意,因为我想了解它的工作原理以及内存使用情况(考虑到这并不是真正的问题)。
  • @KamiKaze this 可能适合你。

标签: c arrays string-literals


【解决方案1】:

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

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

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

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

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


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

标量的初始值设定项应该是一个表达式,可选地用大括号括起来。这 对象的初始值是表达式的初始值(转换后);同类型 约束和转换适用于简单赋值,

简单来说,这意味着“非数组”(标量)可以用大括号或不带大括号进行初始化,其含义相同。除此之外,适用与常规分配相同的规则。

【讨论】:

  • 学究气;初始化列表的行为不像任何东西。您的意思是说从列表初始化自动数组的行为与从列表初始化静态数组的行为相同。我这样说是因为人们有时会误解,花括号列表实际上是一个对象,语法是从列表所代表的这个对象初始化变量
  • @M.M 在整个第 6.7.9 章中为初始化列表指定了大量的行为。人们并没有错误地认为大括号列表实际上是一个对象。虽然从 C 语言的角度来看它不是一个对象,但它在所有真正的 ROM 系统上分配了存储空间和它自己的地址的初始化列表。为方便起见,初始化器列表存储在与未初始化的对象相同的类型中,否则编写初始化.data 的“CRT”会很痛苦。尽管在基于 RAM 的类似 PC 的计算机上,这部分不是必需的。
【解决方案2】:

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

是的。但是使用char arr[10] = "Hello";,您正在将字符串文字复制到数组arr,并且无需“保留”字符串文字。因此,如果实现选择在将字符串文字复制到 arr 后完全删除它,那是完全有效的。

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

再一次,没有需要为此制作/存储字符串文字。

只有当你直接有一个指向字符串文字的指针时,它通常会存储在某个地方,例如: 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 标准并不要求必须将字符串文字存储在任何地方,只要满足与字符串文字关联的属性即可。

之间有区别吗:char a = 'a';字符 a = {'a'};如何/在哪里定义差异?

是的。 C11, 6.7.9 说:

标量的初始值设定项应该是一个表达式,可选地用大括号括起来。 [..]

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

【讨论】:

  • 所以字符串文字可能不会为程序的运行时分配内存?
  • 它可能会或可能不会被分配 - 没有一般的答案(取决于实现,是否实际上可以消除有问题的字符串文字,优化级别等)。特别是对于问题中数组复制的情况,gcc(7.2)在使用-O3编译时会愉快地消除它。
  • “因此,如果一个实现选择在将字符串复制到 arr 后完全删除它,那是完全有效的。”程序到底怎么可能在执行期间删除它自己的 ROM 的一部分?不存在这样的程序。更有可能的是,在基于 RAM 的系统上,字符串文字在启动时被复制到变量中,但变量本身需要具有静态存储持续时间。只有当编译器可以推断出字符串文字在程序的其他地方没有再次使用时,才会发生这种情况。
  • 还要挑剔您的示例是不正确的,因为静态存储文件范围变量用零填充剩余字节。局部变量上的 strcpy() 不会这样做。因此编译器可能无法优化代码——它必须知道程序永远不会使用尾随零。这反过来意味着 puts 必须与 main 位于同一翻译单元中(它不是),否则编译器将无法判断 puts 的作用。
  • 在这样的系统上,“实现”会知道不可能这样做。 “但是变量本身需要具有静态存储持续时间”在您从我的回答中引用的特定句子中,它指的是arr,并且不需要在那里保留“变量”。
【解决方案3】:

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

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


您的第二个问题与第一个问题完全不同;但是char a = 'a';char a = {'a'}; 是相同的。单个初始化器可以选择用大括号括起来。

【讨论】:

  • 这在功能上是不可观察的,这就是标准所说的。但这会对内存使用产生影响(我对此很好奇)。 Sidequestion:我从这个问题中得到了一些非常有趣的答案。但是没有人投票,我不想在这里强制投票,但是这个问题是否缺少任何东西,或者仅仅是没有反对票就足以成为“赞美”(至少在 [c] 标签中)。
  • @KamiKaze 内存使用不是可观察到的行为(根据标准)。如果编译器认为没有它也可以,实际上不必有任何与字符串文字或其他任何东西相对应的内存区域
【解决方案4】:

我相信您的问题与实现高度相关(硬件和编译器方面)。但是,一般来说:数组是放在 RAM 中的,不管是全局的还是不全局的。

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

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

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

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

char b[10]= {72, 101, 108, 108, 111, 0};还创建一个具有静态存储持续时间的“字符串”文字?因为理论上是一样的。

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

之间是否有区别: 字符 a = 'a'; 字符 a = {'a'}; 如何/在哪里定义差异?

我不相信。

回复编辑

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

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

【讨论】:

  • 请看我的编辑。我想我的意图还不够清楚,也许你想相应地改变你的答案
  • @KamiKaze 我的编辑是否正确回答了您的问题?
  • 更多的是关于不同初始化的差异。
  • @KamiKaze 我认为不同之处在于,当你写“Hello”时,文字是作为一个整体存储的。当您编写字符或数字时,文字会单独存储,然后加载到数组中。否则,它们都具有静态存储持续时间。当然,数组在 RAM 中并且可以修改..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-11
  • 1970-01-01
  • 2010-12-14
  • 2018-06-23
  • 1970-01-01
  • 2015-01-31
相关资源
最近更新 更多