在各种系统上修复CHAR_BIT?

pas*_*636 3 glibc char bit

我对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;
}
Run Code Online (Sandbox Code Playgroud)

以下是补充:我已阅读所有回复,但仍不清楚.在实践中,#include <limits.h>并使用CHAR_BIT,我可以遵守.但那是另一回事.在这里,我想知道为什么它会出现这种情况,首先它是glibc /usr/include/limits.h中的固定值'8',当那些具有1字节!= 8位的系统与glibc一起安装时会发生什么; 然后我发现值'8'甚至不是代码使用的实际值,所以'8'表示什么都没有?如果没有使用该值,为什么要将'8'放在那里?

谢谢,

Fil*_*ves 12

潜入系统头文件可能是令人生畏和令人不快的体验.glibc头文件很容易在脑海中造成很多混乱,因为它们在某些情况下包含了其他系统头文件,这些文件覆盖了到目前为止定义的内容.

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

#define CHAR_BIT 8
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

因此,如果使用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>
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

使用此程序,您可以在系统的帮助下找到系统的路径,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
Run Code Online (Sandbox Code Playgroud)

你可以在那个文件中找到它:

/* Number of bits in a `char'.  */
#undef CHAR_BIT
#define CHAR_BIT __CHAR_BIT__
Run Code Online (Sandbox Code Playgroud)

请注意该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;
}
Run Code Online (Sandbox Code Playgroud)

然后运行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;
}
Run Code Online (Sandbox Code Playgroud)

CHAR_BITgcc中的定义limits.h(即文件输入/usr/lib/gcc/i486-linux-gnu/4.7/include-fixed/limits.h)从__CHAR_BIT__9 更改为9将使此代码打印9.再次,您可以在预处理发生后停止编译过程; 你可以测试一下gcc -E.

如果您使用gcc以外的编译器编译代码怎么办?

好吧,那就是它,标准32位字的默认ANSI限制.从ANSI C标准中的5.2.4.2.1段(整数类型的大小<limits.h>):

下面给出的值应替换为适用于#if预处理指令的常量表达式.[......]它们的实现定义值的大小(绝对值)应等于或大于显示的值,并带有相同的符号.

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

    CHAR_BIT 8

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

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

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

请记住,您永远不应该搞乱系统头文件.当你用错误和重要的常量编译东西时会发生很奇怪的事情CHAR_BIT.这样做仅用于教育目的,并始终恢复原始文件.


Kei*_*son 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
Run Code Online (Sandbox Code Playgroud)

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