【问题标题】:In C, why do some people cast the pointer before freeing it?在 C 语言中,为什么有些人在释放指针之前会强制转换它?
【发布时间】:2016-03-05 08:24:51
【问题描述】:

我正在处理旧代码库,几乎每次调用 free() 都会对其参数进行强制转换。例如,

free((float *)velocity);
free((float *)acceleration);
free((char *)label);

其中每个指针都是对应的(和匹配的)类型。我认为这样做毫无意义。这是非常古老的代码,所以我想知道它是否是 K&R 的东西。如果是这样,我实际上希望支持可能需要这个的旧编译器,所以我不想删除它们。

使用这些演员表是否有技术原因?我什至看不到使用它们的实用理由。在释放数据类型之前提醒自己有什么意义?

编辑:这个问题不是另一个问题的重复。另一个问题是这个问题的一个特例,我认为如果接近的选民会阅读所有答案,这是显而易见的。

Colophon:我给“const answer”打勾是因为这是可能需要这样做的真正原因;但是,关于它是 ANSI C 之前的自定义(至少在某些程序员中)的答案似乎是在我的案例中使用它的原因。这里有很多人的很多优点。感谢您的贡献。

【问题讨论】:

  • “在释放它之前提醒我们自己的数据类型有什么意义?”也许想知道将释放多少内存?
  • @Codor 编译器不执行释放,操作系统执行。
  • @m0skit0 "也许知道将释放多少内存?" 不必知道要释放多少类型。仅仅因为这个原因,演员就是糟糕的编码。
  • @m0skit0 为了可读性而进行强制转换总是不好的编码,因为强制转换会改变类型的解释方式,并且可能会隐藏严重的错误。当需要可读性时,cmets 更好。
  • 在恐龙在地球上行走,写编程书籍的古代,我相信在标准C语言中没有void*,而只有char*。因此,如果您的考古发现揭示了将参数转换为 free() 的代码,我相信它必须要么来自那个时期,要么是那个时期的生物编写的。不过我找不到任何来源,所以我不会回答。

标签: c pointers casting


【解决方案1】:

如果指针为const,则可能需要强制转换来解决编译器警告。下面是一个导致警告但不强制转换 free 参数的代码示例:

const float* velocity = malloc(2*sizeof(float));
free(velocity);

编译器(gcc 4.8.3)说:

main.c: In function ‘main’:
main.c:9:5: warning: passing argument 1 of ‘free’ discards ‘const’ qualifier from pointer target type [enabled by default]
     free(velocity);
     ^
In file included from main.c:2:0:
/usr/include/stdlib.h:482:13: note: expected ‘void *’ but argument is of type ‘const float *’
 extern void free (void *__ptr) __THROW;

如果您使用free((float*) velocity);,编译器将停止抱怨。

【讨论】:

  • @m0skit0 这并不能解释为什么有人会在释放之前投向float*。我用 gcc 4.8.3 尝试了free((void *)velocity);。当然它不适用于古老的编译器
  • 但是为什么需要动态分配常量内存呢?你永远不能使用它!
  • @Nils_M 这是一个简单的例子来说明问题。我在函数中的实际代码中所做的是分配非常量内存,分配值,转换为 const 指针并返回它。现在,有一个指向预先分配的 const 内存的指针,有人必须释放它。
  • Example:“这些子例程返回新分配的内存中的字符串,由 *stringValueP 指向,您最终必须释放该字符串。有时,用于释放内存的 OS 函数被声明为将指向非常量的指针作为其参数,因为 *stringValueP 是指向 const 的指针。”
  • 错误,如果函数将const char *p 作为参数然后释放它,正确的做法是不要在调用free 之前将p 转换为char*。首先不要将其声明为采用const char *p,因为它修改 *p 并应相应地声明。 (如果它使用 const 指针而不是指向 const 的指针 int *const p,则您不需要进行强制转换,因为它实际上是合法的,因此在没有强制转换的情况下也可以正常工作。)
【解决方案2】:

预标准 C 没有 void* 而只有 char*,所以你必须强制转换所有传递的参数。如果您遇到古老的 C 代码,您可能会因此找到这样的转换。

Similar question with references.

当第一个 C 标准发布时,malloc 和 free 的原型从 char* 变为他们今天仍然拥有的 void*

当然,在标准 C 中,这样的转换是多余的,只会损害可读性。

【讨论】:

  • 但是你为什么要把free的参数转换成它已经是的类型呢?
  • @chux 预标准的问题在于:没有任何义务。人们只是指着 K&R 的书寻找佳能,因为那是他们唯一拥有的东西。正如我们从 K&R 第 2 版中的几个示例中看到的那样,K&R 自己对如何将参数强制转换为 free 在标准 C 中工作感到困惑(您不需要强制转换)。我还没有读过第一版,所以我不知道他们是否在 80 年代的准标准时代也感到困惑。
  • Pre-standard C 没有void*,但它也没有函数原型,因此即使在 K&R 中也不需要转换free 的参数(假设所有数据指针类型使用相同的表示)。
  • 由于 cmets 中已经说明的多种原因,我认为这个答案没有意义。
  • 我看不出这个答案会如何真正回答任何相关的问题。最初的问题涉及转换为其他类型,而不仅仅是char *。没有void 的旧编译器会有什么意义?这样的演员阵容会取得什么成就?
