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

时间:2012-08-16 19:54:35

标签: c language-lawyer

以下程序是否是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节:

  

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

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

最后,考虑相同的程序,除了ab的定义是全局的。 程序是否是一个严格符合的程序?

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

  • 该计划并非严格遵守。由于分配是按值而非代表,b.array可能包含也可能不包含与a.array不同的位。
  • a不需要转换,因为它与b的类型相同,但替换是按值,并按成员完成。
  • 即使ab中的定义属于全局,但是分配后,b.array可能包含也可能不包含与a.array不同的位。 (关于b中填充字节的讨论很少,但是发布的问题与结构比较没有关系.c99没有提到如何在静态存储中初始化填充,但是c11明确指出它是零初始化。)< / LI>
  • 在旁注中,我们同意memcmp如果使用b中的memcpy初始化a,则定义得很明确。

感谢所有参与讨论的人。

2 个答案:

答案 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)。
  •   

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


如果您执行memcpy,那么memcmp将始终返回0并且程序将严格符合要求。 memcpya的对象表示重复为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。虽然我们可以安全地假设填充字节不会改变,但仅仅因为它没有真正的原因,同样不适用于填充位。标准提到奇偶校验标志作为填充位的示例。这可以是实现的软件功能,但它也可以是硬件功能。因此,可能存在用于填充位的其他硬件标志,包括因任何原因在读访问时改变的标志。

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