みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

文字を整数として扱う場合の演算について質問

javaの参考書に、文字を整数として扱う場合の演算についての解説があり、疑問点があったので質問します。

質問1:何故char型の変数は、キャストしなくてもint型のリテラルを代入することができるのか?
     例えば、 以下の演算はキャストしなくてもこのまま代入できます。

char ch='a';
ch=98;



でも、以下の演算はキャストしていないのでエラーになります。     

char ch='a';
ch=ch+1;

これは何故ですか?参考書に記載されていた理由として、「byte,char,short、
これ等の型の変数や値を使って計算すると、それ等は一度intに直して計算されるから」というような趣旨の事が書いてありました。
つまり、char型の変数には、キャストしない限りint型の数値を代入できないということですよね?
でも前者のソースコードは、chはchar型であるにもかかわらず、int型のリテラル98を代入できています。
これは何故ですか?



質問2:javaの参考書に、インクリメント・デクリメント演算子と複合代入演算子は、型を保存するという解説がありました。これはどういう意味ですか?
僕の仮説では、例えば、
     char ch='a';
     ch+=5;
であれば、5は、char型のまま代入されるということでしょうか?

投稿日時 - 2011-05-29 20:44:40

QNo.6772754

すぐに回答ほしいです

質問者が選んだベストアンサー

 どんな言語でも、数字のリテラルに対する変換規則は、とっても複雑なことが多いです。数値が様々な文脈で登場し、全ての場合において妥当な結果を出そうとすると、どうしても複雑にならざるをえないからなんですけど・・・
 この質問も、そんな複雑怪奇な規則の上に成り立っています。

 まず、char型の定義によれば、この型は数値型で、16bitで表現されるとあります。文字コードは、数値で表現されますので、これは、自然なことです。

 さて、2項演算子の演算定義によると、どちらの数値もlongで無ければ、両方の数値はintに昇格しintで演算を行い、その結果は、intを返すと定義されています。

 最後に、代入演算において、プリミティブ型の縮小変換が自動で行われるのは、次の条件を満たす時だけです。
 ・式が型 int の定数式。
 ・変数の型が,byte,short 又は char。
 ・式の値(定数式なのでコンパイル時にわかる)が,その変数の型で表現可能。

 さて、質問1ですが、
 ch=98 の式は、上の全ての条件を満たします。charは16bitですから、十進の98を表現可能ですし、右辺はちゃんと定数式になっています。ですから、intの数値を縮小変換でcharに変換します。
 でも、 ch=ch + 1 はダメです。右辺が定数式になっていません。(2行まとめて実質定数式じゃないか・・・と言うのは、ダメですよ。行間に一行何かを入れたら破綻しますからね。)

 質問2の場合は・・・おもしろいことに、複合代入演算子に関しては、代入時にプリミティブ型の縮小変換を自動ですることが認められているようです。
 従って、いったんintでch+5の演算を行い、intで返された値を、charに代入時に再び変換します。したがって、結果的には、char型が代入されることになりますが、いったんint昇格は発生するようです。

 この話題、結構微妙な面を多く含み、文脈が少し変わると答えも変わることには注意してくださいね。最後は言語規定を注意深く読まないといけません。しかも、この辺は複雑怪奇ですから、私も言語規定まで戻ってから、この文章書いてますけど、間違っている可能性も・・・否定できません(苦笑)

 実際のプログラミングでは、こんな微妙なことにならないように、ちゃんとキャストを明示的に使うのが一番ですし、char型を数字の代わりに使わないことがもっとよいことです。そうすれば、文字コードの数値計算なんてあり得ませんから、こんな話題にも触れなくてOKです。
 文字コードのリテラル表現は、'\u0030'の形式を使えば、整数代入のことも忘れられます。

投稿日時 - 2011-05-29 22:15:36

補足

かなり丁重でクレバーなご回答誠にありがとうございます!
追加で質問があります。

質問:質問1に対しての以下の回答で疑問点があったので再度質問します。


>でも、 ch=ch + 1 はダメです。右辺が定数式になっていません。(2行まとめて実質定数式じゃないか・・・と言うのは、ダメですよ。行間に一行何かを入>れたら破綻しますからね。)


