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

发布于 2024-09-26 11:48:24 字数 952 浏览 3 评论 0原文

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

我们在哈佛架构嵌入式平台上使用 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. 方法来查找比较函数指针(而不是数据指针)的所有情况(因此我们可以将它们与 FP_NULL 进行比较,我们将其定义为 32 位0),或

  2. < p>以这样的方式重新定义 NULL,使其能够做正确的事情。

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

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

有什么想法或建议吗?

Interesting issue we came across here this week.

We are working in C on a Harvard Architecture embedded platform, which has 16-bit data addresses and 32-bit code addresses.

The issue occurs when you are working with function pointers. If you have code like

if (fp) fp();

or

if (fp != 0) fp();

everything is fine.

However if you have code like

if (fp != NULL) fp();

then, because NULL is defined as (void *) 0, the compiler (gcc in this case) a) does not warn and b) does a 16-bit comparison against your function pointer instead of a 32-bit comparison. Fine as long as your function pointer doesn't happen to lie on a 64k boundary so all bottom 16 bits are 0.

At the moment we have large swathes of code which contain explicit checks against NULL. Most of them will be data pointers, but some of them will be function pointers. A quick grep for != NULL or == NULL revealed over 3000 results, to many to go through manually to check.

So, what we would like now would be either

  1. a way to find all the cases where function pointers (but not data pointers) are compared (so we can instead have them compare against FP_NULL which we would define as a 32-bit 0), or

  2. to redefine NULL in such a way that it would do the right thing.

  3. (Or, I suppose, to
    update our gcc port to detect and
    correctly handle this case).

I can't think of any approach that works for 1. The only approach I can think of for 2 is to redefine NULL as a 0 function pointer, which would be very wasteful for the vast majority of comparisons which are against data pointers. (A 32-bit compare is 4 instructions, a 16-bit compare is 1 instruction).

Any thoughts or suggestions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

余厌 2024-10-03 11:48:24

在我看来,最简单的方法是将所有出现的 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 为(以及其他)指针操作数和 null 定义了 ==!= 操作数对于这些:“如果一个操作数是指针,另一个是操作数
    空指针常量,将空指针常量转换为指针的类型。”

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

It seems to me the easiest way is replace all occurrences of NULL by 0. This works for function pointer (as you say) and object pointers.

This is a variant of (2) Redefine NULL to plain 0.

But the fact that you cannot compare function pointers with NULL is a bug in your implementation. C99 states that comparison of the null pointer constant is possible with both object and function pointers, and that NULL should expand to this constant.

Small addition from the C-FAQ question 5.8:

Q: Is NULL valid for pointers to functions?
A: Yes (but see question 4.13)

Mixing function pointers with (void *) 0

(A reply to R..'s comment). I believe using function pointers and (void *) 0 together is well-defined. In my reasoning I will refer to sections of the C99 draft 1256, but will not quote large parts to keep it readable. It should also be applicable to C89.

  • 6.3.2.3 (3) defines the integer constant expression 0 and such an expressions cast to (void *) as null pointer constant. And: "If a null pointer constant is converted to a
    pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal
    to a pointer to any object or function."
  • 6.8.9 defines the == and != operands for (among other) a pointer operand and a null pointer constant. For these: "If one operand is a pointer and the other is a
    null pointer constant, the null pointer constant is converted to the type of the pointer."

Conclusion: In fp == (void *) 0, the null pointer constant is converted to the type of fp. This null pointer can be compared to fp and is guaranteed to be unequal to fp if it points to a function. Assignment (=) has a similar clause, so fp = (void *) 0; is also well-defined C.

埋情葬爱 2024-10-03 11:48:24

您描述的方式应该有效:

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

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

在所有文件中的所有 #include 后,尝试自己重新定义 NULL(为普通的 0):-)

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

The way you describe should work:

6.3.2.3/3 An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

So, either NULL has been redefined to something not 0 or (void*)0 (or equivalent?) or your compiler is non-conformant.

Try redefining NULL yourself (to plain 0) after all the #includes in all files :-)

just for kicks: try gcc -E (to output the preprocessed source) on a problem file and check the expansion of NULL

风吹雨成花 2024-10-03 11:48:24

你可以试试这个:

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

You could try this:

#ifdef NULL
  #undef NULL
#endif
#define NULL 0
2024-10-03 11:48:24

以下是一些建议:

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

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

Here are a few suggestions:

  1. temporarily change NULL to (char*)0 or something else that is not implicitly convertible to a function pointer. This should give a warning about every comparison with a non-matching pointer. You could then run the produced compiler output through a tool like grep and look for a typical pattern for function pointers, like (*)(.

  2. redefine NULL as 0 (without the cast to void*). This is another valid definition for NULL and might do the right thing for you, but no guarantees.

嗼ふ静 2024-10-03 11:48:24

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

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

You could try a segment hack (really only a hack), so you can use the fast 16bit comparision, without any risk.
Create segements at each n*0x10000 boundary with size of 4 (or even smaller), so there never can't exists a real function.

It depends on your embedded device memory space, if this is a good or a really bad solution.
It could work if you have 1MB normal Flash, which will never change.
It will be painfull if you have 64MB Nand Flash.

云雾 2024-10-03 11:48:24

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

#define NULL ((void *)0)

#define NULL 0

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

Edit the implemention's system headers to replace all occurrances of

#define NULL ((void *)0)

with

#define NULL 0

Then file a bug report with the vendor. You should not have to modify your (perfectly correct, albeit ugly style) code because of a bug in the vendor's compiler.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文