什么是类型双关语,它的目的是什么?
What is type punning and what is the purpose of it?
Type punning
指针锯齿的一种形式,其中两个指针引用内存中的同一位置,但将该位置表示为不同的类型。编译器会将这两个"双关语"视为不相关的指针。类型双关可能会导致通过两个指针访问的任何数据出现依赖问题。
这篇文章想说什么? 如果我使用它或不使用它会发生什么?
正如它所说,类型双关语是指您有两个不同类型的指针,它们都指向同一位置。例:
// BAD CODE
uint32_t data;
uint32_t* u32 = &data;
uint16_t* u16 = (uint16_t*)&data;
*u16 = ... // de-referencing invokes undefined behavior
此代码调用 C++(和 C)中的未定义行为,因为不允许通过不兼容类型的指针访问相同的内存位置(有一些特殊例外)。这被非正式地称为"严格别名冲突",因为它违反了严格的别名规则。
进行类型双关的另一种方法是通过联合:
// 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 中是允许的并且完全没问题)。
类型双关语和别名是不同但相关的概念,尽管它们在很大程度上是正交的,但一些编译器编写者似乎无法区分它们。
类型双关是指将存储写入为一种类型并读取为另一种类型的情况,通常目的是允许将值解释为位序列,允许将位序列解释为值,或允许将值用作表示形式匹配的另一种类型, 至少在感兴趣的部分。 例如,后一种形式的类型双关语在以下情况下可能很有用:可能具有指向各种结构类型的指针,所有这些结构类型共享一个公共初始序列,并且可能需要对所有这些结构的公共初始序列成员进行操作,尽管结构的类型不同。 请注意,尽管该标准包含明确的保证,这表明后一种形式的类型双关语应该是有用的,但将其与别名混淆的编译器不支持此类构造。
别名是指一种不同的概念,其中使用两个或多个同时活动但看似不相关的方式以相互交互的方式访问存储。 给定如下内容:
int test1(int *p1, int *p2)
{
*p1 = 1;
*p2 = 2;
return *p1;
}
如果p1==p2
,则p1
和p2
将别名,因为p1
将用于访问p2
在创建和上次使用p2
之间的某个时间标识的存储,在上下文中p1
不可能从p2
创建[有可能p1
可能是在调用函数之前从p2
创建的, 但是p1
不可能从函数中的p2
派生出来]。 但是,由于标准允许标识相同类型的左值之间的混叠,因此上述构造将在p1==p2
时定义行为,尽管p1
和p2
别名。
另一方面,给定如下内容:
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;
}
在这里,指针p1
、p2
和p3
具有明显不相交的生存期,因此不会同时处于活动状态,并且会相互别名。 每个指针都是独立派生自uarr
,每个指针的生存期将在下次使用uarr
之前结束。 因此,此代码使用类型双关语来访问与struct s1
和struct s2
相同的存储,但所写的不会利用别名,因为对相关存储的所有访问都明显来自同一个根级对象uarr
。
不幸的是,即使基于类型的访问规则旨在(根据基本原理和脚注)指示何时允许事物别名,一些编译器以使语言功能(如通用初始序列保证)基本上无用的方式解释它们,因为他们使用类型访问规则作为重写代码的借口,以从uarr
中删除p3
的派生, 从而在没有混叠的地方引入混叠。
使用双关语是有充分理由的。 假设您想通过串行链路传输数据,但数据是 实际上是不同类型的包装结构。 打包结构作为 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 = %08Xn",x,y,z);
return 0;
}
答: x = 04030201, y = 05040302, z = 08070605
请注意,这是小端序(低内存中的LSB)
- 静态自动 constexpr t = { "red" , "black" , "green" } 是什么类型;派生到?
- 这是什么类型的C++语法,我应该采取什么步骤来理解这一点
- 这在C++是什么类型的错误?
- int(int)& 或 int(int) const &是什么类型?
- 我如何找出3D容器的元素是什么类型
- 在 c 和 c++ 中,二维数组的元素是什么类型?
- 这个 lambda 的参数是什么类型?
- 转换逻辑目标是什么类型
- 如果我创建一个修改值的迭代器,静态成员"reference"应该是什么类型?
- 这是什么类型的行为
- 给定以下内容,"function"是什么类型?
- C++:文本文件中的数据是什么类型的
- C++11 中的"auto var = {condition} ? 1 : 1.0"是什么类型?是双精度还是整数?
- C++中的 decltype(this) 是什么类型
- 如何知道模板<类型名称类型>是什么类型?
- 内置数组的大小是什么类型?
- c++ 11中的std::chrono::high_resolution_clock::now()是什么类型?
- 在Microsoft Visual C++中,光线跟踪器应该是什么类型的项目
- 如何为delete编写一个C包装器,既快速又释放给定的任何类型,而不告诉它是什么类型
- 如何编写一个函数,无论数组是什么类型都输出它