什么是打字?类型惩罚如何与C中的工会一起使用?

时间:2014-08-06 05:33:43

标签: c type-punning

任何人都可以在C中解释什么是类型双关语,并通过简单的示例程序演示何时出现此类问题?

我在许多网站(甚至维基)都进行了搜索,但即便如此,我也无法理解。

1 个答案:

答案 0 :(得分:6)

Punning类型是一个广泛的概念,适用于任何具有类型系统和一点灵活性的语言,因此我将使用维基百科的例子与Berkeley套接字:

From the wikipedia page

  

在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的指针可以自由转换为指向struct sockaddr的指针;此外,两种结构类型共享相同的内存布局。因此,对结构字段my_addr->sin_familymy_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位将填充值0x000204060x占用8位后的每对值(8 * 4) = 32位),值从右到左从十六进制数打包,因此06进入第一个(最低有效)字节,即b04进入第二个字节,即g02进入第三个字节(三个中最重要的)字节,即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  |------
------------------------------------

请注意rgb的所有重叠color。访问rgb现在可以访问color特定部分,这是其中的8位部分。通常将uint32_t转换为uint8_t只会给出uint32_t的最低有效位,因此rgb都将成为无意义的数字。但正如我之前所说,这里的联盟被用于打击类型,因此标准定义的转换被规避了。

(Gabriel Staples的主要编辑和更正,2019年3月6日)