哈佛架构平台上的NULL指针问题

时间:2010-10-08 10:07:54

标签: c null harvard-architecture

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

我们在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的显式检查。它们中的大多数将是数据指针,但其中一些将是函数指针。 != NULL== NULL的快速grep显示超过3000个结果,许多人需要手动检查。

所以,我们现在想要的是

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

  2. 以一种正确的方式重新定义NULL。

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

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

    有任何想法或建议吗?

6 个答案:

答案 0 :(得分:18)

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

这是(2)将NULL重新定义为普通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为(以及其他)指针操作数和空指针常量定义==!=操作数。对于这些:“如果一个操作数是指针而另一个是a 空指针常量,空指针常量转换为指针的类型。“

结论:在fp == (void *) 0中,空指针常量转换为fp的类型。这个空指针可以与fp进行比较,如果它指向一个函数,则保证不等于fp。赋值(=)有一个类似的子句,因此fp = (void *) 0;也是明确定义的C。

答案 1 :(得分:3)

你可以试试这个:

#ifdef NULL
  #undef NULL
#endif
#define NULL 0

答案 2 :(得分:3)

您描述的方式应该有效:

  

6.3.2.3/3值为0的整型常量表达式,或者类型为void *的表达式,称为空指针常量。如果将空指针常量转换为指针类型,则保证将结果指针(称为空指针)与指向任何对象或函数的指针进行比较。

那么,NULL是否被重新定义为非0(void*)0(或等效?)或者您的编译器不符合。

尝试在所有文件中的所有#include之后自己重新定义NULL(对于0}: - )

仅用于踢:在问题文件上尝试gcc -E(输出预处理的源)并检查NULL的扩展

答案 3 :(得分:3)

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

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

答案 4 :(得分:2)

以下是一些建议:

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

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

答案 5 :(得分:1)

编辑实现的系统标题以替换

的所有出现
#define NULL ((void *)0)

#define NULL 0

然后向供应商提交错误报告。由于供应商编译器中存在错误,您不必修改(完全正确,尽管是丑陋的样式)代码。