3

limits.h の CHAR_BIT について混乱しています。移植性のためにマクロ CHAR_BIT があるという記事をいくつか読みました。コードで 8 のようなマジック ナンバーではないマクロを使用するには、これは合理的です。しかし、limits.h は glibc-headers からのもので、その値は 8 に固定されています。1 バイトが 8 ビット (たとえば 16 ビット) を超えるシステムに glibc-headers がインストールされている場合、コンパイル時にそれは間違っていますか? 「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 ヘッダー ファイルは、これまでに定義されたものをオーバーライドする特定の状況下で他のシステム ヘッダー ファイルを含むため、頭の中で簡単に多くの混乱を引き起こす可能性があります。

の場合、ヘッダー ファイルを注意深く読むと、 の定義は gcc を使用せずにコードをコンパイルする場合にのみ使用されるlimits.hことがわかります。CHAR_BIT

#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_nextGCC拡張です。この質問でそれが何をするかを読むことができます:プロジェクトで #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>は、このプログラムの 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_BITgcc の定義limits.h(つまり、 のファイル/usr/lib/gcc/i486-linux-gnu/4.7/include-fixed/limits.h)を 9 に変更__CHAR_BIT__すると、このコードは 9 を出力します。ここでも、前処理が行われた後にコンパイル プロセスを停止できます。でテストできますgcc -E

gcc 以外のコンパイラでコードをコンパイルしている場合はどうなりますか?

それなら、デフォルトの ANSI 制限は、標準の 32 ビット ワードに対して想定されます。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。これは教育目的でのみ行い、常に元のファイルを復元してください。

于 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 に設定される場合があります。

の値は、1 バイトのビット数を制御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

これらのファイルのうち 2つには の#defineディレクティブがCHAR_BITあり、1 つは に設定し8、もう 1 つは に設定し__CHAR_BIT__ます。これらの定義のどれが実際に有効になるかはわかりません (気にする必要はありません)。私が知る必要があるのは、システムを破壊するようなことをしない限り、-#include <limits.h>の正しい定義を与えることだけです。CHAR_BIT

于 2013-10-31T14:57:22.177 に答える
0

全体的なポイントは、異なるサイズのシステム用にコンパイルすると、CHAR_BIT が正しいサイズに変更されるということです。

于 2013-10-31T14:23:19.713 に答える