以下程序是否是C中严格符合的程序?我对c90和c99感兴趣,但c11答案也可以接受。
#include <stdio.h>
#include <string.h>
struct S { int array[2]; };
int main () {
struct S a = { { 1, 2 } };
struct S b;
b = a;
if (memcmp(b.array, a.array, sizeof(b.array)) == 0) {
puts("ok");
}
return 0;
}
在comments to my answer in a different question中,Eric Postpischil坚持认为程序输出将根据平台而改变,主要是由于未初始化填充位的可能性。我认为结构赋值会覆盖b
中的所有位与a
中的相同位。但是,C99似乎没有提供这样的保证。从第6.5.16.1节第2节:
在简单赋值(
=
)中,右操作数的值将转换为赋值表达式的类型,并替换存储在左操作数指定的对象中的值。
复合类型背景下的“转换”和“替换”是什么意思?
最后,考虑相同的程序,除了a
和b
的定义是全局的。 程序是否是一个严格符合的程序?
编辑:我想在这里总结一些讨论材料,而不是添加我自己的答案,因为我没有自己的创作。
b.array
可能包含也可能不包含与a.array
不同的位。a
不需要转换,因为它与b
的类型相同,但替换是按值,并按成员完成。a
和b
中的定义属于全局,但是分配后,b.array
可能包含也可能不包含与a.array
不同的位。 (关于b
中填充字节的讨论很少,但是发布的问题与结构比较没有关系.c99没有提到如何在静态存储中初始化填充,但是c11明确指出它是零初始化。)< / LI>
memcmp
如果使用b
中的memcpy
初始化a
,则定义得很明确。感谢所有参与讨论的人。
答案 0 :(得分:4)
在C99§6.2.6
中§6.2.6.1概述
1除非本条款规定,否则所有类型的陈述都没有说明。
[...]
4 [..]具有相同对象表示的两个值(除NaN之外)比较相等,但比较相等的值可能具有不同的对象表示。
6当值存储在结构或联合类型的对象中时,包括在成员对象中,对应于任何填充字节的对象表示的字节采用未指定的值。 42) < / p>
42)因此,例如,结构分配不需要复制任何填充位。
43)具有相同有效类型T的对象x和y在作为T类型的对象访问时可能具有相同的值,但在其他上下文中具有不同的值。特别是,如果为类型T定义了==,那么x == y并不意味着memcmp(&amp; x,&amp; y,sizeof(T))== 0.此外,x == y并不一定意味着x和y具有相同的值;对T类值的其他操作可以区分它们。
§6.2.6.2整数类型
[...]
2对于有符号整数类型,对象表示的位应分为三组:值位,填充位和符号位。不需要任何填充位; [...]
[...]
5未指定任何填充位的值。[...]
在J.1未指明的行为
- 在结构或联合中存储值时填充字节的值(6.2.6.1)。
[...]
- 整数表示中任何填充位的值(6.2.6.2)。
因此,a
和b
的表示中可能存在不同而不影响值的位。这与其他答案的结论相同,但我认为标准中的这些引用将是一个很好的附加背景。
如果您执行memcpy
,那么memcmp
将始终返回0并且程序将严格符合要求。 memcpy
将a
的对象表示重复为b
。
答案 1 :(得分:0)
我的意见是它严格遵守。根据4.5,Eric Postpischil提到:
严格遵守的程序应仅使用该程序的那些功能 本国际标准中规定的语言和库。它 不得产生依赖于任何未指定,未定义或 实现定义的行为,不得超过任何最小值 实施限制。
有问题的行为是memcmp
的行为,这是明确定义的,没有任何未指定,未定义或实现定义的方面。它适用于表示的原始位,不知道有关值,填充位或陷阱表示的任何信息。因此,在这种特定情况下,memcmp
的结果(但不是功能)取决于这些字节中存储的值的实现。
6.2.6.2中的脚注43):
对象x和y可能具有相同的有效类型T to 当它们作为T类型的对象被访问时具有相同的值,但是 在其他情境中拥有不同的价值观。特别是,如果==是 为类型T定义,然后x == y并不意味着memcmp(&amp; x,&amp; y, sizeof(T))== 0.此外,x == y并不一定意味着 x和y具有相同的值;对类型T的值的其他操作可以 区分它们。
编辑:
进一步思考,由于这个原因,我不太确定严格遵守:
它不会产生依赖于任何未指定[...]
的输出
显然memcmp
的结果取决于表示的未指定行为,从而实现此子句,即使memcmp
本身的行为已明确定义。在输出发生之前,该子句没有说明功能的深度。
所以不严格遵守。
编辑2:
当使用memcpy
复制结构时,我不确定它是否会严格符合要求。根据附件J,初始化a
时会发生未指定的行为:
struct S a = { { 1, 2 } };
即使我们假设填充位不会改变且memcpy
总是返回0,它仍然使用填充位来获得其结果。并且它依赖于它们不会改变的假设,但是标准中没有关于此的保证。
我们应该区分结构中的填充字节,用于对齐,以及特定本机类型中的填充位,如int
。虽然我们可以安全地假设填充字节不会改变,但仅仅因为它没有真正的原因,同样不适用于填充位。标准提到奇偶校验标志作为填充位的示例。这可以是实现的软件功能,但它也可以是硬件功能。因此,可能存在用于填充位的其他硬件标志,包括因任何原因在读访问时改变的标志。
我们将难以找到这样一个奇特的机器和实现,但我没有看到任何禁止这一点。如果我错了,请纠正我。