深入研究系统头文件可能是一种令人望而生畏和不愉快的经历。glibc头文件可能会在某些情况下包含其他系统头文件,从而覆盖了到目前为止已定义的内容,因此很容易让人感到困惑。
就limits.h
而言,如果你仔细阅读头文件,你会发现CHAR_BIT
的定义仅在使用gcc编译代码时才会被使用,因为有这样一行:
在几行代码之上是一个if
条件:
#if !defined __GNUC__ || __GNUC__ < 2
因此,如果您使用gcc编译代码(这很可能是情况),则不会使用CHAR_BIT
的这个定义。这就是为什么更改该定义后您的代码仍然打印旧值的原因。在标头文件中向下滚动一点,您可以找到针对使用GCC的情况下的解决方法:
#if defined __GNUC__ && !defined _GCC_LIMITS_H_
# include_next <limits.h>
include_next
是GCC的一个扩展。你可以在这个问题中了解它的作用:为什么会在项目中使用#include_next?
简而言之:它会查找下一个具有指定名称(在本例中为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>
,并且程序名为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文件中的CHAR_BIT定义(即位于/usr/lib/gcc/i486-linux-gnu/4.7/include-fixed/limits.h的文件)从__CHAR_BIT__更改为9将使此代码打印9。可以在预处理完成后停止编译过程;您可以使用gcc -E进行测试。如果您正在使用除gcc以外的编译器编译代码呢?那么就默认使用标准32位字的默认ANSI限制。从ANSI C标准(整数类型大小)的第5.2.4.2.1段开始:
下面给出的值应替换为适用于#if预处理指令的常量表达式。[...] 它们的实现定义值应等于或大于具有相同符号的那些值的数量(绝对值)。
不是位字段的最小对象(字节)的位数
CHAR_BIT 8
POSIX规定符合规范的平台具有CHAR_BIT == 8。
当然,glibc的假设对于没有CHAR_BIT == 8的机器可能会出错,但请注意,您必须处于异常架构下,并且不使用gcc,并且您的平台不符合POSIX规范。这种情况不太可能发生。
请记住,“实现定义”意味着编译器编写者选择发生的事情。因此,即使您没有使用gcc进行编译,您的编译器也有可能定义了某种__CHAR_BIT__等效物。即使glibc不使用它,您也可以进行一些研究并直接使用编译器的定义。这通常是不好的做法-您将编写面向特定编译器的代码。
请记住,永远不应该操纵系统标头文件。当您使用错误的重要常量(例如CHAR_BIT)编译东西时,非常奇怪的事情会发生。仅出于教育目的而这样做,并始终将原始文件恢复回去。
#include <limits.h>
注释掉吗?编译失败了。CHAR_BIT未声明。 - password636#ifdef
分支吗?如果你查看任何标准库头文件,你会看到各种预处理器分支,而CHAR_BIT
可能在数十个不同的位置被定义。无论如何,你都不应该编辑这些文件,它们只是作为参考。 - Andon M. Coleman