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

このQ&Aのポイント
  • 質問1:char型の変数はint型のリテラルを代入できるのか?
  • 質問2:インクリメント・デクリメント演算子と複合代入演算子は型を保存するという意味?
  • char型の変数にはキャストしない限りint型の数値を代入できないが、リテラルは代入可能
回答を見る
  • ベストアンサー

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

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型のまま代入されるということでしょうか?

  • Java
  • 回答数3
  • ありがとう数3

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

  • ベストアンサー
  • mitoneko
  • ベストアンサー率58% (469/798)
回答No.1

 どんな言語でも、数字のリテラルに対する変換規則は、とっても複雑なことが多いです。数値が様々な文脈で登場し、全ての場合において妥当な結果を出そうとすると、どうしても複雑にならざるをえないからなんですけど・・・  この質問も、そんな複雑怪奇な規則の上に成り立っています。  まず、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'の形式を使えば、整数代入のことも忘れられます。

wantanton
質問者

補足

かなり丁重でクレバーなご回答誠にありがとうございます! 追加で質問があります。 質問:質問1に対しての以下の回答で疑問点があったので再度質問します。 >でも、 ch=ch + 1 はダメです。右辺が定数式になっていません。(2行まとめて実質定数式じゃないか・・・と言うのは、ダメですよ。行間に一行何かを入>れたら破綻しますからね。) 駄目な理由は例えば、変数を含む計算式の場合、変数は別の行で違う値をとる可能性があるから、定数式の定義「コンパイル時に値が確定し、実行中に変化しない値」の観点から駄目。こういうことでしょうか? 例えば以下のソースコード char ch='a'; ch=ch+1; ではコンパイル時にchは値が確定しない。(ch='a';の下で、chは1をとってしまいますから、char ch='a';をコンパイルしても、次のch=ch+1;をコンパイルしたらアウト、つまり値が確定してない) こういうことでしょうか?

その他の回答 (2)

  • mitoneko
  • ベストアンサー率58% (469/798)
回答No.3

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

wantanton
質問者

お礼

ありがとうございます! すっきりしました! いやーmitonekoさん相当に詳しいですね。 何故そこまで知ってるんですか、、 すごすぎる

  • mitoneko
  • ベストアンサー率58% (469/798)
回答No.2

 補足への追加回答です。  こういうものを考える時は、相手は、コロンとセミコロンが違うだけで、「全く意味わかんねぇぞっ」と胸を張って返答してくる堅物だということを押さえておかないといけません。特に、言語規格を読む時にはそうです。  書いてあることを書いて有るとおりに解釈しないと、往々にして、こんな疑問が発生します。  定数式の定義「コンパイル時に値が確定し、実行中に変化しない値」  はい。意味は合ってます。言いたいことは解りますよ。でも、間違いなんです。定義は意訳してはいけません。意味を斟酌して要約するのは間違いの元です。  あなたの定数式の定義だと、ch=ch+1;の例も定数式と解釈できてしまいます。で困ってるんですよね。  では、コンパイル時の定数式の定義です。  これは、次のものだけを使用して構成された式を意味します。    ・基本型のリテラル及び型Stringのリテラル  ・基本型へのキャスト又は型Stringへのキャスト  ・単項演算子+, -, ~及び!(ただし,++又は--は含まない)  ・乗法的な演算子*, /及び%  ・加法的な演算子+及び-  ・シフト演算子<<, >>及び>>>  ・関係演算子<, <=, >及び>=(ただし,instanceofは含まない)  ・等値演算子==及び!=  ・ビット単位論理演算子&, ^及び|  ・条件AND演算子&&及び条件OR演算子||  ・三項条件演算子? :  ・初期化子が定数式であるfinal変数を参照する単純名  ・初期化子が定数式であるfinal変数を参照する形式TypeName.Identifierなる限定名  ここまで。というわけで、先の例のch=ch+1;の右辺が定数式でない理由は、chが定数式で初期化されたfinal変数を参照するものではなく、リテラルでもないからです。  堅物な回答ですねぇ。でも、相手が堅物ですからしょうが無いです。ここは諦めましょう。で、こんな細かいことをいちいち考えてプログラミングしたくはないですよね。だから、こんな迷宮に踏み込むようなはめになるコードを書くのは、可能な限りやめましょう。としておくのがあなたの精神衛生のためですよと(笑)

wantanton
質問者

補足

お返事遅れてしまい大変申し訳ありません。。 かなり難しいですね、、 頭パンクしそうです。 定数式をネットで調べたところ、以下のような記述がありました。 >定数式には、まずすべてのリテラルが当てはまる。 >次に、そのリテラル同士による計算結果が当てはまる。 >さらに、「リテラル」もしくは「リテラル同士の計算結果」を格納したfinal変数も当てはまる。 ということは、 char ch='a'; ch=ch+1; の場合、自動で縮小変換されないのは、chはファイナル変数でないからということですよね? また、 char ch; ch='a'+1; が自動で縮小変換されるのは、式が「リテラル同士の計算結果」だからですよね?

