任何人都可以在C中解释什么是类型双关语,并通过简单的示例程序演示何时出现此类问题?
我在许多网站(甚至维基)都进行了搜索,但即便如此,我也无法理解。
答案 0 :(得分:6)
Punning类型是一个广泛的概念,适用于任何具有类型系统和一点灵活性的语言,因此我将使用维基百科的例子与Berkeley套接字:
在Berkeley套接字界面中可以找到一个典型的类型惩罚示例。将已打开但未初始化的套接字绑定到IP地址的函数声明如下:
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
绑定函数通常按如下方式调用:
struct sockaddr_in sa = {0};
int sockfd = ...;
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
bind(sockfd, (struct sockaddr *)&sa, sizeof sa);
Berkeley套接字库从根本上依赖于以下事实:在C中,指向struct
sockaddr_in
的指针可以自由转换为指向structsockaddr
的指针;此外,两种结构类型共享相同的内存布局。因此,对结构字段my_addr->sin_family
(my_addr
类型为struct sockaddr*
)的引用实际上将引用字段sa.sin_family
(其中sa
的类型struct sockaddr_in
)。换句话说,套接字库使用类型punning来实现一种基本的继承形式。在编程世界中经常看到使用“填充”数据结构,以允许在实际上相同的存储空间中存储不同类型的值。当两种结构用于互斥以进行优化时,通常可以看到这种情况。
编辑:我没有注意到你提到尝试维基百科的编辑。我认为在这种情况下你应该拿走的是我在第一句中所说的,即“类型Punning是一个广泛的概念,适用于任何具有类型系统和一点灵活性的语言”。如果你遇到问题,我会说要寻找更多的策略示例和实现(也许在C语言中看OOP以获得更多涉及的一些概念[它本身并不完全相同])
另一个编辑:我想到你可能意味着在工会的背景下打字,所以这里是this问题的一个修改过的例子,它询问了工会的目的(在这里向后工作) ):
union RGB
{
uint32_t color;
struct componentsTag
{
uint8_t b;
uint8_t g;
uint8_t r;
} components;
} pixel;
pixel.color = 0x020406;
uint8_t rVal = pixel.components.r; //this will equal 02
uint8_t gVal = pixel.components.g; //this will equal 04
uint8_t bVal = pixel.components.b; //this will equal 06
在此处在线运行此示例:https://onlinegdb.com/H1Sfm6p8E
此处使用类型双关语来允许访问每种颜色的各个值,而不使用C的类型转换。您可能想知道这是如何工作的。在内存中,union占用32位。当color
设置为行pixel.color = 0x020406
时,这些32位将填充值0x00020406
(0x
占用8位后的每对值(8 * 4) = 32位),值从右到左从十六进制数打包,因此06
进入第一个(最低有效)字节,即b
,04
进入第二个字节,即g
,02
进入第三个字节(三个中最重要的)字节,即b
。最重要的字节00
,由于联合没有足够的字节来存储它,所以4字节的十六进制字符串没有任何内容。如果你在结构中添加alpha
作为一个新行,这个值可能是uint8_t alpha
uint8_t r
。
将这32位分成几部分的图表可能如下所示:
------------------------------------
-----| 00 | 02 | 04 | 06 |------
------------------------------------
| uint32_t color |
------------------------------------
但是24位components
struct 也占用相同的内存:即同一空间中的3个最右边的字节(24个最低有效位) /强>
所以现在联盟的内存的完整图表是:
------------------------------------
-----| 00 | 02 | 04 | 06 |------
------------------------------------
| uint32_t color |
------------------------------------
-----| NA | r | g | b |------
------------------------------------
请注意r
,g
和b
的所有重叠color
。访问r
,g
或b
现在可以访问color
的特定部分,这是其中的8位部分。通常将uint32_t转换为uint8_t只会给出uint32_t的最低有效位,因此r
,g
和b
都将成为无意义的数字。但正如我之前所说,这里的联盟被用于打击类型,因此标准定义的转换被规避了。
(Gabriel Staples的主要编辑和更正,2019年3月6日)