アプリ版:「スタンプのみでお礼する」機能のリリースについて

C言語の「汎整数拡張(インテグラルプロモーション)」というものに関するものだと思います。

char型とchar型を加えた結果は、char型でしょうか。それともint型でしょうか。
(下のプログラムの
printf("sizeof(a[0]+a[1])は%d\n", sizeof(a[0]+a[1])); /* char型+char型 */
という部分の結果は4なので、int型と考えるべきなのかな。)

私は、char型とint型の加算の結果はint型だと思っていましたが、
char型とchar型の加算の結果はやはりchar型だと思っていました。
(それが間違えているのでしょうか。)


if(a[0]==i) /* char型とint型の比較(?) */
の部分では、左辺はchar型、右辺はint型ですが、このように型の違う変数を比較しても文法上構わないのでしょうか。
(私は、「比較は必ず型の同じもの同士でしかできない」と思っていました。)
左辺はchar型のように見えて、じつはint型ですか。


#include <stdio.h>
int main(void)
{
char a[4];
int i=77;
printf("sizeof(int)は%d\n", sizeof(int));
printf("sizeof(char)は%d\n", sizeof(char));
printf("sizeof('M')は%d\n", sizeof('M'));
printf("sizeof(a[0])は%d\n", sizeof(a[0]));

a[0]='M';
a[1]=7+6;
a[2]=a[0]+a[1];
printf("sizeof(a[0]+a[1])は%d\n", sizeof(a[0]+a[1])); /* char型+char型 */
printf("sizeof(+a[0])=%d\n", sizeof(+a[0]));

if(a[0]==i) /* char型とint型の比較(?) */
puts("a[0]==i");
else
puts("a[0]!=i");

return(0);
}

ちなみにワーニングもエラーもなんにもでません。

A 回答 (7件)

「sizeof 単項式」について補足です。



これは sizeof 演算子が「単項演算子」であるために書かれたものだと思われます。
例えば同様に単項演算子である ++ も、「++ 単項式」です。

では単項式とは何なのかというと、これは以下のようなものを指します。(抜けがあるかもしれませんが。)
・定数
・名前(変数名や関数名など)
・( 式 )
・後置式

( 式 ) はおなじみの「式の結果を返す」ものですが、実はこれ全体で単項式です。
本来「sizeof 単項式」では ( ) を必要としないので、sizeof より優先順位の低い演算子を含む式を裸で渡すことはませんが、( ) で囲めばどんな式でも書けるわけです。

後置式は「配列構文によるアドレス参照式」や「関数呼び出し式」、「メンバ参照式」「後置型インクリメント/デクリメント式」です。
なので、
> sizeof(a[0])
> と書いたら、[ ] という演算子は2項演算子だから、a[0]は単項式になっていない
は、わざわざ ( ) で囲まず sizeof a[0] と書いても、正しい単項式なので問題ありません。
(これらの演算子は、全て sizeof より優先順位が上になっています。)

先に ++ も単項式を取る、ということを書きましたが、++ だって
  ++*(p + n)
のような記述が通るのだから、sizeof でも問題がなくて当然です。

また、例えば ++ では「オペランドは左辺値でなければならない」という原則があり、左辺値でない式に ++ を適用するとコンパイルエラーが発生します。
しかし、sizeof に「オペランドはオブジェクトでなければならない」などという原則を聞いたことがありません。
無論コンパイルエラーも起きませんし、期待通りに式の値のサイズが返ります。
「式の値のサイズ」というのはコンパイル時に判明しているものなので、取得できて当然といえば当然ですが、これがCで定められた仕様かどうかというのは残念ながら見つけることはできませんでした。
ただし、質問にあるような sizeof の文法は全て正しく、警告が出ないのは仕様通りだというのは間違いありません。
    • good
    • 0
この回答へのお礼

>新回答がなければ、あと1日くらいで締め切りたいなあという気分になってきています。

