• ベストアンサー

int型とchar型について

C言語初心者です。 よろしくお願いします。 ◎1----------------------- #include<stdio.h> int main(void) { int ss[4]="789"; printf("%c\n",ss[0]); return 0; } --------------------------- ◎2----------------------- #include<stdio.h> int main(void) { int *p; p="789"; printf("%c\n",*p); return 0; } --------------------------- ◎1、◎2の2つのプログラムについて疑問があります。 ◎1の「int ss[4]="789";」と◎2の「int *p;」のintの部分は今まで、何の疑問も抱かず、「char」として入力していました。 そこでchar型は1バイトの整数、int型は4バイトの整数ということで容量が違うだけで、intとしても大丈夫だろうと思ったのですが、 ◎1では、「'initializing' : 'char [4]' から 'int [4]' に変換することはできません。」とエラーが出て、◎2では「'char [4]' から 'int *' に変換することはできません。」とエラーが出ます。 intは文字列は扱えないということなのでしょうか? 以上intだと実行できない理由がわかりません。 初歩的なことですいませんが、教えていただけると嬉しいです。

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

  • ベストアンサー
  • goosyu
  • ベストアンサー率58% (36/62)
回答No.7

>intは文字列は扱えないということなのでしょうか? >以上intだと実行できない理由がわかりません。 →"789”は 文字列リテラルと呼ばれchar型の配列としてC言語仕様で決められ,初期化する場合もchar型の配列とされています。  この為「int ss[4]="789";」はコンパイルエラーとなることが予想されます。  int型の配列に文字コードを格納したいのであれば「int ss[]={'7','8','9','\0'};」とすべきで,扱いにくいですがint型配列で文字列を格納したことになります。 >printf("%c\n%d\n",*p,*p); >この*pは7のアドレスの値を指しているのに、 >55× 256^0+56×256^1+57×256^2+0×256^3 >以上の'8''9'も含めた計算が行われたのかが疑問としてあります。 →まずこの例はC言語仕様とは関係なく,VC++環境(CPU x86系)でのメモリ操作がからんだ結果ですので全ての実行環境で同じ結果にはならないことを注意して下さい。  「int *p = (int *)"789";」はchar型の配列で'7','8','9','\0'の順に文字コードが格納されその先頭アドレスを「int*型」として「int *p」に代入し初期化されています。  この「p」のアドレスが仮に0x1000とした場合,次の様に文字列が格納されています。この実行環境の'0'~'9'の文字コードは0x30~0x39が割り当てられています。 0x1000 0x37 ('7') <--- int *pの指すアドレス 0x1001 0x38 ('8') 0x1002 0x39 ('9') 0x1003 0x00 ('\0')  ここで「int *型」の「p」を「*p」とすると,この環境の「int型」は32ビット(4バイト)ひと固まりとして処理され「*p = 0x00393837;」と結果が得られます。順番通り取り出すと「0x37383900」となるように考えるのが自然かもしれませんが,「CPU x86系」ではこの様な取り出し方ではなくローバイトから取り出し「0x00393837」となります。  「0x00393837」は10進数で「3749943」ですのでこの結果になります。 ちなみに「printf("%c\n%d\n",*p,*p);」の最初の「%c」で「7」と出力されていましたが,*pは「0x00393837」ですので下位8ビット「0x37」が出力されたことが予測されます。 >p=(int *)"789";のようにint型にしてpにアドレスを渡すと、他の回答者さんにも質問したのですが、' 'の1つを1バイトのメモリと考えると、 >「'7','<不定>','<不定>','<不定>'」・・・や >「'  7  ','  8  ','  9  ','  0  '」 >ではなく、'' ''1つを4バイトとして、 >「''          7           ''」・・・ >のように、4バイトの領域1つ1つに'7''8''9'が入っていくという事なのですかね?? →いいえ,(int *)でキャストしてもchar型の配列で格納されていますので1バイト毎にそれぞれ格納され合計で4バイトとなります。 >あともう1つ疑問なのですが、 >>この4バイトの数値をint型として読み込むと >>55×256^0+56×256^1+57×256^2+0×256^3となります。 > >という回答をもらった部分なのですが、1バイトが256でintは4バイトという事で、256の部分は、256^4の値が入るとまだ完全に理解していないので、そう思ってしまったのですが、 >>55×256^0+56×256^1+57×256^2+0×256^3 >の部分を説明していただけると助かります。 →4つのキーワードから考えると「256^0」,「256^1」,「256^2」, 「256^3」の4つが対応します。  まず式を全て16進数に変えて256^n(重み)はどの様なものか確認します。   3749943(0x00393837) = 55(0x37)*256^0+56(0x38)*256^1+55(0x39)*256^2+0(0x00)*256^3   0x00393837 = 0x37*0x01(256^0)+0x38*0x0100(256^1)+0x39*0x010000(256^2)+0x00*0x01000000(256^3)  ・次に足す順番を入れ替えて計算結果と見比べます。   0x00393837 = 0x00*0x01000000 + 0x39*0x010000 + 0x38*0x0100 + 0x37*0x01   0x00393837 = 0x00000000 + 0x390000 + 0x3800 + 0x37   このように8ビット毎に重みを付けて計算しています。

