【问题标题】:Why cast free's return value to void?为什么将 free 的返回值转换为 void?
【发布时间】:2020-06-22 20:08:48
【问题描述】:

我正在阅读一本使用 C 语言的书(Programming with POSIX Threads,Butenhof,1997 年),我遇到了以下行:

(void)free(data);

这里,data 只是一个指向已分配结构的指针,

data = malloc(sizeof(my_struct_t));

为什么free 的结果会被强制转换为void

根据我对 C 的理解,这似乎没有意义,原因有两个:

  • free 函数已经返回void
  • 代码没有使用返回值(甚至没有分配给变量)

这本书写于 1997 年。这是某种遗产吗?

作者提到这些示例是在 Digital Unix 4.0d 上运行的,但如果你不打算使用该结果,我仍然无法想象有理由转换函数的结果。

【问题讨论】:

  • 一些可能的解释可以在这里找到:stackoverflow.com/questions/689677/…
  • 出于好奇,您的 C 书出版日期是什么时候? (是哪本书?)如果是在 1995 年之前,那可能有一些理由——标准 C 编译器在那之前并不普遍。如果它在那之后出版并且仍然包含演员表(并且没有解释为什么),请担心它教给你的其他坏习惯。买一本更新的书!
  • @JonathanLeffler 正如我在原帖中提到的,这本书出版于 1997 年,使用的是 UNIX 4.0d。这本书是 David R. Butenhof 的“使用 POSIX 线程编程”。到目前为止,它提供了非常丰富的信息,并且是由 POSIX 线程标准的原始贡献者之一编写的。
  • 上周我一直在使用我的副本——是的,它仍然有用。它写在“无处不在的标准 C”的风口浪尖上(我说的是“大约 1995 年”)。 'UNIX 4.0d' 听起来像 Digital UNIX — Butenhof 工作的地方,前言中确实提到了它。将free() 上的演员视为书中的怪事,您无需效仿。很久以前它是半相关的,但它不再相关了。

标签: c casting free void legacy-code


【解决方案1】:

不需要这个演员表。当时可能不会,因为 C 已经以 C89 的形式标准化了。

如果是这样,那将是由于隐式声明。这通常意味着编写代码的人忘记了#include <stdlib.h> 并且正在使用静态分析器。这不是最好的解决方法,一个更好的主意是改用#include <stdlib.h>。以下是 C89 关于隐式声明的一些措辞:

如果函数调用中带括号的参数列表之前的表达式仅包含一个标识符,并且如果该标识符没有可见的声明,则该标识符被隐式声明,就像在包含函数调用的最里面的块中一样,声明

extern int identifier();

出现了。

但这很奇怪,因为它们也没有转换 malloc 的结果,而且 mallocfree 在同一个头文件中。

这也有可能只是一个错误或以某种方式告诉读者free 没有返回结果。

【讨论】:

  • 语言标准化并不意味着每个人都会立即更新他们的工具链和代码以符合它。老式的“K&R”C 又坚持了 8 年,这似乎是合理的。不过,我同意静态分析工具需要为free 而不是malloc 的演员,这很奇怪。
  • @dan04 你通常使用 malloc 的结果;) 我不喜欢写 (void) printf (...) 之类的东西来阻止编译器发出警告,但是“必须在没有任何警告的情况下编译,即使是愚蠢的”在很多项目中都会发生。
【解决方案2】:

如果我们谈论的是标准的free 函数,那么它的原型是

void free(void *ptr);

因此,演员阵容完全没用。
现在进行一些猜测。

作者可能忘记包含声明此原型的stdlib.h 标头,因此编译器假定它的返回类型为int。现在,在对此代码进行静态分析期间,编译器警告它认为非void 函数的未使用返回值。此类警告通常会通过将演员表添加到 void 来消除。

【讨论】:

  • 但请注意,如果由于推测的原因引入了演员表,那么使用它来消除警告是不正确的。在这种情况下,编译器将赋予free 与实际不同的类型,从而导致调用具有未定义的行为(假设C90 语义,在所有情况下调用未声明的函数并不固有地表现出UB)。在实践中,这可能会导致某些系统出现善意的不当行为。正确的解决方案是为函数提供正确的声明。
  • 值得注意的是,“使用 POSIX 线程编程”中的示例多次未能包含相关的标准头文件。也许这是作者的一些不好的做法,他们可能一直在使用默认包含所有标准库的非标准编译器设置。
【解决方案3】:

这将是一个遗产!

在有 C 标准之前,free() 函数将(隐式地)属于 int 类型——因为还没有可靠的类型 void 让它返回。没有返回值。

当第一次修改代码以使用标准 C 编译器时,它可能不包含 <stdlib.h>(因为它在标准之前不存在)。旧代码会为分配函数(类似于calloc()realloc())编写extern char *malloc();(可能没有extern),并且不需要声明free()。然后代码会将返回值转换为正确的类型——因为至少在某些系统(包括我学习 C 的系统)上这是必需的。

稍后,添加了(void) 演员表以告诉编译器(或者,更有可能是lint)“故意忽略来自free() 的返回值”以避免投诉。但最好添加 <stdlib.h> 并让其声明 extern void free(void *vp); 告诉 lint 或编译器没有可忽略的值。

JFTR:早在 80 年代中期,ICL Perq 最初是基于字的架构,内存位置的char * 地址与指向同一位置的“anything_else 指针”是一个非常不同的数字。以某种方式声明char *malloc() 至关重要;将结果从它转换为任何其他指针类型至关重要。演员阵容实际上改变了 CPU 使用的数字。 (当我们系统上的主内存从 1 MiB 升级到 2 MiB 时,我们也很高兴——因为内核使用了大约 3/4 MiB,这意味着用户程序可以在分页之前使用 1 1/4 MiB 等。)

【讨论】:

  • 我刚刚打开了 K&R,第 1 版的副本,其中包含第 1 页上的 free() 的实现。 177 隐式返回 int.
  • 当然 — void 在标准发布之前已添加到某些系统(可能是 Unix System III),但在编写 K&R 第 1 版时(1978 年),这不是 C 的一部分。没有返回值的函数被声明为没有返回类型(这意味着它返回了int),只要你不使用没有返回的值,就没有问题。 C90 标准必须将这种代码视为有效的——如果不是这样,它就会像标准一样失败。但是 C99 删除了“隐式int”和“隐式函数声明”规则。并非世界上所有的代码都赶上了。
  • Op 声称这本书写于 1997 年。您在这里谈论的是非常早期的准标准“K&R C”,而且似乎根本没有人会写一本关于它的书。据我所知,唯一存在的此类书确实是 K&R 第一版。
  • 如果您认为可以避免使用隐式声明,那么不包含标头是一种常见的(如果是不切实际的)做法,因为人们认为这会减少构建时间。
  • 有人使用(void) 转换为printf()吗??
猜你喜欢
  • 1970-01-01
  • 2018-01-21
  • 1970-01-01
  • 2011-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-24
  • 2016-11-20
相关资源
最近更新 更多