と私は書きました。で、新回答が付いたのですが、勝手ながら、どこかで けりを付けないと終わりそうにないので、ここで締め切ります。

正直いって、ご回答のすべてを理解したわけではありません。
しかし、sizeofに関しては、

>質問にあるような sizeof の文法は全て正しく、警告が出ないのは仕様通りだというのは間違いありません。
ということがわかったので、それでよしとしたいです。



ありがとうございました。

お礼日時:2002/08/02 21:00

No.4でtoysmithさんも書かれていますが、charやshortなどintよりもサイズが小さい整数型は、単項の状態で(int型への)整数格上げが行われます。

(これを単項変換といいます。)
機械語レベルで言えば、「(変数の)データをメモリからレジスタに移す際、レジスタ長に拡張している」ということにすぎず、この時の拡張が「単項変換」にあたるのです。
※int型のサイズは、通常は汎用レジスタのサイズと同じです。

つまり char + char は、内部では (int)char + (int)char として処理されますので、結果の値は int型 です。
(No.4の補足の見解通りです。)

== についても、ほぼその通りです。
ただ、「算術変換」というのは「(二項演算において)両オペランドの型のうち、精度の高い方の型に統一する」ものであり、「単項変換」とは異なるものです。
== においてのcharデータは、単項変換によってintになったものであり、算術変換されたものではありません。

また、sizeof演算子についてですが、sizeofの書式は
  sizeof 単項式   単項式を( )で囲む必要はないが、囲む方がよい。
  sizeof(型名)    ( )は必須
となっています。
「単項式」となっているので、当然計算式や文字列を与えることもできます。
計算式では式の値(演算結果)のサイズが、文字列では文字列のサイズ(\0分も含む)が返ります。
(仕様通りなので、トリッキーということはないです。)
変数を単項で与えた場合は特別で、単項変換された式の値ではなく、その変数自身が占有するメモリのサイズが返ります。
例えば配列変数を与えると、要素の型のサイズ×要素数の結果が返ります。

あと1点だけ。
単項変換について補足すると、No.4の回答に
> char,short,float型は評価時にint,doubleへの格上げが行われます。
とありますが、ANSI Cではfloat→doubleの暗黙変換はなくなりました。
この辺は古いコンパイラだと、ANSI C準拠であっても対応していない場合もあり、注意が必要なところです。

この回答への補足

たいへん詳しく丁寧にありがとうございます。

単項変換とか算術変換とか整数格上げ、暗黙の型変換、あるいは私の言う「汎整数拡張」「通常算術型変換」とかいろいろな言葉が出てきて、いろいろな本にもいろいろな言葉が使われていて、どれがどれなのか整理しなければいけないんですが、それは私が自分で納得するようにいつか行なうにします。

ご回答によると、

>char + char は、内部では (int)char + (int)char として処理されますので、結果の
>値は int型 です。
>(No.4の補足の見解通りです。)
>== についても、ほぼその通りです。

ということで、私がNo.4の補足で書いたことは、(言葉の使い方が正確かどうか自信がないですが、)大体あっているようなので、それはそれでよしとしましょう。

>ANSI Cではfloat→doubleの暗黙変換はなくなりました。
このことはどこかで読んだんで、確かにその通りだと思います。

sizeof(型名)
が正しい使い方であるのは誰しも承知していることと思います。それは置いておいて

>sizeof 単項式
これが私が今、よくわからないことなんです。
『新ANSI C言語辞典』(平林雅英)も、
sizeof 単項式
というように「単項式」という言葉を使っているし、
『新C言語入門』(林 晴比古  ソフトバンク)も同様に「単項式」という言葉を使っています。

しかし、No.5補足に書いたとおり、K&R(第2版訳書訂正版p251)やJIS X3010(6.3.3.4 sizeof演算子)では、単項式ではなく単に「式」と言っています。
(JIS X3010の6.3.3.4 意味規則 より。
「sizeof演算子の結果は、そのオペランドの(バイト数での)大きさとする。オペランドは、式、又は括弧で囲まれた型の名前のいずれかとする。その大きさは、オペランドの型によって決定する。オペランド自身は、評価しない。その結果は、整数定数とする。」)

