下面的程序是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坚持认为程序输出将根据平台的不同而改变,这主要是因为可能存在未初始化的填充位。我认为结构赋值将覆盖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明确声明它是零初始化的。)memcmp的memcpy初始化b,则可以很好地定义b。我感谢所有参与讨论的人。
发布于 2012-08-16 21:23:06
我的看法是,这是严格一致的。根据埃里克·波斯特皮奇尔提到的4.5条:
严格符合标准的程序只应使用本国际标准中规定的语言和库的特性。它不应产生依赖于任何未指定的、未定义的或实现定义的行为的输出,并且不应超过任何最低实现限制。
所讨论的行为是memcmp的行为,这是定义良好的,没有任何未指定的、未定义的或实现定义的方面。它在表示的原始位上工作,而不知道任何关于值、填充位或陷阱表示的信息。因此,结果(但在这种特殊情况下不依赖于memcmp的)取决于存储在这些字节内的值的实现。
(第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本身的行为定义得很好。在输出发生之前,该子句不会说任何关于功能深度的内容。
因此,这是而不是严格一致的。
编辑2:
我不太确定当memcpy用于复制结构时,它是否会成为严格的一致性。根据附件J,未指定的行为发生在初始化a时:
struct S a = { { 1, 2 } };即使我们假设填充位不会改变,并且memcpy总是返回0,它仍然使用填充位来获得其结果。它依赖于这样的假设,即它们不会改变,但在这方面的标准中没有保证。
我们应该区分用于对齐的结构中的填充字节和特定本机类型(如int )中的填充位。虽然我们可以安全地假设填充字节不会改变,但这只是因为没有真正的原因,但同样的情况并不适用于填充位。标准提到一个奇偶校验标志作为一个填充位的例子。这可能是一个软件功能的实现,但也可能是一个硬件功能。因此,可能存在用于填充位的其他硬件标志,包括因任何原因而更改读访问的硬件标志。
我们将很难找到这样一个异国情调的机器和实现,但我没有看到任何禁止这一点。如果我错了就纠正我。
https://stackoverflow.com/questions/11994513
复制相似问题