109

C++ では、sizeof('a') == sizeof(char) == 1. 'a'は文字リテラルでありsizeof(char) == 1、標準で定義されているため、これは直感的に理解できます。

ただし、C ではsizeof('a') == sizeof(int). つまり、C 文字リテラルは実際には整数のように見えます。誰かが理由を知っていますか?この C の癖についての言及はたくさんありますが、なぜそれが存在するのかについての説明はありません。

4

12 に答える 12

39

同じ主題に関する議論

「より具体的には、積分昇格。K&R C では、最初に int に昇格せずに文字値を使用することは事実上 (?) 不可能だったので、最初に文字定数 int を作成すると、そのステップが省略されました。複数の文字がありました。 'abcd' などの定数、または int に収まる多くの定数。"

于 2009-01-11T23:21:04.487 に答える
31

元の質問は「なぜ?」

その理由は、既存のコードとの下位互換性を維持しようとしながら、リテラル文字の定義が進化および変更されたためです。

初期の C の暗い時代には、タイプはまったくありませんでした。私が C でのプログラミングを初めて学んだ頃には、型は導入されていましたが、関数には呼び出し元に引数の型を伝えるためのプロトタイプがありませんでした。代わりに、パラメーターとして渡されるすべてのものが int のサイズ (これにはすべてのポインターが含まれます) であるか、double であることが標準化されました。

これは、関数を記述しているときに、宣言方法に関係なく、double ではないすべてのパラメーターが int としてスタックに格納され、コンパイラーがこれを処理するコードを関数に入れたことを意味します。

これは物事に多少の矛盾を生じさせたので、K&R が有名な本を書いたとき、彼らは文字リテラルは関数パラメーターだけでなく、どの式でも常に int に昇格されるという規則を入れました。

ANSI 委員会が最初に C を標準化したとき、文字リテラルが単に int になるようにこの規則を変更しました。これは、同じことを達成するためのより簡単な方法に思えたからです。

C++ が設計されていたとき、すべての関数は完全なプロトタイプを持つ必要がありました (これは C ではまだ必要ではありませんが、良い習慣として広く受け入れられています)。このため、文字リテラルを char に格納できることが決定されました。C++ でのこの利点は、char パラメーターを持つ関数と int パラメーターを持つ関数のシグネチャが異なることです。この利点は、C には当てはまりません。

これが、それらが異なる理由です。進化...

于 2014-04-23T16:04:22.050 に答える
23

C の文字リテラルが int 型である具体的な理由はわかりません。しかし、C++ では、そのようにしない十分な理由があります。このことを考慮:

void print(int);
void print(char);

print('a');

print の呼び出しで、char を受け取る 2 番目のバージョンが選択されることが予想されます。文字リテラルが int であることは、それを不可能にします。C++ では、複数の文字を持つリテラルの型は int のままですが、それらの値は実装で定義されていることに注意してください。したがって、'ab'has type int'a'has typecharです。

于 2009-01-11T23:26:28.110 に答える
19

MacBookでgccを使用して、次を試します:

#include <stdio.h>
#define test(A) do{printf(#A":\t%i\n",sizeof(A));}while(0)
int main(void){
  test('a');
  test("a");
  test("");
  test(char);
  test(short);
  test(int);
  test(long);
  test((char)0x0);
  test((short)0x0);
  test((int)0x0);
  test((long)0x0);
  return 0;
};

実行すると次のようになります。

'a':    4
"a":    2
"":     1
char:   1
short:  2
int:    4
long:   4
(char)0x0:      1
(short)0x0:     2
(int)0x0:       4
(long)0x0:      4

これは、ご想像のとおり、文字が 8 ビットであることを示唆していますが、文字リテラルは int です。

于 2009-01-11T23:08:41.360 に答える
8

Cが書かれていた頃、PDP-11のMACRO-11アセンブリ言語には次のものがありました。

MOV #'A, R0      // 8-bit character encoding for 'A' into 16 bit register

この種のことはアセンブリ言語では非常に一般的です。下位8ビットは文字コードを保持し、他のビットは0にクリアされます。PDP-11には次のものもあります。

MOV #"AB, R0     // 16-bit character encoding for 'A' (low byte) and 'B'

これにより、16ビットレジスタの下位バイトと上位バイトに2文字をロードする便利な方法が提供されました。次に、それらを他の場所に書き込んで、テキストデータまたは画面メモリを更新します。

したがって、文字をレジスタサイズに昇格させるという考えはごく普通であり、望ましいことです。ただし、ハードコードされたオペコードの一部としてではなく、メインメモリのどこかから次のものを含むレジスタに「A」を入れる必要があるとします。

