在C语言中,为什么有人在释放指针之前将其转换?


167

我正在使用旧的代码库,几乎所有对free()的调用都在其参数上使用了强制类型转换。例如,

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

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

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

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

Colophon:我给“常量答案”打了勾号,因为这是可能需要这样做的真实理由。但是,关于它是ANSI C之前的自定义(至少在某些程序员中)的答案似乎是在我的情况下使用它的原因。许多人在这里有很多优点。感谢你的贡献。


13
“在释放数据之前提醒自己数据类型有什么意义?” 也许知道要释放多少内存?
m0skit0

12
@Codor编译器不执行解除分配,而操作系统执行。
m0skit0

20
@ m0skit0 “也许知道要释放多少内存?” 不必知道要释放多少类型。仅出于这个原因进行转换是错误的编码。
user694733

9
@ m0skit0出于可读性考虑,强制转换始终是不好的编码,因为强制转换会更改类型的解释方式,并且可能隐藏严重的错误。需要可读性时,注释会更好。
user694733

66
在远古时代恐龙走过大地,写编程书籍,我相信没有void*预标准C,但只char*。因此,如果您的考古发现揭示了将参数强制转换为free()的代码,那么我相信它必须是从那个时间段开始的,或者是那个时候的生物所写的。不过,我找不到任何来源,因此请不要回答。
伦丁

Answers:


171

如果指针为,则可能需要强制转换以解决编译器警告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);编译器,则停止抱怨。


2
@ m0skit0不能解释为什么有人float*在释放之前会强制转换为。我尝试free((void *)velocity);使用gcc 4.8.3。当然,它不会有一个古老的编译工作
马诺斯斯尼古拉迪斯

54
但是,为什么需要动态分配常量内存呢?您永远无法使用它!
Nils_M 2015年

33
@Nils_M这是一个简单的例子。我在函数的实际代码中所做的工作是分配非常量内存,分配值,强制转换为const指针并返回它。现在,有一个指向预先分配的const内存的指针,有人必须释放它。
Manos Nikolaidis

2
示例:“这些子例程将在* stringValueP所指向的新分配的内存中返回字符串,您最终必须释放该字符串。有时,您用来释放内存的OS函数被声明为使用指向非常量对象的指针作为其参数,因此,因为* stringValueP是指向const的指针。”
卡斯顿S'1

3
错了,如果一个函数需要const char *p作为一个参数,然后将其释放,做正确的事情是不是投pchar*调用free之前。请勿将其声明为const char *p第一位,因为它会进行修改 *p,因此应相应声明。(如果使用const指针而不是const指针int *const p,则不需要强制转换,因为它实际上是合法的,因此无需
Ray

59

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

类似的问题与参考

当第一个C标准发布时,malloc和free的原型从原来的 char*变成了void*今天。

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


23
但是,为什么要将参数转换free为已经存在的类型呢?
jwodder 2015年

4
@chux预标准的问题在于:没有任何义务。人们只是指着佳能的K&R书,因为那是他们唯一的东西。从K&R第二版的几个示例中可以看出,K&R本身对参数强制转换如何free在标准C中工作感到困惑(您不必强制转换)。我还没有看过第一版,所以我也无法确定它们是否在80年代的标准前时代也感到困惑。
伦丁2015年

7
标准C没有void*,但也没有函数原型,因此free即使在K&R中,也不需要强制转换参数(假设所有数据指针类型都使用相同的表示形式)。
伊恩·阿伯特

6
由于评论中已经提到的多种原因,我认为这个答案没有道理。
R .. GitHub停止帮助ICE

4
我不知道这个答案会如何真正回答任何相关问题。最初的问题涉及对其他类型的强制转换,而不仅仅是对char *。没有旧的编译器会产生什么意义void?这样的演员会达到什么目的?
AnT

34

这是一个例子,其中如果没有强制转换,免费将失败:

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*代码。


请注意,在问题中发布的代码中,强制类型转换不是to void *,而是to float *char *。这些演员不仅是无关紧要的,而且是错误的。
Andrew Henle

1
问题实际上是相反的。
m0skit0

1
我不明白答案。在什么意义上会free(p)失败?它会产生编译器错误吗?
Codor)2015年

1
这些都是好点。显然,const限定符指针也是如此。
伦丁2015年

2
volatile自从C标准化(甚至不再存在)以来,就已经存在。它不是在C99中添加的。
R .. GitHub停止帮助ICE

30

古老的原因:1.通过使用free((sometype*) ptr),代码明确了有关指针类型的信息,应将指针视为free()调用的一部分。显式强制转换在用free()(自己动手)替换时很有用DIY_free()

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

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

  1. 考虑到要花费大量时间来跟踪内存问题,等等,像释放类型这样的小技巧将有助于搜索并缩小调试范围。

现代:Manos Nikolaidis @@egur提出的避免constvolatile警告。以为我会注意3场的效果预选赛:,,和。constvolatilerestrict

[编辑] char * restrict *rp2每个@R ..评论添加

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;
}

3
restrict只是因为放置位置而没有问题-它会影响对象rp而不是指向类型。如果您改为使用char *restrict *rp,那将很重要。
R .. GitHub停止帮助ICE

16

这是另一种假设。

我们被告知该程序是在C89之前编写的,这意味着它不能与的原型不匹配free,因为不仅不存在C89之前的事物,const也不void *存在C89之前的事物,C89之前的功能原型stdlib.h本身就是委员会的发明。如果系统头文件free根本不愿意声明,则它们将按照以下方式进行操作:

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

现在,关键是缺少函数原型意味着编译器没有进行参数类型检查。它应用了默认参数提升(仍然适用于可变参数调用)。使每个呼叫站点的参数与被呼叫者的期望一致的责任完全由程序员承担。

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

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”编译器。得知早期的Crays遇到这样的问题,我也不会感到惊讶。


1
上半年我完全同意。第二部分是一个有趣而合理的猜想。
chux-恢复莫妮卡

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.