私はいろいろ考えてしまいます。
式なら単に「式」でいいはずなのに、なぜ「単項」を付けて「単項式」という本があるのだろうか。
そもそも「単項式」ってなんだろう。
前述した『新C言語入門』(林 晴比古)は、「単項式」の説明として「演算子とひとつのオペランドからなる式のこと。」と言います。

そうしたら、私は考えてしまいます。
質問のようにchar a[4]; という宣言があったときに、
sizeof(a[0])
と書いたら、[ ] という演算子は2項演算子だから、a[0]は単項式になっていない。オペランドはaと0の2つある。このような書き方はまずいのか?

>「単項式」となっているので、当然計算式や文字列を与えることもできます。
>計算式では式の値(演算結果)のサイズが、文字列では文字列のサイズ(\0分も含む)が返ります。
>(仕様通りなので、トリッキーということはないです。)

回答No.5でいうような「変数」よりは広い範囲のものをオペランドに取れるんではないかな、とは思ったのですが、その点についてはこのようにはっきり「トリッキーということはない」とご回答を頂いたのでよかったです。

当然
sizeof 'a'
sizeof 1.2
これらも問題ないはずです。

しかし、
sizeof 単項式
というときの「単項式」は単に「式」と書けばいいのではないだろうか。、
そうすれば「式」と書いている資料に合うし、柴田望洋氏の書くような
sizeof(x+2)
sizeof(a+1.0)
これらも全く問題ないし、
第一、質問のプログラムみたいにsizeofをいろいろに使っているプログラムでエラーも警告もないのも自然に納得が行き、いいことずくめではないか、
等と考えてしまいます。

大体「単項式」って、『新C言語入門』(林 晴比古)のいう「演算子とひとつのオペランドからなる式のこと。」ではなくて、正確には違う意味ではないのか、などと考えてしまいます。

しかし、頼みのJIS X3010は別の場所(6.3.3 単項演算子 構文規則)で、
sizeof 単項式
と単項式という言葉を使っているし、、、、
「構文規則」ってのもそもそもよくわからないし、、、、
(要するに、「単項式に単項演算子を何個施しても、それは単項式だ」ということが言いたいのか?)


#私の頭もついていけなくなりかけているし、そろそろ考えるのはやめたほうがいいかな。
新回答がなければ、あと1日くらいで締め切りたいなあという気分になってきています。

補足日時:2002/08/01 22:26
    • good
    • 0

再びangbandです。



 Cの仕様としてはchar+charが、どんな型になるかは
規定されていないと思います。ただ保証されているのは
「情報が失われないように暗黙にキャストされる」こと
だけです。ですから32ビットPCであれば、char+charが
一時的に32ビット(int)になっていても不思議じゃない
ですね。

 この一時的な値を sizeof(式) で計算することは
できるかもしれませんが、K&Rに書いてない以上、
トリッキーなコードと言わざるを得ませんし、書くべき
ではありません。

sizeof 変数
sizeof(型)

これだけが正しいsizeofの使い方です。

 話がずれましたけど、結論としてはchar+charの
左辺値がcharで収まるならcharかもしれないし、
それでもintかもしれない。左辺値がcharに収まらない
時は、少なくともchar以上の大きさの型になる、
ということだと思いますよ。

 このあたりはコンパイラの実装に関わるところですが。
 ご参考になれば、幸いです。

この回答への補足

sizeofの正しい使い方の問題


>sizeof 変数
>sizeof(型)
>これだけが正しいsizeofの使い方です。

sizeofの正しい使い方は質問後に自分でも調べましたが、
どうも、どこまでが正しい使い方なのか、よくわかりませんでした。

