填充数组的机制
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"};
编译器实际上用引号内的字符填充了0th到4th"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 个字符数组时得到的输出基本上是由于两件事 -
- 数组元素存储在连续的内存位置。
- 在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
。
我尝试填充定义如下的结构数组:
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"};
编译器实际上用引号内的字符填充了0th到4th"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 个字符数组时得到的输出基本上是由于两件事 -
- 数组元素存储在连续的内存位置。
- 在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
。