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

3
非常简单,以下代码是否安全/可移植?
#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编译了此代码,没有收到警告,看起来运行正常。

5个回答

4

安全性:

C99

指向void的指针可以转换为任何不完整或对象类型的指针,反之亦然。 任何不完整或对象类型的指针都可以转换为指向void的指针,并再次转换回来;结果应与原始指针相等。

但需要确保您的原始指针类型是正确的。


指针可以转换的事实并不保证生成的指针可以用于访问对象。需要C语言的其他规则,例如C 2011 6.5 7中的规则来说明这一点。此外,1999年的标准已经过去将近20年,并已被撤销。 - Eric Postpischil

4

有两个要考虑的问题:

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

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

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


但是,当然这会关闭编译器的任何类型检查。 - Paul Ogilvie
1
@PaulOgilvie - 考虑到编译器已经允许放宽很多限制了...我个人不认为C语言过于类型安全。不过,这也是它强大的一部分。 - StoryTeller - Unslander Monica
1
是的,C语言就像一把电锯。你可以用它来完成任何任务。它曾经是编写汇编语言的便捷方式,程序员知道编译器会生成什么。现在,它正在变成一个可怕的、半高级语言,带有其UB教条主义。 - Paul Ogilvie
@EricPostpischil - 但这就是重点。对象指针转换是允许的。除非类型对齐,否则无法访问存储的值。不管怎样,这已经不重要了。我选择了另一种方向。 - StoryTeller - Unslander Monica
@StoryTeller:对齐和访问并不是问题,兼容性才是关键。在C标准中,“对齐”和“访问”是具有不同含义的术语。 - Eric Postpischil
@EricPostpischil - 当然,访问是个问题。“一个对象应该被访问其存储值…”。我也使用“对齐”作为修辞手法,而不是正式术语。所以请原谅我的混淆。 - StoryTeller - Unslander Monica

2

这样做勉强可以,但只是侥幸而已。

你正在将一个 int* 转换为 void*,并在指针按值传递到 add 时将其转换回 int*

例如,如果 add 接受两个 double 指针,则您的代码行为将未定义。


0

它是“安全的”,因为行为是定义良好的。将函数声明为接受void *是正常的,当它需要处理许多不同类型的数据时;例如memcpy

它在道德上并不“安全”。通过传递void *,您使编译器无法检查您传递的指针是否指向保持以您期望的形式和数量保存数据的内存。然后由您进行强制执行。

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

但是,在一般情况下,当我们将void *传递给函数时,我们还应传递描述内存详细信息的其他信息,以便函数可以完成其工作;如果我们有一个仅处理一种类型的函数,则会仔细审查使我们传递void *值而不是该类型的设计决策。

出于理智和(根据编译器而定)性能的考虑,请考虑将您未写入的参数标记为const


0
如果您知道您转换为void *的指针总是指向一组已知类型(例如char,short,int等),则可以创建一个结构类型,其中包含联合和辨别式,并在必要时重新进行强制转换,然后将其作为未定义类型的指针传递,仅仅比void *更具体,以此来承担一些责任。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接