17

本周我们在这里遇到的有趣问题。

我们在哈佛架构嵌入式平台上使用 C 语言工作,该平台具有 16 位数据地址和 32 位代码地址。

当您使用函数指针时会出现此问题。如果你有类似的代码

if (fp) fp();

或者

if (fp != 0) fp();

一切安好。

但是,如果您有类似的代码

if (fp != NULL) fp();

然后,因为NULL定义为(void *) 0,所以编译器(在这种情况下为 gcc)a) 不会发出警告,并且 b) 对您的函数指针进行 16 位比较,而不是 32 位比较。只要您的函数指针不恰好位于 64k 边界上,所以所有底部 16 位都是 0,就可以了。

目前,我们有大量代码,其中包含针对 NULL 的显式检查。它们中的大多数将是数据指针,但其中一些将是函数指针。快速 grep!= NULL== NULL显示 3000 多个结果,许多人需要手动检查。

所以,我们现在想要的是

  1. 一种查找比较函数指针(但不是数据指针)的所有情况的方法(因此我们可以将它们与我们定义为 32 位 0 的 FP_NULL 进行比较),或者

  2. 重新定义 NULL 以使其做正确的事情。

  3. (或者,我想,更新我们的 gcc 端口以检测并正确处理这种情况)。

我想不出任何适用于 1 的方法。我能想到的唯一方法 2 是将 NULL 重新定义为 0 函数指针,这对于绝大多数针对数据指针的比较来说是非常浪费的。(32 位比较是 4 条指令,16 位比较是 1 条指令)。

有什么想法或建议吗?

4

6 回答 6

19

在我看来,最简单的方法是替换所有出现的NULLby 0。这适用于函数指针(如您所说)和对象指针。

这是 (2) 将 NULL 重新定义为 plain 的变体0

但是,您无法将函数指针与之进行比较这一事实是NULL您的实现中的一个错误。C99 声明空指针常量可以与对象和函数指针进行比较,并且 NULL 应该扩展到该常量。

C-FAQ 问题5.8的小补充:

问:NULL 对指向函数的指针有效吗?
答:是的(但请参阅问题4.13

将函数指针与(void *) 0

(对 R.. 评论的回复)。我相信使用函数指针和(void *) 0一起是明确定义的。在我的推理中,我将参考 C99 草案 1256 的部分内容,但不会引用大部分内容以保持可读性。它也应该适用于 C89。

  • 6.3.2.3 (3) 定义整型常量表达式0和这样的表达式转换(void *)空指针常量。并且:“如果将空指针常量转换为指针类型,则生成的指针(称为空指针)保证与指向任何对象或函数的指针进行比较不相等。”
  • 6.8.9为(以及其他)指针操作数和空指针常量定义了==和操作数。!=对于这些:“如果一个操作数是指针而另一个是空指针常量,则空指针常量将转换为指针的类型。”

结论:在fp == (void *) 0中,空指针常量被转换为 的类型fp。如果它指向一个函数,这个空指针可以被比较fp并且保证不等于。fp赋值 ( =) 有一个类似的子句,所以fp = (void *) 0;也是定义良好的 C。

于 2010-10-08T10:25:02.037 回答
4

您描述的方式应该有效:

6.3.2.3/3 值为 0 的整数常量表达式,或转换为 void * 类型的表达式称为空指针常量。如果将空指针常量转换为指针类型,则生成的指针(称为空指针)保证与指向任何对象或函数的指针不相等。

因此,要么 NULL 被重新定义为非0(void*)0(或等效?),要么你的编译器不符合要求。

0在所有文件中的所有#includes之后尝试自己重新定义NULL(到plain ):-)

只是为了踢球:gcc -E在问题文件上尝试(输出预处理的源)并检查 NULL 的扩展

于 2010-10-08T10:33:41.000 回答
3

你可以试试这个:

#ifdef NULL
  #undef NULL
#endif
#define NULL 0
于 2010-10-08T10:31:45.093 回答
3

这里有一些建议:

  1. 暂时将 NULL 更改为(char*)0或其他不可隐式转换为函数指针的内容。这应该对每次与不匹配指针的比较发出警告。然后,您可以通过 grep 之类的工具运行生成的编译器输出,并查找函数指针的典型模式,例如(*)(.

  2. 将 NULL 重新定义为0(不强制转换为 void*)。这是 NULL 的另一个有效定义,可能会为您做正确的事情,但不能保证。

于 2010-10-08T10:36:28.657 回答
3

您可以尝试分段破解(实际上只是破解),因此您可以使用快速的 16 位比较,没有任何风险。在每个 n*0x10000 边界处创建大小为 4(甚至更小)的分段,因此永远不存在真正的函数。

这取决于您的嵌入式设备内存空间,这是一个好的解决方案还是一个非常糟糕的解决方案。如果你有 1MB 的普通 Flash,它就可以工作,这永远不会改变。如果你有 64MB 的 Nand Flash 会很痛苦。

于 2010-10-08T11:11:41.953 回答
2

编辑实现的系统标头以替换所有出现的

#define NULL ((void *)0)

#define NULL 0

然后向供应商提交错误报告。您不应该因为供应商的编译器中的错误而修改您的(完全正确,尽管风格丑陋)的代码。

于 2010-10-08T15:10:15.507 回答