No.2の補足で、
sizeof a+1.0
sizeof x+2
というような書きかたが柴田望洋という人の書いた本に書いてある、
と書きましたが、
この人の書いた本以外で、このような書き方をしている本は、私の知る限りありません。

しかし、
sizeof 'a'
sizeof 1.2
sizeof sin(10)
という書き方は、『新ANSI C言語辞典』(平林雅英、技術評論社)に書いてあるんで、多分、正しいんだろうと思います。
(本に書いてあるからといって正しいとはいえないんだろうけど、どこまでも疑ってもしょうがないもので、、、、)

ご回答のK&Rがプログラミング言語Cの第1版を指しているのなら、それは手元にないんで、確認できません。

手元にあるのはその第2版(訳書訂正版)で、その251ページに
「(sizeof の)被演算数は、式か、カッコで囲まれた型名かのいずれかである」
と書かれていて(JIS X3010でも同旨。)、
「式」と書いているので、「変数」とは違う概念であるように思われます。


それはともかく、質問では、
printf("sizeof('M')は%d\n", sizeof('M'));
printf("sizeof(a[0])は%d\n", sizeof(a[0]));
printf("sizeof(a[0]+a[1])は%d\n", sizeof(a[0]+a[1]));
printf("sizeof(+a[0])=%d\n", sizeof(+a[0]));
という使い方がされています。
どれもsizeof(型名)ではないです。

私は「変数」が何なのか、正確には知らないですが、
'M'も a[0]+a[1]も +a[0]も変数ではないと思います。

したがって、ご回答によると、sizeof(a[0])以外の3つについては、
トリッキーなコードだということになると思います。

補足日時:2002/08/01 00:16
    • good
    • 0

実行文における変数は単項式です。


多項式は各項の評価結果を演算します。
よって、宣言した方のまま演算が行われるとは限りません。

char,short,float型は評価時にint,doubleへの格上げが行われます。
つまりchar + charは(int)char + (int)charと等価です。
注 [charがunsignedな処理系では(unsigned)]

これは右辺値に限った話で、左辺値では宣言時の型が採用されます。

右辺値、左辺値、式、評価について理解を深めましょう。

この回答への補足

私なりに調べてみました。

char型もint型も汎整数型に含まれます。
char型は、整数型には含まれない。

signedもunsignedも与えられていない整数型は符号付きですが、
>注 [charがunsignedな処理系では(unsigned)]

そうだ、
自分の処理系で、charがunsigendなのかどうか、まず調べるとしましょう。

#include <stdio.h>
#include <limits.h>

int main(void)
{
printf("この処理系のchar型は");

if(CHAR_MIN)
puts("符号付きで、");
else
puts("符号ナシで、");

printf("%d~%dを表すことができます。\n", CHAR_MIN, CHAR_MAX);

return(0);
}

実行結果

この処理系のchar型は符号付きで、
-128~127を表すことができます。

..............................

ということで、
char型が符号付き(signed char)だとして:


●char型+char型の話:

この場合の+は加算演算子で、演算前にオペランドに対して通常算術型変換を行ないます。

通常算術型変換を、char型とchar型の場合について述べると、
両オペランドがint型を持つように汎整数拡張します。
したがって、char型+char型では、2つのオペランドをint型に格上げした上で加算を行ないます。

>char + charは(int)char + (int)charと等価です。

斯くのごとく、ご回答と一致します。

通常算術型変換の共通の型(この場合int型)は、結果の型となります。

自分で自分の質問に答えると、
>私は、char型とint型の加算の結果はint型だと思っていましたが、
>char型とchar型の加算の結果はやはりchar型だと思っていました。
>(それが間違えているのでしょうか。)

間違えです。


●次に、等価演算子(==)について:
等価演算子の両オペランドが算術型である場合、演算前に通常算術型変換を行ないます。
したがって、
char型==int型
の場合、左辺は汎整数拡張するので、int型同士として比較することになります。


もし、間違えがあったら、ご指摘いただくと幸いです。

補足日時:2002/07/31 23:25
    • good
    • 0

