Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/149.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++_C_Strict Aliasing - Fatal编程技术网

C++ 避免违反严格别名规则的最简单经验法则?

C++ 避免违反严格别名规则的最简单经验法则?,c++,c,strict-aliasing,C++,C,Strict Aliasing,当我读到另一个关于别名()的问题和它的首要答案时,我意识到我仍然不完全满意,尽管我认为我已经完全理解了它 (这个问题现在被标记为C和C++)。如果你的答案只是其中之一,请澄清哪一个。 因此,我想了解如何在这方面进行一些开发,以积极的方式投射指针,但使用一个简单的保守规则,确保我不会引入UB。我这里有一个这样一个规则的建议 (更新:当然,我们可以避免所有类型的双关语。但这并不是很有教育意义。当然,除非在联合例外之外,存在严格定义的零例外。) 更新2:我现在明白了为什么这个问题中提出的方法不正确。然

当我读到另一个关于别名()的问题和它的首要答案时,我意识到我仍然不完全满意,尽管我认为我已经完全理解了它

(这个问题现在被标记为C和C++)。如果你的答案只是其中之一,请澄清哪一个。 因此,我想了解如何在这方面进行一些开发,以积极的方式投射指针,但使用一个简单的保守规则,确保我不会引入UB。我这里有一个这样一个规则的建议

(更新:当然,我们可以避免所有类型的双关语。但这并不是很有教育意义。当然,除非在
联合
例外之外,存在严格定义的例外。)

更新2:我现在明白了为什么这个问题中提出的方法不正确。然而,了解是否存在一种简单、安全的替代方案仍然很有趣。到目前为止,至少有一个答案提出了这样的解决方案。

这是原始示例:

int main()
{
   // Get a 32-bit buffer from the system
   uint32_t* buff = malloc(sizeof(Msg));

   // Alias that buffer through message
   Msg* msg = (Msg*)(buff);

   // Send a bunch of messages    
   for (int i =0; i < 10; ++i)
   {
      msg->a = i;
      msg->b = i+1;
      SendWord(buff[0] );
      SendWord(buff[1] );   
   }
}
这意味着现在有两个指针(不同类型)指向相同的数据。我的理解是,任何试图写入其中一个指针的行为都会使另一个指针实质上无效。(我所说的“无效”是指我们可以安全地忽略它,但通过无效指针进行读/写是错误的。)

因此,我建议的规则是,一旦创建第二个指针(即创建
msg
),就应该立即永久地“退出”另一个指针

有什么比将指针设置为NULL更好的方法使其失效:

Msg* msg = (Msg*)(buff);
buff = NULL; // 'retire' buff. now just one pointer
msg->a = 5;
现在,分配给
msg->a
的最后一行不能使任何其他指针无效,因为,当然,没有指针

接下来,当然,我们必须找到一种方法来调用
SendWord(buff[1])。无法立即执行此操作,因为
buff
已失效且为空。我现在的建议是重新考虑

Msg* msg = (Msg*)(buff);
buff = NULL; // 'retire' buff. now just one pointer
msg->a = 5;

buff = (uint32_t*)(msg);   // cast back again
msg = NULL;                // ... and now retire msg

SendWord(buff[1] );
总之,每次在两个“不兼容”类型之间强制转换指针(我不确定如何定义“不兼容”?),都应该立即“退出”旧指针。如果有助于强制执行规则,请显式将其设置为NULL

这够保守吗

也许这太保守了,还有其他问题,但我首先想知道这是否足够保守,以避免通过违反严格别名引入UB

最后,重述为使用此规则而修改的原始代码:

int main()
{
   // Get a 32-bit buffer from the system
   uint32_t* buff = malloc(sizeof(Msg));

   // Send a bunch of messages    
   for (int i =0; i < 10; ++i)
   {  // here, buff is 'valid'

      Msg* msg = (Msg*)(buff);
      buff = NULL;
      // here, only msg is 'valid', as buff has been retired
      msg->a = i;
      msg->b = i+1;
      buff = (uint32_t*) msg;  // switch back to buff being 'valid'
      msg = NULL;              // ... by retiring msg
      SendWord(buff[0] );
      SendWord(buff[1] );
      // now, buff is valid again and we can loop around again
   }
}
intmain()
{
//从系统中获取32位缓冲区
uint32_t*buff=malloc(sizeof(Msg));
//发一串信息
对于(int i=0;i<10;++i)
{//这里,buff是“有效的”
味精*味精=(味精*)(浅黄色);
buff=NULL;
//在这里,只有消息是“有效的”,因为buff已经退役
msg->a=i;
msg->b=i+1;
buff=(uint32\u t*)msg;//切换回buff为“有效”
msg=NULL;//…通过使msg退役
发送字(buff[0]);
SendWord(buff[1]);
//现在,buff再次有效,我们可以再次循环
}
}
我的理解是,任何试图通过其中一个 将使另一个指针实质上无效

只要不访问类型为punned的指针,另一个“官方”指针就可以了。然而,如果你这样做,它将导致未定义的行为,这可能只是工作,做你所说的或其他事情,包括使其他指针无效。编译器可以随心所欲地处理UB

根据以下标准,使
buff
成为指向
Msg
的有效指针的唯一方法是
memcpy
/
memmove

memcpy( (void*)msg, (const void*) buff, sizeof (*msg));
此外,触发UB的不仅是写入,还包括读取或任何其他访问对象的方式:

如果程序试图通过访问对象的存储值 除以下类型之一之外的左值行为为 未定义的

一些编译器还允许“挂起”该规则,如GCC、clang和ICC(可能也是MSVC),但这不能被视为可移植或标准行为。 深入分析了进一步的技术及其代码生成分析

你真的需要打破严格的别名规则吗? 大多数时候,不,你不需要。要克服这个问题,必须有完善的法律解决办法。
在上述情况下,只需在
结构中存储一个普通指针
,并以确定的格式发送每个成员

C++回答:那不行。C++严格混叠规则明确地列举了可以用来访问对象的类型。如果您使用不同的类型,您将获得UB,即使您已经“退出”了所有不同类型的访问方法。根据C++14(n4140)3.10/10,允许的类型为:

如果程序试图通过glvalue访问对象的存储值,而不是 以下类型的行为未定义:

  • 对象的动态类型
  • 对象动态类型的cv限定版本
  • 与对象的动态类型类似的类型(如4.4中所定义)
  • 与对象的动态类型相对应的有符号或无符号类型
  • 与动态类型的cv限定版本相对应的有符号或无符号类型 对于对象
  • 在其元素或非静态元素中包含上述类型之一的聚合或联合类型 数据成员(递归地包括子集合的元素或非静态数据成员 或包含的联盟)
  • 是对象动态类型的基类类型(可能是cv限定的)
  • 字符
    无符号字符
    类型
根据4.4,“类似类型”涉及修改多级指针的cv鉴定

所以,如果你
int main()
{
   // Get a 32-bit buffer from the system
   uint32_t* buff = malloc(sizeof(Msg));

   // Send a bunch of messages    
   for (int i =0; i < 10; ++i)
   {  // here, buff is 'valid'

      Msg* msg = (Msg*)(buff);
      buff = NULL;
      // here, only msg is 'valid', as buff has been retired
      msg->a = i;
      msg->b = i+1;
      buff = (uint32_t*) msg;  // switch back to buff being 'valid'
      msg = NULL;              // ... by retiring msg
      SendWord(buff[0] );
      SendWord(buff[1] );
      // now, buff is valid again and we can loop around again
   }
}
memcpy( (void*)msg, (const void*) buff, sizeof (*msg));
1: int *some_buff = malloc(sizeof(whatever));
2: memset(some_buff,0,sizeof(whatever));
3: while (some_buff[0] == 0)
4: {
5:     whatever *manipulator = (whatever*)some_buff; 
6:     manipulate(manipulator);
7: }
int *some_buff = malloc(sizeof(whatever));
memset(some_buff,0,sizeof(whatever));
whatever *manipulator = (whatever*)some_buff;
manipulate(manipulator);
printf("%d\n",some_buff[0]);