関連するQ&A

  • キャスト演算子について

    初心者の初歩的な質問ですが、変数の型を変換するキャスト演算子がありますが本を読んでいて思ったのですが変換した式の型はその後どうなるのでしょう? 例で(前略) ----------------------------   int A =0; /*A=0*/ double B =2; /*B=2.000000*/ A = (int)B; B = 3; ----------------------------- とするとA=2 となりますがこの時のBの型はどうなるのでしょう? キャストを使用すると最初に宣言したdoubleがintになってしまうのか?後で代入した3は最初に宣言したdouble型の「3.000000」?それとも変換したint型「3」? 多分最初のままのような気はしますが。よろしくお願いします。

  • 文字列や文字から整数への変換方法について

    文字列(string型)や文字(char型)から整数(int型など)に変換 する時のルールがよく解りません。 たとえば、キーボードから数字を打って、その入力された数字を 数値として整数型の変数に代入したい場合のやり方です。 ちょっとプログラムを作ってみました。 using System; class clmain { private static void Main() { Console.Write("1桁の整数を入れてね "); char ch = char.Parse(Console.ReadLine()); int by1 = (int)ch; int by2 = (int)char.GetNumericValue(ch); Console.WriteLine("by1 = {0}, by2 = {1}, ch = {1}", by1, by2, ch); Console.Write("整数を入れてね "); string st = Console.ReadLine(); /* by = (int)st; コンパイルエラー */ int by3 = int.Parse(st); Console.WriteLine("by3 = {0}, st = {1}", by3, st); } } まず、char型からint型への変換では、  int型変数=(int)char型変数; はコンパイルは通りますけど、実行すると全く違った値が入って しまいます。たとえば char型変数の値が "1" だと、int型変数には 49 が入ります。 int型変数 = (int)char.GetNumericValue(char型変数); と書いてようやく、思い通りの動きをしてくれます。 また、string型からint型への変換では  int型変数=(int)string型変数; はコンパイルエラーになります。 int型変数 = int.Parse(string型変数); とするとコンパイル出来て正しく動きます。 これで質問ですけど、 (1)なぜstring型とchar型で、int型への変換方法が違うのでしょうか? (2)int型変数=(int)char型変数; とすると、上に書いたように、全く 違った(希望しない)値が代入されてしまいます。これは、どういう 動きをしているのでしょうか? また、これはコンパイルエラーに なりませんけど、どういう時にこの書き方をするのでしょうか? 解る方、お願いします。

  • インクリメント/デクリメント演算子を使った計算

    Javaでのインクリメント演算子とデクリメント演算子を 使った計算についての質問です。 int a = 10; int b = a++ + ++a + --a - a--; このコード処理後の変数a,bの値の計算方法(考え方)が つかめず苦戦しています。 解答はa=10,b=22だそうです。 ちなみに例えば次のコードのb=a++;のように 右辺と左辺が1つずつの場合は理解しやすいです。 int a = 1; int b = 0; b = a++; //処理A 処理A後の変数a,bの値は b=aをした後でa=a+1を行うので a=2,b=1 となることは理解できます。 ご教授のほどよろしくお願いします。

  • 文字列と整数型について

    はじめまして。 どうしても困っているのでヒントでも良いのでおねがいします。 関数内(func1)で確保した文字列変数のポインタを 別の関数(func2)にポインタ渡しします。 func2内で整数型で計算した結果を引数で示された文字列変数に 代入するというようなことをしたいと思ってます。 ここで、intは4byteとします。 メモリ長だけで見ると、bit[4] = tmp です。 void func1(){ char bit[4]; func2(bit); return; } void func2(char* p){ int tmp = 0x10101100; p = tmp; <---- ??? return; } そこでどのようにすれば、 代入することができるのか分かりません。 以下のような結果になるように代入したいと思っています。 bit[0] = 0x10; bit[1] = 0x10; bit[2] = 0x11; bit[3] = 0x00; 小さな文字列型に整数型をどのように渡せばよいのかが 一番疑問に思っているところです。 整数型に文字列型を代入する場合には 文字列のバイト指定とシフト演算で実現できています。 ヒントでもよいのでお願いします。

  • Perlインクリメントデクリメント

    あっていますでしょうか? インクリメント演算子とデクリメント演算子 インクリメント(加算)演算子は変数の数値を一つ増やす。 デクリメント(減算)演算子は変数の数値を一つ減らす。 ++ インクリメント演算子 変数の値を1増やす $a++ ++$a $aの値を1増やす -- デクリメント演算子 変数の値を1減らす $a-- --$a $aの値を1減らす $a=1; 変数$aに1を代入する print "はじめは$aでした。\n"; $aの代わりに1が入り はじめは1でした。と表示する。 $a++; $aに1を足す。 print "1増えて$aになりました。\n"; $aの代わりに2が入り、1増えて2になりました。 と表示する。 $a--; $aから1減らす。 print "1減って$aに戻りました。\n"; $aの代わりに1を入れて 1減って1に戻りました。 と表示する ↓ 実行結果 ↓ はじめは1でした。 1増えて2になりました。 1減って1に戻りました。 $a++ と ++$a の違い インクリメント演算子、デクリメント演算子には2通りの書き方があります。 ++$a(前置)と$a++(後置) --$a(前置)と$a--(後置) 前置の場合は変数の参照より先に演算が行われます。 後置の場合は変数の参照より後に演算が行われます。 $x=$a=1; 変数$xと$aに1を代入する。 $x=++$a; $aに1を足した後に$xに代入する。$xの値は2になる。 $x=$a=1; 変数$xと$aに1を代入する。 $x=$a++; 変数$aの値を$xに代入した後に$aに1を足す。$xの値は1。 文字もインクリメント出来ます。 $x="A"; "A"を$xに代入する。 $x=++$x; Aから一つ増やしてBにして$xに代入する。 $x="aaa"; "aaa"を$xに代入する。 "aaa"を一つ増やして"aab"を$xに代入する。 ただし、文字のデクリメントはできません。

    • ベストアンサー
    • Perl
  • 複数の演算子を選択する場合の処理

    複数の演算子を選択するプログラムがあり、選択する演算子によって計算方法を変えたいのですが、このときに今やっている方法はifの条件分岐にて処理を行っておりますが、演算子は変数に代入できないのでしょうか? 例えばべき乗を選択した場合は 変数= ^ また、これと同じく代入演算子の場合も 変数= ^= という方法があれば教えていただきたいです。 よろしくお願いいたします。

  • 評価の概念について質問

    前回も評価の概念について質問しました。ご回答いただいた方へ、誠にありがとうございます。 自分の中でその答えを整理した上で、総仕上げの質問といきます。 質問1:評価とは、「リテラルや変数について、それが『何の表記』で『何の型』で『いくつの(どのような)値』であるかを解釈すること」でしょうか? 例えば、・ソースコード中に 'A' という単語(token)が登場したなら,それは英字表記を用いたchar型の文字値 A だと解釈する 質問2;「式を評価」とは、以下の2つの仮説のうちどちらでしょうか?またどちらでもなければお答え示してくだされば幸いです。 仮説1:「その式を構成する変数やリテラル全てについて、それ等が『何の型』で『いくつの(どのような)値』であるかを解釈すること」 仮説2:仮説1に加え、それ等のリテラルや変数を用いて演算し、答えを求めるプロセスも含む。

    • ベストアンサー
    • Java
  • クラス変数について質問

    Javaのオブジェクト指向を解説した参考書で現在勉強しているのですが、その参考書のある問題の答えの解説として「クラス変数は参照変数なので参照以外を代入できません」と書いてありました。 でも、例えば、static int n=0と書けば、0をnに代入できます。 因みに、「クラス型の変数」は参照しか代入できないのはわかっています。 よって「クラス変数」は誤植で、正しくは「クラス型変数」なのでしょうか?

    • ベストアンサー
    • Java
  • 文字列リテラルの比較について

    下記のプログラムの疑問点を教えて頂きたい。 (1)mainの実引数(ch[0]は、str_chr関数を呼ぶ場合、intの型変更する理由、 (2)cをキヤストしてchar 型変更した後、再度int C に代入する理由 (3)char型*Sとint 型Cでは、型が違うのに何故比較できる理由 char *str_chr(const char *s, int c) { c = (char)c; while (*s != c) { } main { char ch[2]; str_chr(G,ch[0]) main {

  • 評価について質問

    質問1:式を構成する要素(変数やリテラル)の評価と演算について、左から右に評価していく過程で、その式の演算子の優先順位の高い順から、その演算子の取るリテラルの評価が終わり次第、計算をする。 よって、オペランドを取る演算子の位置によっては、評価と演算が交互に行われることもある。 たとえば a*b+cという式があれば、 aを評価→bを評価→*の演算→cを評価→+の演算という計算プロセス。 これはつまり、*は、この式において最も優先順位が高いから、aとbを評価した直後、演算を実行したといえる。 何故僕はこんな質問したかというと、全ての要素の評価が終わってから、演算が実行されるものだと思ったからです。 質問2:「式を評価する」とは、変数やリテラルの評価と優先順位を考慮した演算を実行し、そしてその式の答えを求める一連のプロセスのことでしょうか? 質問3:変数の評価とは、「ソースコードの中でその単語(token)を変数だと解釈することで、そのtokenがjavaの変数名の規約に従っていることかつ、それが事前に宣言されていることを 検査した上で、その変数の値を解釈する」こういうことですか?

    • ベストアンサー
    • Java

専門家に質問してみよう