什么是类型双关语,它的目的是什么?

Posted

技术标签:

【中文标题】什么是类型双关语,它的目的是什么?【英文标题】:What is type punning and what is the purpose of it? 【发布时间】:2017-10-23 13:29:22 【问题描述】:

双关语

指针别名的一种形式,其中两个指针指向内存中的同一位置,但将该位置表示为不同的类型。编译器会将两个“双关语”视为不相关的指针。对于通过两个指针访问的任何数据,类型双关都有可能导致依赖性问题。

这篇文章想表达什么? 如果我使用它或不使用它会发生什么?

【问题讨论】:

到底什么不清楚?您自己已经给出了答案这一事实并没有使回答问题变得更容易 What is the strict aliasing rule?的可能重复 Unions and type-punning的可能重复 【参考方案1】:

正如它所说,类型双关语是指你有两个不同类型的指针,都指向同一个位置。示例:

// BAD CODE
uint32_t data;
uint32_t* u32 = &data;
uint16_t* u16 = (uint16_t*)&data; // undefined behavior

此代码调用 C++(和 C)中的未定义行为,因为不允许您通过不兼容类型的指针访问相同的内存位置(有一些特殊例外)。这被非正式地称为“严格的别名违规”,因为它违反了strict aliasing rule。

进行类型双关的另一种方法是通过联合:

// BAD C++ CODE
typedef union

  uint32_t u32;
  uint16_t u16 [2];
 my_type;

my_type mt;
mt.u32 = 1;
std::cout << mt.u16[0]; // access union data through another member, undefined behavior

这在 C++ 中也是未定义的行为(但在 C 中是允许的并且非常好)。

【讨论】:

你的意思是mt.u16[0] 我认为联合行为在 C++ 中适用于 pod 类型 @Cort 它适用于大多数平台,但它是未定义的行为。【参考方案2】:

类型双关语和别名是不同但相关的概念,一些编译器编写者似乎无法区分它们,尽管它们在很大程度上是正交的。

类型双关是指将存储作为一种类型写入并作为另一种类型读取的情况,通常是为了允许将值解释为位序列,允许将位序列解释为值,或允许将值用作表示匹配的另一种类型,至少在感兴趣的部分中。例如,后一种形式的类型双关语可能在以下情况下很有用:一个人可能有指向各种结构类型的指针,所有这些结构类型都共享一个公共初始序列,并且可能需要对所有的公共初始序列成员进行操作。尽管结构的类型不同,但这些结构。请注意,即使标准包含明确的保证,表明后一种形式的类型双关应该是有用的,但将其与别名混淆的编译器不支持这种结构。

别名是指一个不同的概念,其中使用两个或多个同时活动但看似不相关的手段以相互交互的方式访问存储。给定类似的东西:

int test1(int *p1, int *p2)

  *p1 = 1;
  *p2 = 2;
  return *p1;

如果p1==p2,则p1p2 将使用别名,因为p1 将用于访问p2 标识的存储空间,介于p2 的创建和最后一次使用之间,在以下上下文中p1 不能从 p2 创建 [有可能 p1 在调用函数之前可能已经从 p2 创建,但不可能在函数内从 p2 派生 p1 ]。因为标准允许标识相同类型的左值之间存在别名,但是,上述构造将定义当p1==p2 时的行为,尽管事实上p1p2 有别名。

另一方面,假设如下:

struct s1 int x; ;
struct s2 int x; ;
union s1s2 struct s1 v1; struct s2 v2;  uarr[100];

int test1(int i, int j)

  int temp;
   struct s1 *p1 = &uarr[i].v1; temp = p1->x; 

  if (temp)
     struct s2 *p2 = &uarr[j].v2; p2->x = 1; 

   struct s1 *p3 = &uarr[i].v1; temp = p3->x; 
  return temp;

在这里,指针p1p2p3 具有明显不相交的生命周期,因此不会同时处于活动状态并相互别名。每个指针都独立派生自uarr,每个指针的生命周期将在下次使用uarr 之前结束。因此,此代码使用类型双关语来访问与struct s1struct s2 相同的存储,但正如所写的那样,并没有利用别名,因为对所讨论存储的所有访问显然都来自同一个根-级别对象uarr

不幸的是,尽管基于类型的访问规则旨在(根据基本原理和脚注)指示事物何时允许别名,但一些编译器以使语言特性的方式解释它们例如 Common Initial Sequence 保证基本上是无用的,因为他们使用类型访问规则作为重写代码的借口,以删除从uarr 派生的p3,从而在已经存在的地方引入了别名没有。

【讨论】:

【参考方案3】:

使用双关语有充分的理由。 想象一下,您想通过串行链路传输数据,但数据是 实际上是不同类型的压缩结构。 打包结构作为 BYTE 数组发送,但要显示数据 这是不同的类型...

int main(void)  

    unsigned char a[10] = 1,2,3,4,5,6,7,8,9,0;
    unsigned int x,y,z;

    x = *(unsigned int*) a;
    y = *(unsigned int*) (a+1);
    z = *((unsigned int*) a+1);

    printf("x = %08X, y = %08X, z = %08X\n",x,y,z);

    return 0;

回答: x = 04030201, y = 05040302, z = 08070605

请注意,这是小端(低位内存中的 LSB)

【讨论】:

以上是关于什么是类型双关语,它的目的是什么?的主要内容,如果未能解决你的问题,请参考以下文章

[react] React Fiber它的目的是解决什么问题?

nameof 的目的是啥?

什么是javascript?并说明它的使用目的及基本特点。

什么是libXinerama?

Webpack postcss loader,它的目的是啥?

布局兼容类型的目的是啥?