【解决方案3】:

下面是一个例子,如果没有强制转换,free 会失败:

volatile int* p = (volatile int*)malloc(5 * sizeof(int));
free(p);        // fail: warning C4090: 'function' : different 'volatile' qualifiers
free((int*)p);  // success :)
free((void*)p); // success :)

在 C 语言中,您会收到警告(在 VS2012 中收到警告)。在 C++ 中你会得到一个错误。

抛开罕见的情况不谈,强制转换只会使代码膨胀...

编辑: 我投给void* 而不是int* 来演示失败。它将与 int* 一样工作,将隐式转换为 void*。添加int*代码。

【讨论】:

  • 请注意,在问题中发布的代码中,演员表不是void *,而是float *char *。这些演员表不仅无关紧要,而且是错误的。
  • 问题其实是相反的。
  • 我不明白答案; free(p) 在什么意义上会失败?它会给出编译器错误吗?
  • 这些都是好点。显然,const 限定符指针也是如此。
  • volatile 自从 C 标准化以来就已经存在,如果不是更长的话。它在 C99 中添加。
【解决方案4】:

旧原因:1. 通过使用free((sometype*) ptr),代码明确说明了指针应被视为free() 调用的一部分的类型。当free() 被替换为(自己动手)DIY_free() 时,显式转换很有用。

#define free(ptr) DIY_free(ptr, sizeof (*ptr))

DIY_free() 是(是)一种方法,尤其是在调试模式下,可以对被释放的指针进行运行时分析。这通常与DIY_malloc() 配对,以添加句子、全局内存使用计数等。我的小组在更现代的工具出现之前使用了这种技术多年。它要求被释放的项目被强制转换为最初分配的类型。

  1. 考虑到要花费大量时间来跟踪内存问题等,诸如强制类型转换之类的小技巧将有助于搜索和缩小调试范围。

现代:避免constvolatile 警告,如Manos Nikolaidis@@egur 所述。我想我会注意到 3 个限定符的效果:constvolatilerestrict

[编辑] 为 @R.. comment 添加了 char * restrict *rp2

void free_test(const char *cp, volatile char *vp, char * restrict rp, 
    char * restrict *rp2) {
  free(cp);  // warning
  free(vp);  // warning
  free(rp);  // OK
  free(rp2);  // warning
}

int main(void) {
  free_test(0,0,0,0);
  return 0;
}

【讨论】:

  • restrict 只是一个非问题,因为它的放置位置 - 它影响对象 rp 而不是指向的类型。如果您改为使用char *restrict *rp,那就很重要了。
【解决方案5】:

这是另一个替代假设。

我们被告知该程序是在 C89 之前编写的,这意味着它无法解决与 free 原型不匹配的问题,因为不仅没有 const 或 @ 这样的东西987654323@ 在 C89 之前,C89 之前没有 函数原型 这样的东西。 stdlib.h 本身是委员会的发明。如果系统头文件根本不想声明free,他们会这样做:

extern free();  /* no `void` return type either! */

现在,这里的关键点是函数原型的缺失意味着编译器没有进行参数类型检查。它应用了默认的参数提升(仍然适用于可变参数函数调用的相同),就是这样。使每个调用点的参数符合被调用者的期望的责任完全在于程序员。

然而,这并不意味着在大多数 K&R 编译器上必须将参数强制转换为 free。像

这样的函数
free_stuff(a, b, c)
    float *a;
    char *b;
    int *c;
{
    free(a);
    free(b);
    free(c);
}

应该已经正确编译。所以我认为我们这里有一个程序来处理一个不寻常的环境中的错误编译器:例如,sizeof(float *) > sizeof(int) 和编译器不会使用适当调用的环境指针的约定,除非您在调用时强制转换它们。

我不知道有任何这样的环境,但这并不意味着没有。想到的最有可能的候选者是 1980 年代初期用于 8 位和 16 位微处理器的精简“微型 C”编译器。得知早期的 Cray 有这样的问题,我也不会感到惊讶。

【讨论】:

  • 前半部分我完全同意。后半部分是一个有趣且似是而非的猜想。
  • “我不知道有任何这样的环境,但这并不意味着没有。” - x86_64 是 sizeof(float *) > sizeof(int) 的位置之一,尽管我认为 x86_64 的 K&R 编译器/代码并不多。
  • 更大的问题是指向不同类型的指针具有不同大小或表示的平台,例如使用一个单词保存的单词地址表示int*,但使用两个单词表示char*。传递 free() 一个与 void*char* 不兼容的指针的代码需要强制转换或原型才能正常工作。
【解决方案6】:

free 只接受非常量指针作为参数。因此,对于 const 指针,需要显式转换为非 const 指针。

Unable to free const pointers in C

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-06
    • 1970-01-01
    • 1970-01-01
    • 2021-06-04
    • 2020-11-22
    • 2011-12-16
    • 2020-12-10
    相关资源
    最近更新 更多