システム ヘッダー ファイルに飛び込むことは、困難で不快な経験になる可能性があります。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_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>
は、このプログラムの 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 を出力します。ここでも、前処理が行われた後にコンパイル プロセスを停止できます。でテストできますgcc -E
。
gcc 以外のコンパイラでコードをコンパイルしている場合はどうなりますか?
それなら、デフォルトの ANSI 制限は、標準の 32 ビット ワードに対して想定されます。ANSI C標準のパラグラフ5.2.4.2.1から(整数型のサイズ<limits.h>
):
以下に示す値は、#if 前処理ディレクティブでの使用に適した定数式に置き換えられます。[...] それらの実装定義の値は、同じ符号で示されているものと同じ大きさ (絶対値) である必要があります。
POSIX では、準拠プラットフォームにCHAR_BIT == 8
.
もちろん、glibc の仮定は を持たないマシンではうまくいかない可能性がCHAR_BIT == 8
ありますが、通常とは異なるアーキテクチャを使用し、gcc を使用せず、プラットフォームが POSIX に準拠していないことに注意してください。あまりありません。
ただし、「定義済みの実装」とは、コンパイラの作成者が何が起こるかを選択することを意味することに注意してください。したがって、 を使用してコンパイルしていない場合でもgcc
、コンパイラに__CHAR_BIT__
同等のものが定義されている可能性があります。glibc はそれを使用しませんが、少し調べて、コンパイラの定義を直接使用することができます。これは一般的に悪い習慣です。特定のコンパイラ向けのコードを書くことになります。
システム ヘッダー ファイルをいじってはいけません。のような間違った重要な定数を使用してコンパイルすると、非常に奇妙なことが起こる可能性がありますCHAR_BIT
。これは教育目的でのみ行い、常に元のファイルを復元してください。