64位系统上的NULL定义问题
我正在使用 gcc 4.1.2 在 RHEL 5.1 64 位平台上运行。
我有一个实用函数:
void str_concat(char *buff, int buffSize, ...);
它连接在可变参数列表(...)中传递的 char * ,而最后一个参数应该为 NULL,以指定参数的结尾。在 64 位系统上 NULL 是 8 个字节。
现在来说说问题。我的应用程序直接/间接包含 2 个 stddef.h 文件。
第一个是 /usr/include/linux/stddef.h ,它定义 NULL 如下:
#undef NULL
#if defined(__cplusplus)
#define NULL 0
#else
#define NULL ((void *)0)
#endif
第二个是 /usr/lib/gcc/x86_64-redhat-linux/4.1.2/ include/stddef.h
#if defined (_STDDEF_H) || defined (__need_NULL)
#undef NULL /* in case <stdio.h> has defined it. */
#ifdef __GNUG__
#define NULL __null
#else /* G++ */
#ifndef __cplusplus
#define NULL ((void *)0)
#else /* C++ */
#define NULL 0
#endif /* C++ */
#endif /* G++ */
#endif /* NULL not defined and <stddef.h> or need NULL. */
#undef __need_NULL
当然我需要第二个,因为它将 NULL 定义为 __null (8 字节),而第一个将其定义为整数 0 (4 字节)。
如何防止 /usr/include/linux/stddef.h 被间接包含?
UPD:
编译行非常简单:
g++ -Wall -fmessage-length=0 -g -pthread
许多人建议通过 (void *)0。这当然会起作用。问题在于该函数被用在很多地方,我的意思是很多地方。我想找到能够满足 C++ 标准承诺的解决方案 - 8 字节大小的 NULL。
I'm running on RHEL 5.1 64 bit platfrom using gcc 4.1.2.
I have a utility function:
void str_concat(char *buff, int buffSize, ...);
which concats char * passed in variadic list(...), while last argument should be NULL, to designate end of the arguments. On 64 bit system NULL is 8 bytes.
Now to the problem. My application includes directly/indirectly 2 stddef.h files.
First one is /usr/include/linux/stddef.h which defines NULL as following:
#undef NULL
#if defined(__cplusplus)
#define NULL 0
#else
#define NULL ((void *)0)
#endif
The second one is /usr/lib/gcc/x86_64-redhat-linux/4.1.2/include/stddef.h
#if defined (_STDDEF_H) || defined (__need_NULL)
#undef NULL /* in case <stdio.h> has defined it. */
#ifdef __GNUG__
#define NULL __null
#else /* G++ */
#ifndef __cplusplus
#define NULL ((void *)0)
#else /* C++ */
#define NULL 0
#endif /* C++ */
#endif /* G++ */
#endif /* NULL not defined and <stddef.h> or need NULL. */
#undef __need_NULL
Of course I need the 2nd one, since it defines NULL as __null (8 bytes), while 1st one defines it as integer 0 (4 bytes).
How do I prevent /usr/include/linux/stddef.h to be inderectly included?
UPD:
Compilation line is pretty straightforward:
g++ -Wall -fmessage-length=0 -g -pthread
Many of you advised to pass (void *)0. This of course will work. The problem that the function is used in many, I mean many places. I'd like to find solution that will give me what C++ standard promises - NULL of 8 byte size.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
在这种情况下不存在“NULL 定义问题”。您尝试在代码中使用
NULL
的方式存在问题。NULL
本身无法可移植地传递给 C/C++ 中的可变参数函数。您必须在传递之前显式转换它,即在您的情况下,您必须传递(const char*) NULL
作为参数列表的终止符。您的问题被标记为 C++。无论如何,无论大小如何,在 C++ 中
NULL
将始终被定义为整数常量。在 C++ 中将NULL
定义为指针是非法的。由于您的函数需要一个指针 (const char *
),因此在 C++ 代码中,任何NULL
的定义都不适用于它。为了使代码更简洁,您可以定义自己的常量,例如
并在函数调用中使用它。但您永远无法仅使用
NULL
来实现此目的。每当将普通的NULL
作为可变参数传递时,这就是一个必须修复的公然的可移植性错误。添加:您的更新声称“C++ 标准承诺 8 字节大小的
NULL
”(我猜是在 64 位平台上)。这没有任何意义。 C++ 标准不承诺任何关于NULL
的事情。NULL
旨在用作右值。它没有具体的大小,并且在其实际大小可能有一点影响的情况下也无法有效使用 NULL。引用 ISO/IEC 14882:1998,第 18.1 节“类型”,第 4 段:
There's no "NULL definiton problem" in this case. There's a problem with how you are trying to use
NULL
in your code.NULL
cannot be portably passed to variadic functions in C/C++ by itself. You have to explicitly cast it before passing, i.e. in your case you have to pass(const char*) NULL
as the terminator of the argument list.Your question is tagged as C++. In any case, regardless of size, in C++
NULL
will always be defined as an integer constant. It is illegal in C++ to defineNULL
as a pointer. Since your function expects a pointer (const char *
), no definition ofNULL
will ever work for it in C++ code.For cleaner code you can define your own constant, like
and use it in the calls to your function. But you will never be able to meaningfully use just
NULL
for that purpose. Whenever a plainNULL
is passed as a variadic argument, it is a blatant portability bug that has to be fixed.Added: your update claims that "C++ standard promises
NULL
of 8 byte size" (on a 64-bit platform I presume). This just doesn't make any sense. C++ standard does not promise anything like that aboutNULL
.NULL
is intended to be used as an rvalue. It has no specific size and there's no valid use ofNULL
where its actual size might even remotely matter.Quoting from ISO/IEC 14882:1998, section 18.1 'Types', paragraph 4:
一种解决方案 - 甚至可能是最好的,但肯定非常可靠 - 是将显式空字符指针传递给函数调用:
或者:
这是 POSIX 系统中
execl()
函数的标准推荐做法,例如,出于完全相同的原因 - 可变长度参数列表的尾随参数会受到通常的提升(char 或 Short 到 int;float 到 double),但不能是类型安全的。这也是为什么 C++ 从业者通常避免可变长度参数列表;它们不是类型安全的。
One solution - possibly even the best, but certainly very reliable - is to pass an explicit null char pointer to your function calls:
or:
This is standard recommended practice for the
execl()
function in POSIX systems, for example, and for precisely the same reason - the trailing arguments of a variable-length argument list are subject to usual promotions (char or short to int; float to double), but cannot otherwise be type safe.It is also why C++ practitioners generally avoid variable-length argument lists; they are not type safe.
删除 __GNUG__ 大小写,并反转第二个文件中的 ifdef/endif,这两个文件都会执行以下操作:
也就是说,它们将 C 编译的 NULL 定义为 ((void *)0),将 C++ 定义为 0 。
所以简单的答案是“不要编译为 C++”。
您真正的问题是您希望在可变参数列表中使用 NULL,并结合编译器不可预测的参数大小。您可能会尝试编写“(void *)0”而不是 NULL 来终止列表,并强制编译器传递 8 字节指针而不是 4 字节 int。
Removing the
__GNUG__
case, and inverting the ifdef/endif in the second file, BOTH files do:Which is to say that they define NULL as ((void *)0) for C compilations and 0 for C++.
So the simple answer is "Don't compile as C++".
Your real problem is your desire to use NULL in your variadic arugment list, combined with your compiler's unpredictable argument sizing. What you MIGHT try is writing "(void *)0" instead of NULL to terminate your list, and force the compiler pass an 8-byte pointer instead of a 4-byte int.
您可能无法修复包含,因为系统包含是一个曲折的迷宫。
您可以使用 (void*)0 或 (char*)0 而不是 NULL 来解决该问题。
经过考虑,我拒绝了之前重新定义 NULL 的想法。这将是一件坏事,并且可能会弄乱许多其他代码。
You may not be able to fix the includes because system includes are a twisty maze.
You might fix the problem by using (void*)0 or (char*)0 instead of NULL.
After considering it I am rejecting my previous idea of redefining NULL. That would be a bad thing to do and could mess up a lot of other code.
我之前回复过以下答案。但后来我发现我误解了一些信息并给出了错误的答案。出于好奇,我用VS2008做了同样的测试,但得到了不同的结果。这只是大脑练习...
我在 64 位平台上使用 GCC 4.1.3 进行了快速测试,
这是编译器生成的程序集...
注意所有堆栈位移都是 8 字节...
我做了与 VS2008 进行相同的测试,汇编输出如下:
这次编译器生成不同的代码,它将 0 视为 64 位数据类型(注意 QWORD 关键字)。值和堆栈位移都是正确的。 VS 和 GCC 的行为不同。
I previously replied with the below answer. But then I saw that I misinterpreted several information and gave an incorrect answer. Just out of curiosity, I did the same test with VS2008 and I got different results. This is just brain exercise...
I did a quick test on a 64-bit platform with GCC 4.1.3
And this is the assembly generated by the compiler...
Notice all the stack displacements are 8 byte...
I did the same test with VS2008 , the assembly output is as follows :
This time compiler generates different code and it treats 0 as an 64-bit data-type (notice the QWORD keyword). Both the value and stack displacement is correct. VS and GCC behaves differently.