3

我对limits.h中的CHAR_BIT感到困惑。我读过一些文章说宏 CHAR_BIT 是为了可移植性。在代码中使用宏而不是像 8 这样的幻数,这是合理的。但是limits.h来自glibc-headers,它的值固定为8。如果glibc-headers安装在一个字节超过8位(比如16位)的系统上,编译时会出错吗?'char' 分配 8 位或 16 位?

而当我在limits.h中将CHAR_BIT修改为9时,下面的代码仍然打印'8',怎么办?

#include <stdio.h>
#include <limits.h>

int
main(int argc, char **argv)
{
    printf("%d\n", CHAR_BIT);
    return 0;
}

以下是补充:我已经阅读了所有回复,但仍然不清楚。在实践中,#include <limits.h>使用 CHAR_BIT,我可以遵守这一点。但那是另一回事。这里我想知道为什么会这样,首先它是glibc /usr/include/limits.h中的固定值'8',当那些有1字节!= 8位的系统安装了glibc时会发生什么;然后我发现值'8'甚至不是代码使用的真实值,所以'8'意味着什么?如果根本不使用该值,为什么要放'8'呢?

谢谢,

4

3 回答 3

14

深入研究系统头文件可能是令人生畏和不愉快的经历。glibc 头文件很容易在您的脑海中造成很多混乱,因为它们在某些情况下包含其他系统头文件,这些头文件会覆盖迄今为止定义的内容。

在 的情况下limits.h,如果你仔细阅读头文件,你会发现CHAR_BIT只有在没有 gcc 的情况下编译代码时才会使用 for 的定义,因为这行:

#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 的手册页所述。

显然,如果您更改CHAR_BIT内部的定义/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;
}

CHAR_BIT将gcc 中的定义limits.h(即 中的文件/usr/lib/gcc/i486-linux-gnu/4.7/include-fixed/limits.h)从9更改__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 的机器,glibc 的假设可能会出错,但请注意,您必须处于不寻常的架构下并且不使用 gcc 并且您的平台不兼容 POSIX。不太可能。

但是请记住,“定义的实现”意味着编译器编写者选择发生的事情。因此,即使您不使用 进行编译gcc,您的编译器也有可能定义了某种__CHAR_BIT__等效项。即使 glibc 不会使用它,您也可以做一些研究并直接使用编译器的定义。这通常是不好的做法 - 您将编写面向特定编译器的代码。

请记住,您永远不应该弄乱系统头文件。当您使用错误且重要的常量(例如CHAR_BIT. 仅出于教育目的执行此操作,并始终恢复原始文件。

于 2013-11-07T11:32:50.440 回答
6

CHAR_BIT对于给定的系统,永远不应更改。的值CHAR_BIT指定了最小可寻址存储单元(“字节”)的大小,因此即使是使用 16 位字符(UCS-2 或 UTF-16)的系统也很可能具有CHAR_BIT == 8.

几乎所有现代系统都有CHAR_BIT == 8某些DSP的 C 实现可能会将其设置为 16 或 32。

的值CHAR_BIT控制字节中的位数,它记录它,并允许用户代码引用它。例如,一个对象的位数是sizeof object * CHAR_BIT

如果您编辑系统<limits.h>文件,则不会改变系统的实际特征;它只是给你一个不一致的系统。这就像破解你的编译器,所以它定义了符号_win32而不是_linux; 这不会神奇地将您的系统从 Windows 更改为 Linux,它只会破坏它。

CHAR_BIT是每个系统的只读常量。它由系统的开发人员定义。你不能改变它;甚至不要尝试。

据我所知,glibc 仅适用于 8 位字节的系统。理论上可以修改它以使其在其他系统上工作,但如果没有大量的开发工作,您甚至可能无法将其安装在具有 16 位字节的系统上。

至于为什么破解limits.h文件没有改变你得到的值CHAR_BIT,系统标头很复杂,不打算就地编辑。当我编译一个刚刚#include <limits.h>在我的系统上的小文件时,它直接或间接包括:

/usr/include/features.h
/usr/include/limits.h
/usr/include/linux/limits.h
/usr/include/x86_64-linux-gnu/bits/local_lim.h
/usr/include/x86_64-linux-gnu/bits/posix1_lim.h
/usr/include/x86_64-linux-gnu/bits/posix2_lim.h
/usr/include/x86_64-linux-gnu/bits/predefs.h
/usr/include/x86_64-linux-gnu/bits/wordsize.h
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h
/usr/include/x86_64-linux-gnu/gnu/stubs.h
/usr/include/x86_64-linux-gnu/sys/cdefs.h
/usr/lib/gcc/x86_64-linux-gnu/4.7/include-fixed/limits.h
/usr/lib/gcc/x86_64-linux-gnu/4.7/include-fixed/syslimits.h

其中两个文件具有针对 的#define指令CHAR_BIT,一个将其设置为 ,另一个将其设置8__CHAR_BIT__。我不知道(我也不需要关心)这些定义中的哪些实际生效。我需要知道的是,只要我不做任何破坏系统的事情,它#include <limits.h>就会给出正确的定义。CHAR_BIT

于 2013-10-31T14:57:22.177 回答
0

关键是,当为具有不同大小的系统进行编译时,CHAR_BIT 会更改为正确的大小。

于 2013-10-31T14:23:19.713 回答