駄目な理由は例えば、変数を含む計算式の場合、変数は別の行で違う値をとる可能性があるから、定数式の定義「コンパイル時に値が確定し、実行中に変化しない値」の観点から駄目。こういうことでしょうか?

例えば以下のソースコード

char ch='a';
ch=ch+1;

ではコンパイル時にchは値が確定しない。(ch='a';の下で、chは1をとってしまいますから、char ch='a';をコンパイルしても、次のch=ch+1;をコンパイルしたらアウト、つまり値が確定してない)




こういうことでしょうか?

投稿日時 - 2011-05-31 16:46:11

このQ&Aは役に立ちましたか?

2人が「このQ&Aが役に立った」と投票しています

回答(3)

ANo.3

 この場合においては、おおむね解釈は合ってますよ。
 
 ただ、文字コードそのものを扱うプログラム(コード変換とか、暗号処理とか)以外では、'a'+1なんて式は使わない方が良いですよ。少なくとも、普通の式ではないですから。

投稿日時 - 2011-06-02 20:52:49

お礼

ありがとうございます!

すっきりしました!

いやーmitonekoさん相当に詳しいですね。
何故そこまで知ってるんですか、、
すごすぎる

投稿日時 - 2011-06-04 01:36:20

ANo.2

 補足への追加回答です。
 こういうものを考える時は、相手は、コロンとセミコロンが違うだけで、「全く意味わかんねぇぞっ」と胸を張って返答してくる堅物だということを押さえておかないといけません。特に、言語規格を読む時にはそうです。
 書いてあることを書いて有るとおりに解釈しないと、往々にして、こんな疑問が発生します。

 定数式の定義「コンパイル時に値が確定し、実行中に変化しない値」
 はい。意味は合ってます。言いたいことは解りますよ。でも、間違いなんです。定義は意訳してはいけません。意味を斟酌して要約するのは間違いの元です。
 あなたの定数式の定義だと、ch=ch+1;の例も定数式と解釈できてしまいます。で困ってるんですよね。

 では、コンパイル時の定数式の定義です。
 これは、次のものだけを使用して構成された式を意味します。
 
 ・基本型のリテラル及び型Stringのリテラル
 ・基本型へのキャスト又は型Stringへのキャスト
 ・単項演算子+, -, ~及び!(ただし,++又は--は含まない)
 ・乗法的な演算子*, /及び%
 ・加法的な演算子+及び-
 ・シフト演算子<<, >>及び>>>
 ・関係演算子<, <=, >及び>=(ただし,instanceofは含まない)
 ・等値演算子==及び!=
 ・ビット単位論理演算子&, ^及び|
 ・条件AND演算子&&及び条件OR演算子||
 ・三項条件演算子? :
 ・初期化子が定数式であるfinal変数を参照する単純名
 ・初期化子が定数式であるfinal変数を参照する形式TypeName.Identifierなる限定名

 ここまで。というわけで、先の例のch=ch+1;の右辺が定数式でない理由は、chが定数式で初期化されたfinal変数を参照するものではなく、リテラルでもないからです。

 堅物な回答ですねぇ。でも、相手が堅物ですからしょうが無いです。ここは諦めましょう。で、こんな細かいことをいちいち考えてプログラミングしたくはないですよね。だから、こんな迷宮に踏み込むようなはめになるコードを書くのは、可能な限りやめましょう。としておくのがあなたの精神衛生のためですよと(笑)

投稿日時 - 2011-05-31 21:35:06

補足

お返事遅れてしまい大変申し訳ありません。。


かなり難しいですね、、
頭パンクしそうです。

定数式をネットで調べたところ、以下のような記述がありました。

>定数式には、まずすべてのリテラルが当てはまる。
>次に、そのリテラル同士による計算結果が当てはまる。
>さらに、「リテラル」もしくは「リテラル同士の計算結果」を格納したfinal変数も当てはまる。

ということは、
char ch='a';
ch=ch+1;

の場合、自動で縮小変換されないのは、chはファイナル変数でないからということですよね?

また、
char ch;
ch='a'+1;

が自動で縮小変換されるのは、式が「リテラル同士の計算結果」だからですよね?

投稿日時 - 2011-06-02 13:44:38

あなたにオススメの質問