填充数组的机制

The mechanics of populating an array

我尝试填充定义如下的结构数组:

typedef struct{
    char directive[5];
}directive_nfo_t;

通过使用以下内容:

directive_nfo_t directive_list[]=
{
   {"ALIGN"},{"ASCII"},{"BSS"},{"BYTE"},{"END"},{"EQU"},{"ORG"}
};

令我惊讶的是,前几个元素损坏如下:

[0]= ALIGNASCIIBSS
[1]= ASCIIBSS
[2]= BSS
...

直到我做了以下更改:

typedef struct{
    char directive[6];  <-- made char array +1
}directive_nfo_t;

然后前几个数组是正确的:

[0]= ALIGN
[1]= ASCII
[2]= BSS
...

我的问题是在后台发生了什么来解释这种行为? 问候。

在 C 中,字符串 是一个字符值序列,后跟一个 0 值终止符; string "ASCII" 由字符序列 'A''S''C''I'、[=14 表示=]、0。因此,您需要一个 6 元素数组来存储字符串。

对于 N 个字符长的字符串,您需要 N+1 个字符的数组来存储它。

如果 char 数组足够大,C 编译器会自动在文本后放置一个 '[=11=]'

如果刚好 足以容纳文本,则省略终止符,这就是此处发生的情况。

如果文本甚至没有空间,编译器会说 "too many initialisers" 或 "array bounds overflow"。

结构数组元素在内存中相邻。前两项缺少终止符,因此打印的第二项仅在第三项之后的终止符处停止。第一项,也被打印直到到达相同的终止符。通过使数组大小为 6,编译器能够在每个项目之后放置一个终止符。

当您按照您的方式将 char 数组显式初始化为字符串文字时:

char some_array[] = {"ALIGN"};

编译器实际上用引号内的字符填充了0th4th"position"(共5个位置) ,还有 第五位置 [=13=],而不需要你明确地这样做(如果它有足够的 space)。因此大小等于 6。如果不将 [=13=] 字符计入大小计算并将大小限制为 5,则超出边界。编译器会省略终止符。

在你的情况下,它看起来好像下一个成员的第一个元素 "overwrote" 应该是前一个 省略的 [=13=] 字符,因为你还没有为它预留位置。事实上,"mechanics of populating the array" 归结为编译器写入尽可能多的数据以适应边界。下一个成员字符串的第一个位置的地址在逻辑上对应于您的分配,尽管缺少前一个的 [=13=]

由于您的 printf() 格式标签 %s,该函数打印字符直到到达第一个 [=13=] 字符,即实际上是未定义的行为。

这就是为什么

char directive[6];

代码中的大小分配正确。

与 C++ 不同,C 允许您(无意中)搬起石头砸自己的脚,允许在 char 数组初始值设定项中省略 NUL 终止字符 '[=14=]'没有空间了。您的案例可以缩小到一个简单的数组定义,例如:

char str[5] = "ALFAP";

这是一个语法快捷方式:

char str[5] = {'A', 'L', 'F', 'A', 'P'};

这可能有点误导,因为在不同的上下文中,相同的 "ALFAP" 代表字符串文字,它总是以 NUL 字符结尾:

char* str = "ALFAP" // here, "ALFAP" always contains NUL character at the end

My question is what happens in the background to explain this behavior? Regards.

你有一个结构 directive_nfo_t 类型的数组,每个结构 directive_nfo_t 包含五个字符的数组(在你的第一个例子中)。 当你在 directive_nfo_t 类型中有 5 个字符数组时得到的输出基本上是由于两件事 -

  1. 数组元素存储在连续的内存位置。
  2. 在C语言中,字符串的抽象思想是用空字符结尾的数组来实现的。

当你声明了一个directive_nfo_t类型的数组时,directive_nfo_t的每个元素存储在连续的内存位置,每个元素有5个字符数组(which are also stored in consecutive locations)。在数组的初始化列表 ({"ALIGN"},{"ASCII"},{"BSS"},{"BYTE"},{"END"},{"EQU"},{"ORG"}) 中,您已使用所有 5 个字符将数据存储在 directive_nfo_t ("ALIGN" and "ASCII") 的前两个元素中。正如在 C 语言中,对字符数组进行操作以实现字符串的抽象概念的函数一样,假设字符串将通过在末尾使用空字符来终止。因此,在 directive_nfo_t 数组的前两个元素中,printf 将继续打印字符,直到它到达空字符(它将在存储字符数组 "BSS" 的元素中找到)。打印 ALIGN 后,printf 将访问 directive_nfo_t (character A of ASCII) 数组的第二个元素的第一个字符。发生这种情况是因为 directive_nfo_t 类型数组的第一个元素中没有 space 空字符,并且编译器不会添加超出数组大小的字符,因为它会进行数组绑定检查。从数组的第三个元素开始,您有足够的 space 用于空字符,因此 printf 按预期工作。

如果分配较少的内存来存储字符数组并使用假定 null terminated character array 的函数,您将得到 UNDEFINED BEHAVIOR。当您想在数组中存储最多 MAX 个字符时,始终将字符数组的大小设置为 MAX + 1