在int数组上使用memcmp严格符合吗?

以下程序是否是C中严格符合的程序? 我对c90和c99感兴趣,但c11的答案也是可以接受的。

#include  #include  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; } 

在对不同问题的答案的评论中 ,Eric Postpischil坚持认为程序输出将根据平台而改变,主要是由于未初始化填充位的可能性。 我认为结构赋值会覆盖b所有位与b中的相同。 但是,C99似乎并没有提供这样的保证。 从第6.5.16.1节第2节:

简单赋值= )中,右操作数的值将转换为赋值表达式的类型,并替换存储在左操作数指定的对象中的值。

在复合类型的背景下,“转换”和“替换”是什么意思?

最后,考虑相同的程序,除了ab的定义是全局的。 计划是否是严格遵守的计划?

编辑:只是想在这里总结一些讨论材料,而不是添加我自己的答案,因为我没有真正拥有自己的创作。

  • 该计划并非严格遵守。 由于赋值是按值而不是表示,因此b.array可能包含也可能不包含与a.array不同的位。
  • a不需要转换,因为它与b类型相同,但是替换是按值,并且由成员完成。
  • 即使ab中的定义是全局的,post assignment, b.array可能包含也可能不包含与a.array不同的位。 (关于b的填充字节几乎没有讨论,但是发布的问题不是关于结构比较.c99没有提到如何在静态存储中初始化填充,但c11明确声明它是零初始化。)
  • 另一方面,如果b用来自a memcpy初始化,则会同意memcmp定义良好。

我要感谢所有参与讨论的人。

在C99§6.2.6中

§6.2.6.1总则

1除本条款规定外,所有类型的陈述均未指明。

[…]

4 [..]具有相同对象表示的两个值(除NaN之外)比较相等,但比较相等的值可能具有不同的对象表示。

6当值存储在结构或联合类型的对象中(包括在成员对象中)时,对应于任何填充字节的对象表示的字节将采用未指定的值。 42)

42)因此,例如,结构分配不需要复制任何填充比特。

43)具有相同有效类型T的对象x和y在作为类型T的对象访问时具有相同的值,但在其他上下文中具有不同的值。 特别是,如果为类型T定义了==,那么x == y并不意味着memcmp(&x,&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)。

因此, ab的表示中可能存在不同而不影响该值的位。 这与其他答案的结论相同,但我认为标准中的这些引用将是良好的附加背景。


如果你做一个memcpy那么memcmp将始终返回0并且程序将严格符合。 memcpy将a的对象表示复制到b

我的意见是它严格遵守。 根据4.5,Eric Postpischil提到:

严格符合的程序应仅使用本国际标准中规定的语言和库的特征。 它不应产生依赖于任何未指定,未定义或实现定义的行为的输出,并且不得超过任何最小实现限制。

有问题的行为是memcmp的行为,这是明确定义的,没有任何未指定,未定义或实现定义的方面。 它适用于表示的原始位,而不知道有关值,填充位或陷阱表示的任何信息。 因此,在这种特定情况下, memcmp结果 (但不是function )取决于存储在这些字节中的值的实现。

6.2.6.2中的脚注43):

具有相同有效类型T的对象x和y在作为类型T的对象访问时具有相同的值,但在其他上下文中具有不同的值。 特别是,如果为类型T定义了==,那么x == y并不意味着memcmp(&x,&y,sizeof(T))== 0.此外,x == y并不一定意味着x和y具有相同的价值; 对类型T的值的其他操作可以区分它们。

编辑:

进一步思考,我不再那么严格遵守,因为这样:

它不应产生依赖于任何未指明的[…]的输出。

很明显, memcmp的结果取决于表示的未指定行为,从而实现了该子句,即使memcmp本身的行为已被很好地定义。 在输出发生之前,该子句没有说明function的深度。

所以它并不严格符合。

编辑2:

当使用memcpy复制结构时,我不太确定它是否会严格符合要求。 根据附件J,初始化a时会发生未指定的行为:

 struct S a = { { 1, 2 } }; 

即使我们假设填充位不会改变并且memcpy总是返回0,它仍然使用填充位来获得其结果。 它依赖于它们不会改变的假设,但标准中并没有保证这一点。

我们应该区分结构中的填充字节,用于对齐,以及特定本机类型(如int填充位。 虽然我们可以安全地假设填充字节不会改变,但仅仅因为它没有真正的原因,同样不适用于填充位。 标准提到奇偶校验标志作为填充位的示例。 这可以是实现的软件function,但它也可以是硬件function。 因此,可能存在用于填充比特的其他硬件标志,包括出于任何原因在读取访问上改变的标志。

我们将很难找到这样一个奇特的机器和实现,但我没有看到任何禁止这一点。 如我错了请纠正我。