深入研究系统头文件可能是令人生畏和不愉快的经历。 glibc 头文件很容易在您的脑海中造成很多混乱,因为它们在某些情况下包含其他系统头文件,这些头文件会覆盖迄今为止定义的内容。
以limits.h为例,如果你仔细阅读头文件,你会发现CHAR_BIT的定义只在你编译没有gcc的代码时使用,因为这行:
#define CHAR_BIT 8
在上面几行的 if 条件内:
/* If we are not using GNU CC we have to define all the symbols ourself.
Otherwise use gcc's definitions (see below). */
#if !defined __GNUC__ || __GNUC__ < 2
因此,如果您使用 gcc 编译代码(很可能是这种情况),则不会使用 CHAR_BIT 的此定义。这就是为什么您更改它并且您的代码仍然打印旧值的原因。在头文件上向下滚动一点,您可以在使用 GCC 的情况下找到它:
/* Get the compiler's limits.h, which defines almost all the ISO constants.
We put this #include_next outside the double inclusion check because
it should be possible to include this file more than once and still get
the definitions from gcc's header. */
#if defined __GNUC__ && !defined _GCC_LIMITS_H_
/* `_GCC_LIMITS_H_' is what GCC's file defines. */
# include_next <limits.h>
include_next 是 GCC 扩展。您可以在这个问题中了解它的作用:Why would one use #include_next in a project?
简短的回答:它将搜索具有您指定名称的下一个头文件(在这种情况下为limits.h),并且它将包括GCC生成的limits.h。在我的系统中,它恰好是/usr/lib/gcc/i486-linux-gnu/4.7/include-fixed/limits.h。
考虑以下程序:
#include <stdio.h>
#include <limits.h>
int main(void) {
printf("%d\n", CHAR_BIT);
return 0;
}
使用这个程序,您可以在gcc -E 的帮助下找到系统的路径,它会为包含的每个文件输出一个特殊行(请参阅http://gcc.gnu.org/onlinedocs/cpp/Preprocessor-Output.html)
因为#include <limits.h>在这个程序的第2行,我将它命名为test.c,运行gcc -E test.c可以让我找到包含的真实文件:
# 2 "test.c" 2
# 1 "/usr/lib/gcc/i486-linux-gnu/4.7/include-fixed/limits.h" 1 3 4
你可以在那个文件中找到这个:
/* Number of bits in a `char'. */
#undef CHAR_BIT
#define CHAR_BIT __CHAR_BIT__
注意undef 指令:它需要覆盖任何可能的先前定义。它是在说:“忘记CHAR_BIT 是什么,这是真的”。 __CHAR_BIT__ 是一个 gcc 预定义常量。 GCC 的在线文档是这样描述的:
__CHAR_BIT__
定义为用于表示 char 数据类型的位数。它的存在是为了使标准标题给定数字
限制正常工作。你不应该直接使用这个宏;
而是包含适当的标题。
你可以用一个简单的程序来读取它的值:
#include <stdio.h>
#include <limits.h>
int main(void) {
printf("%d\n", __CHAR_BIT__);
return 0;
}
然后运行gcc -E code.c。请注意,您不应该直接使用它,正如 gcc 的手册页所述。
显然,如果您在/usr/lib/gcc/i486-linux-gnu/4.7/include-fixed/limits.h 中更改CHAR_BIT 定义,或系统中的任何等效路径,您将能够在代码中看到此更改。考虑这个简单的程序:
#include <stdio.h>
#include <limits.h>
int main(void) {
printf("%d\n", CHAR_BIT);
return 0;
}
将 gcc 的limits.h(即/usr/lib/gcc/i486-linux-gnu/4.7/include-fixed/limits.h 中的文件)中的CHAR_BIT 定义从__CHAR_BIT__ 更改为9 将使此代码打印9。同样,您可以在预处理发生后停止编译过程;您可以使用gcc -E 对其进行测试。
如果您使用 gcc 以外的编译器编译代码怎么办?
好吧,就这样吧,默认 ANSI 限制假定用于标准 32 位字。来自 ANSI C 标准中的第 5.2.4.2.1 段(整数类型的大小<limits.h>):
下面给出的值应替换为常量表达式
适用于#if 预处理指令。 [...] 他们的
实现定义的值应在大小上相等或更大
(绝对值)与所示的相同,符号相同。
-
不是位域(字节)的最小对象的位数
CHAR_BIT 8
POSIX 要求合规平台具有CHAR_BIT == 8。
当然,对于没有CHAR_BIT == 8 的机器,glibc 的假设可能会出错,但请注意,您必须在不寻常的架构下并且不使用 gcc 并且您的平台不兼容 POSIX。不太可能。
但是请记住,“实现定义”意味着编译器编写者选择发生的事情。因此,即使您没有使用gcc 进行编译,您的编译器也有可能定义了某种__CHAR_BIT__ 等效项。即使 glibc 不会使用它,您也可以做一些研究并直接使用编译器的定义。这通常是不好的做法 - 您将编写面向特定编译器的代码。
请记住,您永远不应该弄乱系统头文件。当您使用错误且重要的常量(如 CHAR_BIT)编译内容时,可能会发生非常奇怪的事情。仅出于教育目的执行此操作,并始终恢复原始文件。