【问题标题】:Is using memcmp on array of int strictly conforming?在 int 数组上使用 memcmp 是否严格符合?
【发布时间】:2012-08-13 05:40:24
【问题描述】:

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

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

复合类型上下文中的“转换”和“替换”是什么意思?

最后,考虑同一个程序,除了 a 和 b 的定义是全局的。 那个程序会是一个严格遵从的程序吗?

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

  • 程序不严格符合。由于赋值是按值而不是按表示,b.array 可能包含也可能不包含与 a.array 不同的位设置。
  • a 不需要转换,因为它和b 是同一类型,但是替换是按值,逐个成员完成。
  • 即使a 和b 中的定义是全局的,在分配后,b.array 可能包含也可能不包含与a.array 不同的位设置。 (b 中几乎没有关于填充字节的讨论,但发布的问题与结构比较无关。c99 没有提及如何在静态存储中初始化填充,但 c11 明确声明它是零初始化的。)
  • 附带说明,如果 b 是用来自 a 的 memcpy 初始化的,那么 memcmp 是明确定义的。

感谢所有参与讨论的人。

【问题讨论】:

  • 与填充无关,也不适用于问题中使用的 1 和 2 值,但仍然本着它的精神,我没有发现任何东西阻止考虑 -0 的符号和幅度或补码实现作为一种冗余方式来表示 0 以在分配时对其进行规范化。
  • 你在问题​​中的例子仍然不是一个好例子。您不是在比较 struct,因为这似乎是您的意图,而只是数组。因此,在您在此处给出的示例中,问题仍然只是int 是否具有填充位,或者例如所谓的负零。这些事情不会发生在现代架构上。您真的必须考虑一个具有实际填充字节(而不是填充位)的真实结构,以便问题变得相关。
  • @JensGustedt:这个问题具体是关于memcmp 的数组int,struct 用于对b 持有的数组进行赋值。
  • @user315052,对于数组,memcpy 就可以了。 memcpy 和 memcmp 在字节基础上工作,这些字节被视为 unsigned char。 ` unsigned char` 是唯一保证没有填充位并且所有表示都有不同值的数据类型。
  • Eric 是 SO 上为数不多的人之一,在涉及 C 标准语言律师的情况下,您几乎可以简单地从表面上接受他的意见。

标签: c language-lawyer


【解决方案1】:

在 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) 中任何填充位的值。

因此,a 和 b 的表示中可能存在不同但不影响值的位。这与其他答案的结论相同,但我认为标准中的这些引用将是很好的附加上下文。


如果您执行memcpy,则memcmp 将始终返回0,并且程序将严格符合。 memcpy 将a 的对象表示复制到b。

【讨论】:

  • 谢谢!所以,在memcpy 之后执行memcmp 应该是安全的,因为memcpy 复制相同的位,对吧?
  • @user315052 是的,这符合要求;在答案中查看详细信息
【解决方案2】:

我的观点是它严格符合。根据 Eric Postpischil 提到的 4.5:

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

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

6.2.6.2 中的脚注 43):

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

编辑:

再想一想,由于这个原因,我不太确定是否严格符合:

它不应产生依赖于任何未指定的[...]

的输出

很明显memcmp 的结果取决于表示的未指定行为,从而满足了这个条款,即使memcmp 本身的行为是明确定义的。在输出发生之前,该子句没有说明功能的深度。

所以它不严格符合。

编辑 2:

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

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

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

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

我们很难找到这样一个奇特的机器和实现,但我看不出有什么禁止这样做的。如果我错了,请纠正我。

【讨论】:

  • 程序可以产生“ok”或什么也没有。如果程序在一个平台上产生“ok”,而在另一个平台上却没有,这是否意味着输出取决于未明确定义的东西?
  • @jxh:如果一个程序产生的字符序列不允许在一个平台之间变化,那么任何严格遵守的程序都很难使用rand()。更有用的一致性定义是程序必须在任何符合标准的平台上满足其要求;如果上述程序的要求表明它必须打印“ok”或什么都不打印,但不能输出字符串“Fred”,则上述程序将严格遵守标准的任何合理阅读[尽管它可能超出最低限度的实现限制].
  • @supercat:我的评论仅限于示例程序,而不是通用程序行为规范。
  • @jxh:我也是。如果您的程序可以在任何符合标准的平台上产生未定义行为,那么符合标准的平台可以生成输出“Fred”的代码。即使您的程序的唯一要求是它不输出“Fred”,也可能存在符合标准的平台,如果您的程序调用未定义行为,您的程序将违反该要求。但是,由于它不调用 UB,所有符合标准的实现都必须生成遵守不输出“Fred”要求的代码。
  • @supercat:您介绍了使用rand() 的概念,它不一定需要相同的程序来生成相同的输出。我在评论中解释说,如果我的示例程序严格遵守,它应该为所有兼容的编译器生成相同的输出。该答案的原始版本表明该程序符合要求,即使它可能在不同平台上产生不同的输出。
猜你喜欢
  • 2018-02-22
  • 2013-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-02
  • 2011-11-28
  • 2012-06-26
  • 1970-01-01
相关资源
最近更新 更多