address: value
20: 'X'
21: 'A'
22: 'A'
23: 'X'
24: 0
25: 'A'
26: 'A'
27: 0
28: 'A'

このメインメモリからレジスタに「A」だけを読み取りたい場合、どれを読み取りますか?

  • 一部のCPUは、16ビット値の16ビットレジスタへの読み取りのみを直接サポートする場合があります。つまり、20または22での読み取りでは、CPUのエンディアンに応じて、「X」からのビットをクリアする必要があります。下位バイトにシフトする必要があります。

  • 一部のCPUでは、メモリに合わせた読み取りが必要になる場合があります。つまり、関係する最小アドレスはデータサイズの倍数である必要があります。つまり、アドレス24と25からは読み取れるが、27と28からは読み取れない場合があります。

したがって、「A」をレジスタに取り込むためのコードを生成するコンパイラは、少し余分なメモリを浪費し、エンディアンに応じて値を0「A」または「A」0としてエンコードし、適切に整列されるようにすることを好む場合があります(つまり、奇数のメモリアドレスではありません)。

私の推測では、CはこのレベルのCPU中心の動作を単純に引き継いでおり、メモリのレジスタサイズを占める文字定数を考えて、「高水準アセンブラ」としてのCの一般的な評価を支持しています。

http://www.dmv.net/dec/pdf/macro.pdfの6-25ページの6.3.3を参照してください)

于 2011-03-29T06:26:51.917 に答える
5

私はその根拠を見たことがありません (C char リテラルは int 型です) が、Stroustrup がそれについて言わなければならなかったことがあります (Design and Evolution 11.2.1 - Fine-Grain Resolution から):

'a'C では、 isなどの文字リテラルの型int。驚くべきことに、 C++ で'a'charを指定しても互換性の問題は発生しません。異常な例を除いて、sizeof('a')C と C++ の両方で表現できるすべての構造は同じ結果になります。

したがって、ほとんどの場合、問題は発生しないはずです。

于 2009-01-11T23:53:57.650 に答える
5

K&R を読んで、EOF に到達するまで一度に 1 文字ずつ読み取るコード スニペットを見たのを覚えています。すべての文字はファイル/入力ストリームにある有効な文字であるため、EOF を char 値にすることはできません。コードが行ったことは、読み取った文字を int に入れ、EOF をテストし、そうでない場合は char に変換することでした。

これはあなたの質問に正確に答えているわけではありませんが、EOFリテラルがあった場合、残りの文字リテラルが sizeof(int) であることはある程度理にかなっています。

int r;
char buffer[1024], *p; // don't use in production - buffer overflow likely
p = buffer;

while ((r = getc(file)) != EOF)
{
  *(p++) = (char) r;
}
于 2009-01-11T22:51:05.763 に答える
1

これは「統合昇格」と呼ばれる正しい動作です。他のケースでも発生する可能性があります(私の記憶が正しければ、主に二項演算子)。

編集: 念のため、Expert C Programming: Deep Secretsのコピーを確認したところ、char リテラルが型intで始まらないことが確認されました。最初はchar型ですが、で使用されるとintに昇格されます。以下、本より​​引用。

文字リテラルには int 型があり、char 型からの昇格の規則に従ってそこに到達します。これは、K&R 1 の 39 ページで簡単に説明されています。

式内のすべての char は int に変換されます....式内の float はすべて double に変換されることに注意してください....関数の引数は式であるため、引数が関数に渡されるときにも型変換が行われます。特に、char と short は int になり、float は double になります。

于 2009-01-11T23:45:54.997 に答える
0

これは言語仕様に接するだけですが、ハードウェアでは通常、CPU のレジスタ サイズは 1 つ (たとえば 32 ビット) しかありません。レジスタにロードされるときの int への暗黙的な変換。コンパイラは、各操作の後に数値を適切にマスキングしてシフトすることを処理するため、たとえば 2 を (unsigned char) 254 に追加すると、256 ではなく 0 にラップアラウンドしますが、シリコン内では実際には int です。メモリに保存するまで。

言語はとにかく 8 ビットのリテラル型を指定できたので、これは一種の学術的なポイントですが、この場合、言語仕様はたまたま CPU が実際に行っていることをより厳密に反映しています。

(x86 の専門家は、たとえば、短い幅のレジスタを 1 ステップで追加するネイティブ addh op があることに気付くかもしれませんが RISC コア内では、これは 2 つのステップに変換されます。数値を追加し、次に符号を拡張します。 PowerPC)

于 2009-01-11T23:54:30.283 に答える
0

これはさすがに知りませんでした。プロトタイプが存在する前は、int よりも狭いものは、関数の引数として使用するときに int に変換されていました。それは説明の一部かもしれません。

于 2009-01-11T23:07:13.140 に答える