【问题标题】:Does C have a generic "pointer to a pointer" type?C 是否具有通用的“指向指针的指针”类型?
【发布时间】:2012-06-08 17:56:03
【问题描述】:

例如,如果我想编写一个使指针为空的“免费”,我可以编写如下内容:

void myfree(void **data) {
    free(*data);
    *data = NULL;
}

但是,当我尝试编写此代码时,我收到编译器警告(来自 gcc 4.6.2):warning: passing argument 1 of ‘myfree’ from incompatible pointer type [enabled by default] ... note: expected ‘void **’ but argument is of type ‘char **‘(在这种情况下,我正在释放一个 char 数组)。

似乎void* 是特殊情况以避免此类警告,因为callocfree 等不会触发此类警告,但void** 不会(鉴于上述情况)。唯一的解决方案是显式转换,还是我误解了什么?

[我正在重新审视最近一个项目中的一些痛点,想知道如何才能更好地处理它们,所以我在探讨一些极端情况,因此今天提出了 C 问题。]

更新 鉴于void* 是特殊情况,我可以使用void* 来解决这个问题并在myfree 中强制转换,但这将是一个有点不负责任的解决方案,因为每个人和他们的狗都是将指针传递给看起来像free 的东西,所以我需要某种基于“间接程度”的编译器警告,这是一个实用的解决方案。因此出现了通用“指向指针的指针”的想法。

【问题讨论】:

  • 通过时投到void**
  • 是的,但它丑陋+凌乱,尤其是当free 不需要它时。因此想知道是否有更好的东西。
  • 顺便说一句,IMO 在释放指针时不应该例行地将指针设置为 null。有些情况您确实需要,有些情况您不能(例如,如果指针本身是const)。其他人认为始终设置为 null 非常重要,但我认为他们应该以不同的方式设计代码,以便一开始就减少悬空指针。大多数(不是全部)时间,您在释放包含它的结构之前释放一个指针,或者就在指针本身超出范围之前释放它。所以没有意义将它归零:它已经消失了。

标签: c pointers void-pointers


【解决方案1】:

从技术上讲,该标准允许不同的对象指针类型具有不同的表示形式(甚至是不同的大小),尽管要求 char*void* 具有相同的表示形式。但下面是UB:

int *ip = 0;
free(*(void**)(&ip));

仅仅是因为ip 的内存不必与void* 的内存大小相同,即使它是int* 类型的空指针的位模式也不必与void* 类型的空指针的位模式。如果它们不同,那么当您将 int* 转换为 void* 或返回时,编译器当然必须插入代码以在它们之间进行转换。

实际上,实现不会对您这样做(例如 Posix 就禁止这样做)。

更重要的是,严格的别名规则不允许您使用void* 类型的左值访问char* 对象。所以在实践中,对指针表示的担忧不会破坏你的代码,但优化器实际上可能会。基本上,如果函数调用 myfree((void**)(&p)) 被内联,那么编译器可能会看到:

char *p = <something>;
void **data = (void**)(&p);
free(*data);
*data = NULL;
// code that reads p

允许优化器注意到*data = NULL 正在设置void* 类型的对象,而“读取p 的代码”正在读取char* 类型的对象,禁止与其他, void* 那边的对象。因此可以重新排序指令,完全消除*data = NULL;,或者可能其他我没有想到的事情会毁了你的一天,但如果你没有违反规则,这会加速代码。

【讨论】:

  • UB?这是什么意思(对不起)?
  • 未定义的行为。这意味着标准不关心发生了什么。实现可以做任何他们喜欢做的事情,而且他们所做的任何事情通常都是偶然的,因此是令人不快和/或不一致的。
  • 好的,谢谢,我需要考虑一下。现在我不清楚其中一些是如何未定义的,但 stdlib 使用 void* 可以很好地管理。
  • 您可以将任何对象指针类型转换void*,但这种转换可能(标准允许)花费时间和精力,并且涉及更改位-图案。因此,您不能(不允许)只读取另一个指针类型,就好像它是 void*。同样,您可以将int 转换为double,但不能将int 视为double
  • 严格的别名规则的原因是允许潜在的别名确实会减慢程序的速度,即使实际上没有发生别名。因此,允许编译器假设没有别名发生的规则可以加速程序。不过,这是一种权衡,因为有时您想为不同的类型设置别名,但语言不允许您这样做。
【解决方案2】:

您可以使用 MACRO 来执行此操作。与拥有功能相比,这将非常棒;我希望你知道使用 MACRO 的好处。

#define FREE_IF_NOT_NULL(x) if (x != NULL) { \
                                            free(x); \
                                            x = NULL; \
                                       }

【讨论】:

  • free() 是空安全的 - if 是多余的;等效定义为((void)(free(x), x = NULL))
  • 希望你知道使用宏的坏处。
猜你喜欢
  • 1970-01-01
  • 2011-04-24
  • 1970-01-01
  • 1970-01-01
  • 2021-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-30
相关资源
最近更新 更多