muffler
質問者

お礼

ご回答ありがとうございます。 >(int *)でキャストしてもchar型の配列で格納されていますので1バイト毎に >それぞれ格納され合計で4バイトとなります。 以上のご回答がどうも理解できないのですが、 printf("*p=%c\n*p+1=%c\n",*p,*p+1); printf("p=%p\np+1=%p\n",p,p+1); 以上を入力すると、1つ目のprintfで、 *p=7 *p+1=8 出力されるのはわかるのですが、 2つ目のprintfは、 p=0042002C p+1=00420030 と出力され(C→0なので)'8'は次の4バイト目に格納されていると理解し、1バイト毎にそれぞれ格納とは違うと理解してしまったのですが、違いますかね? あと、 >「int *型」の「p」を「*p」とすると,この環境の「int型」は32ビット >(4バイト)ひと固まりとして処理 とあるのですが、この記述がどうも理解できません。 以上ご回答いただけると嬉しいです。

その他の回答 (14)

回答No.15

> ちょっと話しは変わってしまうのですが、今C言語を猛勉強中で参考書としては、 > 「著 林晴比古 新訂 新C言語入門シリーズ」や「著 柴田望洋 明解C言語 実践編」等で > 勉強中なのですが、今回質問でのやりとりような、細かい所までは記載されていないことが多いです。 > 何か、細かい所まで掘り下げているようなおススメの参考書はありませんか?? > 自分からも積極的に勉強していかなければいけないなぁと痛感いたしまして。 とりあえず、今回感じた疑問については他の回答者さんと一緒にお答えさせて 頂きましたが、まずはその提示されている本で記載されていることをマスターしましょう。 で、何かおかしなことになった際は、一般的な(本に書いてある)使い方と 何かが違っているかを確認しましょう。 デバッガを使用して、思った通りの値になっているかどうかの確認も大事です。 今回の質問についてはコンパイラがどのような処理をするか?がメインであって、 C言語をどのように書いていけばいいのか?ではないと思います。 深いところは知っていれば知っていたでプログラミング及びデバッグに 役に立つかとは思いますが、奥が深い(実はそれ程でもないけど)割には それ程効果的ではないです。 結局、変な書き方をしなくなる、つまり、本で書いてあるように正しく 使うようになる、といったところでしょうか? と、書きつつ、深いところを知ることは悪いことではないので... Cは結局アセンブリ言語に変換(しているともいえなくもない)のでそういった 意味ではインラインアセンブラを勉強してみるのは有効かと思います。 簡単にググってみて、結構わかりやすい(と思われる)サイトが ありましたのでお知らせします。 インラインアセンブラで学ぶアセンブリ言語 http://codezine.jp/article/corner/67

muffler
質問者

お礼

