将 void 指针传递给接受非 void 指针的函数是否安全?

Is it safe to pass a void pointer to function that accepts a non-void pointer?

很简单,下面的代码是safe/portable?

#include <stdio.h>
#include <stdlib.h>

int add(int *a, int *b)
{
  return *a + *b;
}

int main()
{
  int x = 2;
  int y = 3;

  void *ptr1 = &x;
  void *ptr2 = &y;

  fprintf(stdout, "%d + %d = %d\n", x, y, add(ptr1, ptr2));

  return EXIT_SUCCESS;
}

我用 -Wall -Werror-Wextra 编译了这个并且没有收到任何警告;似乎 运行 没问题。

没关系,但你的牙齿皮肤。

您正在将 int* 转换为 void*,并且当指针按值传递到 add.

时,它会转换回 int*

例如,如果 add 将两个指针指向 double,那么您的代码的行为将是未定义的。

安全:

C99

A pointer to void may be converted to or from a pointer to any incomplete or object type. A pointer to any incomplete or object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

但是你需要确保你原来的指针类型是正确的。

有两点需要考虑:

  1. C 允许从 void 指针到任何其他对象指针类型的隐式转换。所以传递这些参数在语法上是可以的。

  2. 指向的实际对象的类型,以及函数期望的指针类型,必须满足strict aliasing约束,否则你的程序处于未定义行为状态。

这两点你都可以。所以你的程序完全没问题。

它 "safe" 在于行为定义明确。当需要处理许多不同类型的数据时,将函数声明为接受 void * 是正常的;例如memcpy.

道德上不安全。通过传递 void *,您使编译器无法检查您传递的指针是否指向以您期望的形式和数量保存数据的内存。然后由您来执行此操作。

在您的简单示例中,您可以看到对象实际上是 int,一切都很好。

然而,一般来说,在我们将 void * 传递给函数的地方,我们还应该传递额外的信息来描述我们正在移交的内存,足够详细,以便函数可以完成它的工作;如果我们有一个只处理一种类型的函数,我们会长期认真地研究使我们传递 void * 值而不是那种类型的设计决策。

为了理智起见(并且,取决于编译器,性能),请考虑将您不写入的参数标记为 const

如果您知道转换为 void * 的指针始终指向一组已知类型(例如 char、short、int 等),则可以创建一个由联合和判别式,并在必要时传递 re-casting。

您可以将它作为指向未定义类型的指针传递,只是为了比 void * 更具体一点,这样就承担了一些责任。