Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/72.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 在类型之间按字节复制数据是否会打破严格的别名?_C_Language Lawyer_Undefined Behavior - Fatal编程技术网

C 在类型之间按字节复制数据是否会打破严格的别名?

C 在类型之间按字节复制数据是否会打破严格的别名?,c,language-lawyer,undefined-behavior,C,Language Lawyer,Undefined Behavior,假设我有两个大小相同的类型A和B,我有两个变量 A a = ... ; // Initialized to some constant of type A B b; 如果我使用以下方法将a的内容复制到b- assert(sizeof(A) == sizeof(B)); size_t t; for( t=0; t < sizeof(A); t++){ ((char*)&b)[t] = ((char*)&a)[t]; } assert(sizeof(A)==size

假设我有两个大小相同的类型
A
B
,我有两个变量

A a = ... ; // Initialized to some constant of type A
B b;
如果我使用以下方法将
a
的内容复制到
b
-

assert(sizeof(A) == sizeof(B));
size_t t;
for( t=0; t < sizeof(A); t++){
    ((char*)&b)[t] = ((char*)&a)[t];
}
assert(sizeof(A)==sizeof(B));
尺寸t;
对于(t=0;t
这是否违反了C的严格别名规则? 我知道将指针指向
char*
并读取它不是UB,但我关心的是作业中涉及的两个解除限制


如果这不是UB,这是否是类型双关的有效方法?

此代码不违反别名规则。根据最新草案(n1570),§6.5第7节:

对象的存储值只能由左值表达式访问,该左值表达式具有 以下类型:
-与对象的有效类型兼容的类型,
-与对象的有效类型兼容的类型的限定版本,
-一种类型,它是与数据的有效类型相对应的有符号或无符号类型 对象,
-一种类型,它是与的限定版本相对应的有符号或无符号类型 对象的有效类型,
-一种聚合或联合类型,其中包括上述类型之一 成员(递归地包括子集合或包含的联合的成员),或
-字符类型

(强调矿山)

我对这项任务中涉及的两项解除保护措施都感到担忧

这些解引用是使用字符类型访问存储值


当然,如果
A
的表示形式不是
B

的有效表示形式,您仍然可能触发未定义的行为。在目标具有声明类型的情况下,没有问题,但在仅通过指针知道目标的情况下,标准是不明确的。根据写得非常糟糕的6.5p6,使用
memcpy
memmove
或“作为字符类型数组”复制数据[无论这意味着什么]将导致源的有效类型应用于目标。该标准没有规定在不将操作视为复制“字符类型数组”的情况下复制字节序列必须执行的操作。

这不是由于严格的别名(实际上,这里没有别名,除了
char*
,这是允许的)。这是UB,因为您可能刚刚用陷阱表示填充了B。您正式复制的位模式表示的值使
b
处于不确定状态,IIRC。@如果
b
被保证为没有陷阱表示的类型,则StoryTeller是否为“确定”?。但是标准并没有向您承诺任何有意义的语义。@StoryTeller,如果
A
B
是两种结构类型,具有完全相同的字段,那么我能期望每个字段的行为都是相同的吗。因为标准保证,如果两个结构使用相同的成员定义,那么任何实现都必须具有相同的内存布局。标准确实保证了这一点。我想你应该没事。被复制的任何填充位的内容都不会干扰,因为它们只是未指定的。所有相应的成员将正确复制其值。作为补充说明,我认为应该用调用
memcpy
来替换您的循环。这将需要更少的铸造(反过来会看起来更干净)。我认为这个答案毫无疑问地消除了困惑。从上面评论中的讨论(以及您最后一行的讨论)可以清楚地看出,如果
B
没有陷阱表示,则不会是UB。但该标准并不能保证语义上的任何东西。但是,如果A和B是两种结构类型,具有完全相同的字段,那么我可以期望其行为与单独复制的每个字段相同。因为标准保证,如果两个结构定义了相同的成员,则任何实现都必须具有相同的内存布局。如果目标仅由指针知道,则6.5p6会抛出一个扳手。在我的问题中,目标类型是完整的,因为我强加了a和B大小相等的条件。但谢谢你的洞察力。根据我的经验,很少有人看到标准对某些东西如此模糊。@AjayBrahmakshatriya:标准的作者认识到不同的应用领域需要以不同的方式使用别名,因此,无论标准是否要求,任何适合于任何特定领域的高质量编译器都应该在适合该领域的情况下支持别名,因此无需深入研究每一种可能的情况。尽管如此,6.5p6还是非常糟糕,因为它为真正的优化提供了有用的机会,但也为恶作剧提供了主要的机会。我认为这是不对的:无论是声明的类型还是有效的类型,都只可能用于分配的对象。。。一旦他们有了一个有效的类型,它将继续存在。如果你愿意,我可以在明天的标准中找到相关的段落
然后通过
char*
复制
a
,确实会有问题,因为
*b
会得到有效类型
char
。而
B,不会有问题。@FelixPalmen:在目标是简单声明对象的特定场景中,没有问题。在目标仅由指针知道的场景中[这种情况适用于分配的对象,但也适用于函数从其他地方接收指针的情况],在字节复制用于类型转换的情况下,轮子会脱落。