いくつかの話がこみいって混乱しておられるようです。


(1)C言語特有の約束としてchar型とint型はどちらもchar型とint型の
  両方の性質を持つということです。他の高級言語ではこんなことは
  考えられません。ただ、文字列(配列)として扱えるのはchar型だけです。
  この辺については、C言語はOSを書く言語だから当然の機能だと思う人と
  C言語は無茶苦茶だという人がいます。見方によって、どちらも正しいです。

(2)代入を伴わない中間計算結果の型にはきちんとした約束があります。
  代入する場合には中間結果から代入先の型に変換されます。
  キャストを使って、強制的に型変換することも出来ます。

(3)char型+char型はchar型ですが、オーバーフローには充分気を付ける必要があります。
  その場合、片方のchar型の前で(int)…キャスト を指定すればint型で計算されオーバーフローしません。

この回答への補足

>(3)char型+char型はchar型です

この件については裏づけを取ろうとして本や資料を読んでいるんですが、裏づけが取れません。
確かに、int型+int型はint型ですので、char型+char型のように同じ型同士の演算の結果はその型(この場合char型)になるように思われるのですが、なんだか、違うようなのです。(ご回答に対して失礼ですが…)
もう少し調べてみます

補足日時:2002/07/31 01:10
    • good
    • 0

charとcharを足している式の型を取得することに


意味はない。
 Cコンパイラは情報がいつも失われないよう、暗黙に
型変換するので結果は「charとcharを足しても情報が
失われないビットサイズ」に変換される。

>char型とchar型の加算の結果はやはりchar型だと思っていました。
unsigned char a;
unsigned char b;
unsigned char c;
a = 255;
b = 255;
c = a + b;
を考えてみよう。a+bは暗黙に変換される。なぜなら
charではビットが失われる。しかしcへの代入で、
charに再度キャストされるので、結局、上記のコードは
おかしなことになる。

この回答への補足

残念ながら、ご回答は、
char型+char型の結果はいつも変換されるという意味なのか、
それとも、その結果を見てchar型では情報が失われるときに変換するという意味なのか、
わかりません。

=================================
ちなみに、sizeofのオペランドとして x+2とか a+1.0 という式を書くのは構わないようです。
柴田望洋という人の書いた『C言語プログラミングの落とし穴』『秘伝C言語問答ポインタ編』という本にはそういう例があります。
(多分ご回答とは関係ないでしょうけれど。)

補足日時:2002/07/31 00:59
    • good
    • 0

C言語では、演算あるいは代入などにおいて、自動的型変換(暗黙的型変換)が行なわれ、char型はint型にキャストされるということではないですか。



尚、int型のサイズは、処理系依存なのでshort型(16bit)の場合とlong(32bit)の場合があります。

下記URLを参考にしてください。
http://www.pro.or.jp/~fuji/mybooks/cdiag/cdiag.4 …

参考URL:http://www.pro.or.jp/~fuji/mybooks/cdiag/cdiag.4 …

この回答への補足

参考URL(「第4章 キャストが好き 自動的型変換」)は私の質問に非常に関係あることで、参考になりました。
ただ、直接的な答えではないように思われました。

質問のプログラムの中には
a[2]=a[0]+a[1];
という式があって、この代入に関してご回答されているのかもしれません。

代入に関して JIS X3010(日本工業規格のプログラミング言語Cの文章)を確認してみると、
代入では、
「右オペランドの値を代入式の型に型変換し、左オペランドで示されるオブジェクトに格納されている値をこの値で置き換える。」(6.3.16.1 の意味規則)
ということが行なわれるそうです。
代入式の型というのは左オペランドの型だと思います。

ですので、
a[2]=a[0]+a[1];
という式に関して言えば、仮に右辺がint型で左辺がchar型だろうと、型の違いの問題はないようです。(右辺がint型なのか、char型なのかは置いておくとして。)

補足日時:2002/07/31 00:38
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!