联合与无效指针

use*_*033 12 c unions

使用简单的void*而不是union的区别是什么?例:

struct my_struct {
    short datatype;
    void *data;
}

struct my_struct {
    short datatype;
    union {
        char* c;
        int* i;
        long* l;
    };
};
Run Code Online (Sandbox Code Playgroud)

这两个都可以用来完成完全相同的事情,虽然使用union或void*更好吗?

Pat*_*ter 16

我的库中确实存在这种情况.我们有一个通用的字符串映射模块,可以使用不同的索引大小,8位,16位或32位(出于历史原因).所以代码中充满了代码:

if(map->idxSiz == 1) 
   return ((BYTE *)map->idx)[Pos] = ...whatever
else
   if(map->idxSiz == 2) 
     return ((WORD *)map->idx)[Pos] = ...whatever
   else
     return ((LONG *)map->idx)[Pos] = ...whatever
Run Code Online (Sandbox Code Playgroud)

这样有100条线.在第一步中,我将其更改为联合,我发现它更具可读性.

switch(map->idxSiz) {
  case 1: return map->idx.u8[Pos] = ...whatever
  case 2: return map->idx.u16[Pos] = ...whatever
  case 3: return map->idx.u32[Pos] = ...whatever
}
Run Code Online (Sandbox Code Playgroud)

这让我更好地了解发生了什么,然后我可以决定仅使用32位索引完全删除idxSiz变体.但只有在代码更具可读性后才能实现这一点.PS:这只是我们项目的一小部分,大约是由不再存在的人编写的几十万行代码.所以代码的变化是渐进的,以免破坏应用程序.

结论:即使人们不太习惯联合变体,我更喜欢它,因为它可以使代码更轻松地阅读.在大型项目中,使代码更具可读性非常重要,即使您自己稍后会阅读它.

编辑:添加了评论,因为评论不格式化代码:

之前改变了切换(现在这是真正的代码)

switch(this->IdxSiz) { 
  case 2: ((uint16_t*)this->iSort)[Pos-1] = (uint16_t)this->header.nUz; break; 
  case 4: ((uint32_t*)this->iSort)[Pos-1] = this->header.nUz; break; 
}
Run Code Online (Sandbox Code Playgroud)

被改为

switch(this->IdxSiz) { 
  case 2: this->iSort.u16[Pos-1] = this->header.nUz; break; 
  case 4: this->iSort.u32[Pos-1] = this->header.nUz; break; 
}
Run Code Online (Sandbox Code Playgroud)

我不应该把我在代码中做的所有美化结合在一起,只显示那一步.但我在家里发布了我无法访问代码的答案

  • 我认为可读性来自于使用开关而不是嵌套ifs. (6认同)

swe*_*egi 11

在我看来,void指针和显式转换是更好的方法,因为对于每个经验丰富的C程序员来说,意图是显而易见的.

编辑澄清:如果我在一个程序中看到所述联合,我会问自己作者是否想要限制存储数据的类型.也许执行一些健全性检查,这些检查仅对整数类型有意义.但是如果我看到一个void指针,我直接知道作者设计的数据结构可以保存任意数据.因此,我也可以将它用于新引入的结构类型.请注意,我可能无法更改原始代码,例如,如果它是第三方库的一部分.

  • 只是好奇,联盟版本的意图怎么样不那么明确? (3认同)

Dig*_*oss 7

使用union来保存实际对象而不是指针更常见.

我认为我尊重的大多数C开发人员都不愿意将不同的指针结合在一起; 如果需要一个通用指针,那么void *肯定使用"C路".这种语言牺牲了很多安全性,以便你有意识地对事物的类型进行别名; 考虑到我们为此功能付出的代价,我们不妨在简化代码时使用它.这就是为什么逃避严格打字一直存在的原因.


Jer*_*fin 5

union方法要求您事先知道可能使用的所有类型.该void *方法允许存储在编写相关代码时可能甚至不存在的数据类型(尽管这种未知数据类型做很多事情可能很棘手,例如要求将指针传递给要在该数据上调用的函数而不是能够直接处理它).

编辑:由于似乎有一些关于如何使用未知数据类型的误解:在大多数情况下,您提供某种"注册"功能.在典型的情况下,您将指针传递给可以执行所存储项目所需的所有操作的函数.它生成并返回一个新索引,用于标识类型的值.然后,当您想要存储该类型的对象时,将其标识符设置为从注册中返回的值,并且当使用对象的代码需要对该对象执行某些操作时,它将通过以下方法调用相应的函数.你传入的指针.在一个典型的例子中,那些指向函数的指针将在a中struct,它只是存储(指向)数组中的那些结构.它从注册返回的标识符值只是它存储了这个特定结构的那些结构的数组的索引.