ご回答ありがとうございます。 確かに今持っている参考書をマスター出来てはいないので、まずは参考書の記載をしっかり読んでマスター出来るようにします(;^_^A 載せていただいたサイトも参考にさせてもらいます! ありがとうございました。

  • goosyu
  • ベストアンサー率58% (36/62)
回答No.14

>0x0012FF78 : 0x00 0x00 0x0012FF7A : 0x00 0x0012FF7B : 0x00 0x0012FF7C : 0x00 0x0012FF7D : 0x00 0x0012FF7E : 0xF0 0x0012FF7F : 0x3F >となりましたが、0xF0('240')や0x3F('?')などよくわからないものが入っていたのですが、double型の先頭アドレスをchar型ポインタに代入したことにより、おかしな値となっているという感じですかね? 【→】「0x3FF0000000000000」には意味があります。  64ビットの構成はIEEE 754により次の様に決められています。  符号部(1ビット)、指数部(11ビット)、仮数部(52ビット)  「0x3FF0・・・」を2進数にすると「0011 1111 1111 0000 ・・・」となので,それぞれの項目に値を入れると符号部は「0」(0は正の値、1は負の値)、指数部は「011 1111 1111」(10進数で1023)、仮数部は「0000・・・」(10進数で0)です。  値を求めるには, 2^(指数部の値 - 1023) * (1 + 仮数部の値 * 2^-52)という計算です。  = 2^(1023 - 1023) * (1 + 0 * 2^-52)  = 2^0*1 = 1  ということで「0x3FF0000000000000」は1です。ちなみに0.5だと指数部が1022で値「0x3FE0000000000000」,2だと指数部が1024で値「0x4000000000000000」になります。double型ではint型のように"1"が簡単に判断できないこと説明したかったのですが伝わらなかったようです。    興味があるのであれば「浮動小数点数」,「IEEE 754」でインターネット検索して下さい詳しく説明があります。   >char型の-128が16進数で0x80なら、 >char c = -128; >printf("%02x", c); >の出力結果は0x80になり、 >int型の-128が0xffffff80なら、 >int c = -128; >printf("%02x", c); >の出力結果が、0xffffff80になると思ったのですが、両方とも0xffffff80となります。 >ご回答いただいた、>char型からint型に代入された結果,0xffffff80になります。 >がヒントのような気がするのですが、やはり何故そうなるか考えつきません。 【→】printf()関数の"%x"は整数型(int型)の値を16進数文字列として出力する仕様になっているので,期待しているchar型(もしくは1バイト)の値を出力する仕様ではありません。結果的にint型の値として処理されたと思われます。  N-Ishikawaさんからの回答があるので詳細な説明は省略します。

muffler
質問者

お礼

ご回答ありがとうございます。 doubleなので浮動小数点形式に直してから、計算すると入力値が出てくるということですね。浮動小数点についてもう一度復習してから、じっくり考えたいと思います。 >printf()関数の"%x"は整数型(int型)の値を16進数文字列として出力する仕 >様になっているので,期待しているchar型(もしくは1バイト)の値を出力す >る仕様ではありません。結果的にint型の値として処理されたと思われま >す。 以上のご回答理解しておきます!

回答No.13

goosyuさんに横やりで....(^^; > char c = -128; > printf("%02x", c); > の出力結果は0x80になり、 > > int型の-128が0xffffff80なら、 > int c = -128; > printf("%02x", c); > の出力結果が、0xffffff80になると思ったのですが、両方とも0xffffff80となります。 > ご回答いただいた、 > >char型からint型に代入された結果,0xffffff80になります。 > がヒントのような気がするのですが、やはり何故そうなるか考えつきません。 私自身の見解がかなり強く入っている感じがしなくもないのですが、 今回のプログラムは 32bitで生成される、ということで 引数が 32bit単位のようです。 つまり、32bit以下のデータ型は32bitに拡張されることになるのですが、 引数も32bitに拡張されることになるのです。 変数そのものではなく、変数の中身である-128を引数に渡すのですから printf()に渡す際は32bitに拡張されて0xffffff80になる、ということです。 因みに.....そもそも printf()は第一引数は文字列である必要がありますが、 第2引数以下はコンパイル時はなんでも受け付けます。 で、引数にどんな型で受け付けたかの判別は プログラム内で書式指定した %cや%d等で行います。 つまり....%dや%xと書いたら引数に何を設定しようとその値を int型として読み込むことになります。 char型を渡しても結局 int型になるので、データの不整合自体は起きていませんが、 結局思った通りに動かないと意味はないので、注意する必要があります。 「printf("0x%02x",(unsigned char) c);」に変更すると0x80と表示されますが、 これは 0x80を正負の数ではなく、正の数値である128として渡してます。(^^;;; charは数値としても使用可能ですが、あくまで文字として定義されている型なので その目的にあった使い方をしましょう。 #char型は大抵、文字列ポインタとして使用することが多いので、 #こういったことには悩んだりしないかと。

muffler
質問者

お礼

ご回答ありがとうございます。 >引数にどんな型で受け付けたかの判別は プログラム内で書式指定した >%cや%d等で行います。 >つまり....%dや%xと書いたら引数に何を設定しようとその値を >int型として読み込むことになります。 >charは数値としても使用可能ですが、あくまで文字として定義されている型 >なのでその目的にあった使い方をしましょう。 以上のご回答理解できました。 しっかり頭に入れておきます! ちょっと話しは変わってしまうのですが、今C言語を猛勉強中で参考書としては、「著 林晴比古 新訂 新C言語入門シリーズ」や「著 柴田望洋 明解C言語 実践編」等で勉強中なのですが、今回質問でのやりとりような、細かい所までは記載されていないことが多いです。 何か、細かい所まで掘り下げているようなおススメの参考書はありませんか?? 自分からも積極的に勉強していかなければいけないなぁと痛感いたしまして。

  • goosyu
  • ベストアンサー率58% (36/62)
回答No.12

>long longはうちのコンパイラでは使えませんでした。諦めます。。 【→】Visual C++ (Visual Studio 97)なんですかね。long long型を使うことは当分ないと思いますので今は基本をおさえてからでいいと思います。 >>{0x37,0x38,0x39,0x32,0x31,0x00}の値(6バイト)までしか定義していない >>のでdouble型(8バイト)に足りていません。残り2バイト分は不定の値となり >>double型の値自体が不定値となります。 >とありますが、intで実行し"78"とした場合、*p+1は0x00003838と0で埋めてくれたのですが、もしdoubleで実行できた場合、足りないバイトは0で埋めてくれないのですかね? 【→】多分私の説明で不定値という言葉で何度か説明したことに関係しますが"78"は'7','8','\0'と3バイトから構成され4バイト目は不定値となります。不定値は0でもいいですし255かもしれません。今回「0x00003838」と表示されたのは確かに4バイト目には0x00が格納されていたのでしょう。4バイト目に0x00が格納されていたのは,たまたまそうなったという回答になります。 >あと、・・・の載せていただいたプログラムは、数値を入力すると、 >Debug Error! >Program: C:\install\C++\MSDev98\MyProjects\practice\Debug\practice.exe >runtime error >と出て実行出来ませんでした。 >aやbの文字では実行出来ました。 【→】何とランタイムエラーですか・・・。'a','b'は数字でないのでscanf()関数が処理しないのでそれで実行出来たのでしょう。  runtime error R6002 とかアルファベット1文字と数字が一緒に出ていませんでした? #エラーが出た場合は出た行番号とエラー番号は必ずメモしておいて下さい。デバッグの役に立ちます。  ちょっとしらべましたが浮動小数点のライブラリのリンクが出来ていないのが原因かもしれません。お手数ですが次の行を差し替えてもう一度トライしてもらえませんか?  修正前 double v;  修正後 double v = 0.0; /* 浮動小数点演算ライブラリをリンクする為に0.0記述が必要らしい。 */ >ちなみに載せていただいた、プログラムの、 >printf("0x%08X : 0x%02X\n", pC, 0xff & *pC); >の部分の%02Xに対応するのは*pCとはわかるのですが、'0xff'(ちなみに何故ff?)や'&'(&単体での使用は??)を初めて見たのでどのように作用してるのかわかりません。 【→】この「&」アンパサンドについてはビット演算子またはビット論理積を調べて下さい。 "0xff & *pC"は0xffと*pCをビット論理積をとることになります。例えば「0x0123 & 0xff」と書いた場合結果は 0x0023となります。ここからはちょっと難しいですので軽く読み流してもらってもいいです。  char c = -128; /* 16進数では0x80 */ printf("%02x", c);  この結果は ffffff80 と表示されたはずです。本来は80と表示したいはず。そこで力技(結果重視で一般的方法ではない)で0xffとビット論理積をとって下位8ビットだけを残して表示(出力)させました。「 printf("%02x", 0xff & c); 」  int型(0xffffff80)は10進数で-128でchar型(0x80)は10進数で-128と同じ値を表しています。※  char型からint型に代入された結果,0xffffff80になります。それを承知の上で下位8ビットを取り出して80を出力させました。※-128がなぜ0x80なのかは8ビットでの2の補数表現を使っているからですが説明が長くなるので省略します。  今考えると次の様に書く方法が妥当かもしれません。「printf("%02x", *(unsigned char *)&c);」これはcのアドレスはcharポインタ型ではなくunsigned char ポインタ型とキャストし,その値としてます。この結果,符号なしの値0x80からint型に代入しても0x00000080となりますのでビット論理積を行う必要はありません。

muffler
質問者

お礼

ご回答ありがとうございます。 >4バイト目に0x00が格納されていたのは,たまたまそうなったという回答に >なります。 以上の内容頭に入れておきます! あと、載せていただいたプログラムは、 double v = 0.0; としたら実行できました。ありがとうございます。 double型の値がメモリにどのように格納されるか確認出来ました! 論理積等のご回答は、前に一度勉強したのでもう一度復習してみます! 本当にすいません。そこでまた質問お願いします。「0xff & *pC」の示す値で、1を入力した場合、 0x0012FF78 : 0x00 0x0012FF79 : 0x00 0x0012FF7A : 0x00 0x0012FF7B : 0x00 0x0012FF7C : 0x00 0x0012FF7D : 0x00 0x0012FF7E : 0xF0 0x0012FF7F : 0x3F となりましたが、0xF0('240')や0x3F('?')などよくわからないものが入っていたのですが、double型の先頭アドレスをchar型ポインタに代入したことにより、おかしな値となっているという感じですかね? あとすいません。もうひとつお願いしますm(_ _)m char型の-128が16進数で0x80なら、 char c = -128; printf("%02x", c); の出力結果は0x80になり、 int型の-128が0xffffff80なら、 int c = -128; printf("%02x", c); の出力結果が、0xffffff80になると思ったのですが、両方とも0xffffff80となります。 ご回答いただいた、 >char型からint型に代入された結果,0xffffff80になります。 がヒントのような気がするのですが、やはり何故そうなるか考えつきません。 いつも新たな質問を繰り返してしまいすいません。 ご回答いただけたらありがたいです。

  • goosyu
  • ベストアンサー率58% (36/62)
回答No.11

>以上多々のアドバイスにより、"789"の先頭アドレスがchar型として設定されているとわかりました。 >そこで'7''8''9''0'はメモリ上のどこかにあるという考えでよいのでしょうか? 【→】そうですね。メモリ上のどこかにあるという認識でいいと思います。 >32ビット(4バイト)ひと固まりとして処理され「*p = 0x00393837;」 >とありましたが、'00'で1バイト'39'で1バイト'38'で1バイト'37'で1バイトの合計4バイトでよいのでしょうか? 【→】はい。 >そうならば今回は、「'7','8','9',0」でちょうど4バイト0x00393837と表示されましたが、例えば"78921"とした場合*pで全>部表示させる事はできるのでしょうか? 【→】目的がpの指す文字列を表示するというのであれば「printf("%s\n", p);」です。これ以外の使い方はしません。  *pで全部といわれてもint型はこの実行環境の場合32bitなので「0x00393837」までしか格納できませんから,全部表示は32ビットまでとなります。  どうしても勉強の為に同じような動きをさせたいのであれば64ビットの「long long 型」で同じようなことは出来ます。ただし「long long」型が対応していないCコンパイラもありますので実行出来るかは開発環境によります。  とりあえずVisualC++2008で動作確認したサンプルを貼っておきます。 #include <stdio.h> void main(void) { long long *p = (long long *)"7892100"; printf("%%cは'%c'\n", *p+1); printf("%%lldは%lld\n", *p+1); printf("%%llXは0x%016llX \n", *p+1); } >ちなみに、doubleは8バイトということで、 >double *p; >p=(double *)"78921"; >printf("%%cは'%c', %%dは%d, %%Xは0x%08X \n", *p+1, *p+1, *p+1); >としても、%cは表示されず、%Xは全部0となっていました。 【→】まず感想として新鮮な発想ですね。(いい意味でとらえて下さいね。文章だと伝わらないと困るので補足します。)  「p=(double *)"78921";」は char x[] = {0x37,0x38,0x39,0x32,0x31,0x00}; のような配列の先頭アドレスをdouble型のポインタに代入しています。  「printf("%%cは'%c', %%dは%d, %%Xは0x%08X \n", *p+1, *p+1, *p+1);」の「*p+1」はdouble型の値+1の計算してdouble型の値をprintf()関数の引数に渡しています。「printf("%f",*p+1);」のような記述でなければdouble型の値は表示される保証がありませんし実行すべきでもありません。また{0x37,0x38,0x39,0x32,0x31,0x00}の値(6バイト)までしか定義していないのでdouble型(8バイト)に足りていません。残り2バイト分は不定の値となりdouble型の値自体が不定値となります。  8バイトでdouble型を選択したのはdouble型(実浮動小数点型)の値の格納されかたが整数型(int型)にくらべ複雑ということを知らない為と考えます。次のプログラムを実行してdouble型の値がメモリにどのように格納されるか確認して下さい。 #include <stdio.h> void main(void) { double v; int i; char *pC; scanf("%lf", &v); /* 最初は1,2とか入力して格納内容が単純に変化しないことを確認して下さい */ pC = (char *)&v; /* double型の先頭アドレスをchar型ポインタに代入 */ for (i=0;i<sizeof(v);i++) { /* 8回ループします。*/ printf("0x%08X : 0x%02X\n", pC, 0xff & *pC); /* 「ポインタの指すアドレス : その値」という形式で出力 */ pC++; /* ポインタを加算していく */ } } この様にdouble型の値の格納は簡単ではないので,逆に{0x37,0x38,0x39,0x32,0x31,0x30,0x30,0x00}の値をdouble型の格納されているとして値を参照「*p」しても値の確認は難しいと考えます。 最後に私の回答には偏ったアドバイスがあるかもしれませんので,一度この質問を総括して,別件については新しく質問をされた方が一般的な回答が得られると思います。ここの回答者は的確にフォローしてくれる方が多いので。

muffler
質問者

お礼

ご回答ありがとうございます。 long longはうちのコンパイラでは使えませんでした。諦めます。。 >{0x37,0x38,0x39,0x32,0x31,0x00}の値(6バイト)までしか定義していない >のでdouble型(8バイト)に足りていません。残り2バイト分は不定の値となり >double型の値自体が不定値となります。 とありますが、intで実行し"78"とした場合、*p+1は0x00003838と0で埋めてくれたのですが、もしdoubleで実行できた場合、足りないバイトは0で埋めてくれないのですかね? あと、 #include <stdio.h> void main(void) { double v; int i; char *pC;   ・   ・ の載せていただいたプログラムは、数値を入力すると、 Debug Error! Program: C:\install\C++\MSDev98\MyProjects\practice\Debug\practice.exe runtime error と出て実行出来ませんでした。 aやbの文字では実行出来ました。 ちなみに載せていただいた、プログラムの、 printf("0x%08X : 0x%02X\n", pC, 0xff & *pC); の部分の%02Xに対応するのは*pCとはわかるのですが、'0xff'(ちなみに何故ff?)や'&'(&単体での使用は??)を初めて見たのでどのように作用してるのかわかりません。 以上何度もご丁寧に回答くださって大変申し訳ありませんが、またご回答いただけたら嬉しいです。

  • goosyu
  • ベストアンサー率58% (36/62)
回答No.10

別途回答ということで(N-Ishikawaさん割り込んですいません。少し自重します。) >printf("*p=%c\n*p+1=%c\n",*p,*p+1); >printf("p=%p\np+1=%p\n",p,p+1); >以上を入力すると、1つ目のprintfで、 >*p=7 >*p+1=8 >出力されるのはわかるのですが、2つ目のprintfは、 >p=0042002C >p+1=00420030 >と出力され(C→0なので)'8'は次の4バイト目に格納されていると理解し、1バイト毎にそれぞれ格納とは違うと理解してしまったのですが、違いますかね? →少し違います。  intポインタ型(int*型)はこの実行環境で確かに4バイト単位で操作されますが,「int *p=(int*)"789";」はint型ポインタにはchar型の配列の先頭アドレスが初期値として代入されているだけです。この為,4バイト毎に値は格納されません。  もう少し詳しく説明します。  1.「int *p=(int*)"789";」はどの様に考えるか   「"789"」は文字列リテラルなのでchar型の配列と考えます。   この実行環境ではメモリに1バイト単位で0x37,0x38,0x39,0x00の順に格納されます。   そのchar型配列の先頭アドレスはcharポインタ型(char*型)には代入出来ますが,intポインタ型(int*型)には型変換(キャスト)が必要になります。この為「int *p="789";」に「(int*)」が追加されています。   ここで注意してほしいのは「p」にはchar型の配列の先頭アドレスが代入されているだけで,char型の配列には一切変更はされません。  2.「*p」の値について   「int *p=(int*)"789";」と記述された場合,今回の実行環境で「*p」は「0x00393837」です。   「int *p;」と宣言して「*p」と記述した場合はint型(この実行環境では32ビット)の値を返します。   「*p+1」は「0x00393837 + 1」と同じなので「0x00393838」です。もし「p」の次の項目を参照したい場合は「*(p+1)」または「p[1]」と記述します。   「printf("%c",*p+1);」は「0x00393838」の下位8ビット「0x38」を文字コードとして「8」と出力されています。   この為,結果的'8'と見えているだけです。  【確認方法】   この動作を確認したいのであればデバッガのウォッチ機能で「*p」値を確認するか「printf("%c\n", 0x00393838);」を実行して下さい。同じ結果になった思います。   「printf("%%cは'%c', %%dは%d, %%Xは0x%08X \n", *p+1, *p+1, *p+1);」の結果を見てprintf()動作と値の確認が出来ると思います。 >あと、 >>「int *型」の「p」を「*p」とすると,この環境の「int型」は32ビット >>(4バイト)ひと固まりとして処理 >とあるのですが、この記述がどうも理解できません。 →int型は実行環境(またはCコンパイラ)によって16ビット,32ビットなど整数型(int型)の記憶サイズが異なりますので明確にする為,”この環境の「int型」は32ビット”と記述しています。

muffler
質問者

お礼

ご回答ありがとうございます。 >intポインタ型(int*型)はこの実行環境で確かに4バイト単位で操作されます >が,「int *p=(int*)"789";」はint型ポインタにはchar型の配列の先頭アド >レスが初期値として代入されている >「p」にはchar型の配列の先頭アドレスが代入されているだけで,char型の >配列には一切変更はされません。 以上多々のアドバイスにより、"789"の先頭アドレスがchar型として設定されているとわかりました。 そこで'7''8''9''0'はメモリ上のどこかにあるという考えでよいのでしょうか? あと*p+1では"789"と並んでいる8が表示されたのではなく、下位8ビットに+1され、'7'が'8'になったとわかりました。 32ビット(4バイト)ひと固まりとして処理され「*p = 0x00393837;」 とありましたが、'00'で1バイト'39'で1バイト'38'で1バイト'37'で1バイトの合計4バイトでよいのでしょうか? そうならば今回は、「'7','8','9',0」でちょうど4バイト0x00393837と表示されましたが、例えば"78921"とした場合*pで全部表示させる事はできるのでしょうか? ちなみに、doubleは8バイトということで、 double *p; p=(double *)"78921"; printf("%%cは'%c', %%dは%d, %%Xは0x%08X \n", *p+1, *p+1, *p+1); としても、%cは表示されず、%Xは全部0となっていました。 以上、ご回答いただければ嬉しいです。

回答No.9

別途回答があるかとは思いますが..... >>p=(int *)"789";のようにint型にしてpにアドレスを渡すと、他の回答者さんにも質問したのですが、' 'の1つを1バイトのメモリと考えると、 >>「'7','<不定>','<不定>','<不定>'」・・・や >>「'  7  ','  8  ','  9  ','  0  '」 >>ではなく、'' ''1つを4バイトとして、 >>「''          7           ''」・・・ >>のように、4バイトの領域1つ1つに'7''8''9'が入っていくという事なのですかね?? >→いいえ,(int *)でキャストしてもchar型の配列で格納されていますので1バイト毎にそれぞれ格納され合計で4バイトとなります。 念のため、ですけど、プログラム中に記載された"789"は生成されたプログラムの どこかに「'7','8','9',0」の4バイト文字が固定で設置されています。 これをリテラル(定数)といいます。 P="789"というのはその設置されているアドレスをpに代入しているだけです。 *pというのは pが指すアドレスから pを宣言した型のサイズ分を値としてみいるだけです。 だから決して値が入っていく....ということはないのです。 因みにご存じだとは思いますが、文字列のコピーを行う関数として strcpy()があります。 こちらは値をコピーしていくもので、文字通り、値が入っていく...に 近いものがあります。 p="789"の場合 ・p(アドレス)の値が変更される。 ・"789"は元々固定値としてプログラム中に存在。 ・"789"はプログラム中に存在している領域でもあるため領域確保は不要。 strcpy(p,"789")の場合 ・p(アドレス)の値は変わらず。 ・pが指すアドレスから '7','8','9',0の4バイトが挿入される。 ・プログラム中に格納されている"789"の領域とは別の領域にコピーする関数で  あるため、pに領域確保が必要。 で、何が言いたいか、というと、"789"と書いた時点でプログラム中の データ格納イメージが決定されていることになっています。 それを intとか指定してもアドレスを入れるだけの式に対してどうにもできないのです。 >>「int *型」の「p」を「*p」とすると,この環境の「int型」は32ビット >>(4バイト)ひと固まりとして処理 >とあるのですが、この記述がどうも理解できません。 分かりづらいのであれば 配列変数に置き換えるといいです。 int *p → int p[4]; *pと宣言したのであれば p(アドレス)が変数なので アドレス変更ができ、 p[4]と宣言したのであれば既に領域確保したところを指しているためpは アドレス変数ではなく、アドレスの定数である、という違いはあります。 ただ、どちらの宣言でも p[0]と書けば pが意味するアドレスからの1つの データを扱いますし、p[1]はその次のデータを表します。 pが intで宣言されたのであれば intは4バイトですから p[0]とp[1]の 先頭アドレスの差は4バイトあることになります。 pとp+1の差が4だという理由はそこにあります。

muffler
質問者

お礼

ご回答ありがとうございます。 >P="789"というのはその設置されているアドレスをpに代入しているだけで >す。 >*pというのは pが指すアドレスから pを宣言した型のサイズ分を値としてみ >いるだけです。 多々のアドバイスによりだんだんわかってきました。 "789"という文字列リテラルの先頭アドレスを渡しているという考えにより、*pの表示が何を意味しているのかもう一歩でわかりそうです!

回答No.8

回答2です。 >printf("%c\n%d\n",*p,*p); >この*pは7のアドレスの値を指しているのに、 >55× 256^0+56×256^1+57×256^2+0×256^3 >以上の'8''9'も含めた計算が行われたのかが疑問としてあります。 このケースの場合、printf()には *pとして両方とも 3749943を 渡しています。 別に計算などしていません。 この文を実行してみてもわかるかと思います。 printf("%c\n%d\n%c\n",*p,*p,3749943); >>55×256^0+56×256^1+57×256^2+0×256^3 >の部分を説明していただけると助かります。 goosyuさんのコメントもありますので、こちらでは簡単に 10進数の話をしたいと思います。 ABCで表現される R進数の数値、というのは10進数に変換するとき、 以下式を使用すれば算出できます。 A×R^2 + B×R^1 + C×R^0 10進数で789という数値は以下のように表現できます。 789 = 7×10^2 + 8×10^1 + 9×10^0 ここで、C言語において "789"で表現される1文字1文字は、8bitで表現 できる値ですから0~255までの256個あります。 つまり、256進数(一般的にはそんな言い方しませんが)となるわけです。 '7'、'8'、'9'の文字コード55、56、57及び、256進数で表される数値 ということで以下表現を用いたわけです。 55×256^0+56×256^1+57×256^2+0×256^3 (並び順を逆にしてますが、そこはご了承願います) 実際は1バイト=2桁の16進数ですが、どちらにしろ 0~255の 256個の値を表現できることに代わりはないです。 因みに メモリの格納順と桁位置は逆になっています。 これはINTELのCPUが リトルエンディアンを採用しているからとなります。 詳しくはWikipediaをどーぞ http://ja.wikipedia.org/wiki/%E3%82%A8%E3%83%B3%E3%83%87%E3%82%A3%E3%82%A2%E3%83%B3

muffler
質問者

お礼

ご回答ありがとうございます。 1文字1文字は、8bitということで、2^8通りの数値があるので、256進数となり、55×256^0+56×256^1+57×256^2+0×256^3のとなるのわかりました。 ありがとうございます。

回答No.6

回答2です。 > ◎2に関しては、まず「int *p;」ではエラーが出るので、「char *p;」に変え、 > 「printf("%c\n%d\n",*p,*p);」を追加し実行してみたところ、 > > 7 > 55 > > が出力されました。 すみません。(2)はコンパイルできているものだと勘違いしていました。 「int *p;」のまま、「p="789";」を「p=(int *)"789";」に変えてみたらどうでしょう? #実際にVC++で試したので、ちゃんと動作するかと思います。 -------------------------------------- #include<stdio.h> int main(void) { int *p; p=(int *)"789"; printf("%c\n%d\n",*p,*p); return 0; } -------------------------------------- VC++で確認しているのであれば、7と3749943が出力されるハズです。 > 文字としての7、10進数としての55という感じですかね?? 意図は 同じ *pでも中身は....ということを言いたかったのです。 確かに文字としての7は55ですが..... 因みにこの例は、メモリ上には「'7','8','9','\0'」と格納されているのは 変わりないのですが、それぞれ数値としては 55,56,57,0となります。 x86の場合、この4バイトの数値をint型として読み込むと 55× 256^0+56×256^1+57×256^2+0×256^3となります。 (^は累乗) 55× 1+56×256+57×65536+0×16777216=3749943 また、 以下のようにすると 普通に 789と出力します。 printf("%s\n",p); これは printf()が pのアドレスしか受け取らず、データ型を無視し、 %sでpのアドレスからを文字列として解釈しているからです。 意味がわかっていれば、結構いろいろ奇抜なこともできなくはないですが、 バグを多く含む原因になったりもします。 コンパイラがエラーではじくのはそれを防ぐため、という理解がいいかと思います。

muffler
質問者

お礼

ご回答ありがとうございます。 p=(int *)"789";のようにint型にしてpにアドレスを渡すと、他の回答者さんにも質問したのですが、' 'の1つを1バイトのメモリと考えると、 「'7','<不定>','<不定>','<不定>'」・・・や 「'  7  ','  8  ','  9  ','  0  '」 ではなく、'' ''1つを4バイトとして、 「''          7           ''」・・・ のように、4バイトの領域1つ1つに'7''8''9'が入っていくという事なのですかね?? あともう1つ疑問なのですが、 >この4バイトの数値をint型として読み込むと >55×256^0+56×256^1+57×256^2+0×256^3となります。 という回答をもらった部分なのですが、1バイトが256でintは4バイトという事で、256の部分は、256^4の値が入るとまだ完全に理解していないので、そう思ってしまったのですが、 >55×256^0+56×256^1+57×256^2+0×256^3 の部分を説明していただけると助かります。

muffler
質問者

補足

すいません補足の疑問もお願いします。 printf("%c\n%d\n",*p,*p); この*pは7のアドレスの値を指しているのに、 55× 256^0+56×256^1+57×256^2+0×256^3 以上の'8''9'も含めた計算が行われたのかが疑問としてあります。

  • asuncion
  • ベストアンサー率33% (2127/6289)
回答No.5

>'a'だと一文字を代入ということで、"a"だと文字列リテラルと考え先頭アドレスを代入という考え そうです。

関連するQ&A