在各种系统上修复CHAR_BIT?

时间:2013-10-31 14:11:33

标签: char bit glibc

我对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'放在那里?

谢谢,

3 个答案:

答案 0 :(得分:12)

潜入系统头文件可能是令人生畏和令人不快的体验。 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的联机帮助页所述。

显然,如果您更改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;
}

将gcc CHAR_BIT中的limits.h定义(即/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段(整数类型的大小<limits.h>):

  

下面给出的值应该用常量表达式代替   适用于#if预处理指令。 [...]他们的   实现定义的值的大小应相等或更大   (绝对值)显示的那些,具有相同的符号。

     
      
  • 不是位字段(字节)的最小对象的位数

         

    CHAR_BIT 8

  •   

POSIX要求合规平台具有CHAR_BIT == 8

当然,对于没有CHAR_BIT == 8的机器,glibc的假设可能会出错,但请注意,您必须使用非常规架构而不使用gcc并且您的平台不符合POSIX标准。不太可能。

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

请记住,您永远不应该搞乱系统头文件。当您使用错误和重要的常量(如CHAR_BIT)编译内容时,可能会发生非常奇怪的事情。这样做仅用于教育目的,并始终恢复原始文件。

答案 1 :(得分:6)

对于给定的系统,永远不应该更改

CHAR_BITCHAR_BIT的值指定最小可寻址存储单元的位大小(“字节”) - 因此即使使用16位字符(UCS-2或UTF-16)的系统也很可能具有CHAR_BIT == 8

几乎所有现代系统都有CHAR_BIT == 8;某些DSPs的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

其中两个文件#defineCHAR_BIT个指令,一个指定为8,另一个指定为__CHAR_BIT__。我不知道(我不需要关心)这些定义中的哪些实际生效。我需要知道的是#include <limits.h>将为CHAR_BIT提供正确的定义 - 只要我不做任何破坏系统的事情。

答案 2 :(得分:0)

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