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だと実行できない理由がわかりません。
初歩的なことですいませんが、教えていただけると嬉しいです。
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ビット毎に重みを付けて計算しています。
ご回答ありがとうございます。
>(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バイト)ひと固まりとして処理
とあるのですが、この記述がどうも理解できません。
以上ご回答いただけると嬉しいです。
No.15
- 回答日時:
> ちょっと話しは変わってしまうのですが、今C言語を猛勉強中で参考書としては、
> 「著 林晴比古 新訂 新C言語入門シリーズ」や「著 柴田望洋 明解C言語 実践編」等で
> 勉強中なのですが、今回質問でのやりとりような、細かい所までは記載されていないことが多いです。
> 何か、細かい所まで掘り下げているようなおススメの参考書はありませんか??
> 自分からも積極的に勉強していかなければいけないなぁと痛感いたしまして。
とりあえず、今回感じた疑問については他の回答者さんと一緒にお答えさせて
頂きましたが、まずはその提示されている本で記載されていることをマスターしましょう。
で、何かおかしなことになった際は、一般的な(本に書いてある)使い方と
何かが違っているかを確認しましょう。
デバッガを使用して、思った通りの値になっているかどうかの確認も大事です。
今回の質問についてはコンパイラがどのような処理をするか?がメインであって、
C言語をどのように書いていけばいいのか?ではないと思います。
深いところは知っていれば知っていたでプログラミング及びデバッグに
役に立つかとは思いますが、奥が深い(実はそれ程でもないけど)割には
それ程効果的ではないです。
結局、変な書き方をしなくなる、つまり、本で書いてあるように正しく
使うようになる、といったところでしょうか?
と、書きつつ、深いところを知ることは悪いことではないので...
Cは結局アセンブリ言語に変換(しているともいえなくもない)のでそういった
意味ではインラインアセンブラを勉強してみるのは有効かと思います。
簡単にググってみて、結構わかりやすい(と思われる)サイトが
ありましたのでお知らせします。
インラインアセンブラで学ぶアセンブリ言語
http://codezine.jp/article/corner/67
ご回答ありがとうございます。
確かに今持っている参考書をマスター出来てはいないので、まずは参考書の記載をしっかり読んでマスター出来るようにします(;^_^A
載せていただいたサイトも参考にさせてもらいます!
ありがとうございました。
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さんからの回答があるので詳細な説明は省略します。
ご回答ありがとうございます。
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型は大抵、文字列ポインタとして使用することが多いので、
#こういったことには悩んだりしないかと。
ご回答ありがとうございます。
>引数にどんな型で受け付けたかの判別は プログラム内で書式指定した
>%cや%d等で行います。
>つまり....%dや%xと書いたら引数に何を設定しようとその値を
>int型として読み込むことになります。
>charは数値としても使用可能ですが、あくまで文字として定義されている型
>なのでその目的にあった使い方をしましょう。
以上のご回答理解できました。
しっかり頭に入れておきます!
ちょっと話しは変わってしまうのですが、今C言語を猛勉強中で参考書としては、「著 林晴比古 新訂 新C言語入門シリーズ」や「著 柴田望洋 明解C言語 実践編」等で勉強中なのですが、今回質問でのやりとりような、細かい所までは記載されていないことが多いです。
何か、細かい所まで掘り下げているようなおススメの参考書はありませんか??
自分からも積極的に勉強していかなければいけないなぁと痛感いたしまして。
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となりますのでビット論理積を行う必要はありません。
ご回答ありがとうございます。
>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になります。
がヒントのような気がするのですが、やはり何故そうなるか考えつきません。
いつも新たな質問を繰り返してしまいすいません。
ご回答いただけたらありがたいです。
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」しても値の確認は難しいと考えます。
最後に私の回答には偏ったアドバイスがあるかもしれませんので,一度この質問を総括して,別件については新しく質問をされた方が一般的な回答が得られると思います。ここの回答者は的確にフォローしてくれる方が多いので。
ご回答ありがとうございます。
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?)や'&'(&単体での使用は??)を初めて見たのでどのように作用してるのかわかりません。
以上何度もご丁寧に回答くださって大変申し訳ありませんが、またご回答いただけたら嬉しいです。
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ビット”と記述しています。
ご回答ありがとうございます。
>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だという理由はそこにあります。
ご回答ありがとうございます。
>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% …
ご回答ありがとうございます。
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のアドレスからを文字列として解釈しているからです。
意味がわかっていれば、結構いろいろ奇抜なこともできなくはないですが、
バグを多く含む原因になったりもします。
コンパイラがエラーではじくのはそれを防ぐため、という理解がいいかと思います。
この回答への補足
すいません補足の疑問もお願いします。
printf("%c\n%d\n",*p,*p);
この*pは7のアドレスの値を指しているのに、
55× 256^0+56×256^1+57×256^2+0×256^3
以上の'8''9'も含めた計算が行われたのかが疑問としてあります。
ご回答ありがとうございます。
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
の部分を説明していただけると助かります。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
- C言語・C++・C# c言語でユーザ関数を利用して入力された文字列を反転させるプログラムを作りたいです。 3 2023/01/29 19:47
- C言語・C++・C# c言語配列の結合についてです。 なぜうまくいかないのでしょうか。 #include <stdio.h 4 2022/05/30 22:42
- C言語・C++・C# C言語のエラーについて 2 2022/07/11 13:56
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
- C言語・C++・C# C言語で再起関数とポインタを用いて文字列反転をする方法がわかりません。 4 2023/04/29 20:32
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- C言語・C++・C# カードシャッフルのブログラムを使ってc言語でブラックジャックをしたい 2 2022/04/12 15:13
- C言語・C++・C# 至急教えてください! プログラミングの問題です! お願いします! 出力2と全く同じ出力をするように、 2 2022/06/22 23:10
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・プリン+醤油=ウニみたいな組み合わせメニューを教えて!
- ・タイムマシーンがあったら、過去と未来どちらに行く?
- ・遅刻の「言い訳」選手権
- ・【大喜利】【投稿~11/12】 急に朝起こしてきた母親に言われた一言とは?
- ・好きな和訳タイトルを教えてください
- ・うちのカレーにはこれが入ってる!って食材ありますか?
- ・好きな「お肉」は?
- ・あなたは何にトキメキますか?
- ・おすすめのモーニング・朝食メニューを教えて!
- ・「覚え間違い」を教えてください!
- ・とっておきの手土産を教えて
- ・「平成」を感じるもの
- ・秘密基地、どこに作った?
- ・【お題】NEW演歌
- ・カンパ〜イ!←最初の1杯目、なに頼む?
- ・一回も披露したことのない豆知識
- ・これ何て呼びますか
- ・初めて自分の家と他人の家が違う、と意識した時
- ・「これはヤバかったな」という遅刻エピソード
- ・これ何て呼びますか Part2
- ・許せない心理テスト
- ・この人頭いいなと思ったエピソード
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・ハマっている「お菓子」を教えて!
- ・【大喜利】【投稿~11/1】 存在しそうで存在しないモノマネ芸人の名前を教えてください
- ・好きなおでんの具材ドラフト会議しましょう
- ・餃子を食べるとき、何をつけますか?
- ・あなたの「必」の書き順を教えてください
- ・ギリギリ行けるお一人様のライン
- ・10代と話して驚いたこと
- ・つい集めてしまうものはなんですか?
- ・自分のセンスや笑いの好みに影響を受けた作品を教えて
- ・【お題】引っかけ問題(締め切り10月27日(日)23時)
- ・大人になっても苦手な食べ物、ありますか?
- ・14歳の自分に衝撃の事実を告げてください
- ・【大喜利】【投稿~10/21(月)】買ったばかりの自転車を分解してひと言
- ・ホテルを選ぶとき、これだけは譲れない条件TOP3は?
- ・家・車以外で、人生で一番奮発した買い物
- ・人生最悪の忘れ物
- ・【コナン30周年】嘘でしょ!?と思った○○周年を教えて【ハルヒ20周年】
- ・あなたの習慣について教えてください!!
- ・都道府県穴埋めゲーム
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
VBAのプログラムで、DIAG = 1# ...
-
「#undef」と「#define」の使い...
-
Integer変数をカラにしたいので...
-
C++ 構造体の一括初期化 {0}
-
long型のデータをバイト型の配...
-
VBAの変数のデータ型を変更する...
-
異なる構造体のデータのコピー
-
値が代入されてない時
-
構造体にする理由・利点・使用例
-
Pro*C NUMBER型の...
-
C言語 構造体の中に共用体を定...
-
日付チェック関数について
-
構造体の初期化方法について
-
構造体のポインタにNULLが入らない
-
整数から16進数への変換 現在c...
-
C言語 配列の長さの上限
-
関数から配列を返すには?
-
C言語のポインタに直接アドレス...
-
構造体のextern方法
-
セグメントエラー
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
VBAのプログラムで、DIAG = 1# ...
-
Integer変数をカラにしたいので...
-
C++ 構造体の一括初期化 {0}
-
「#undef」と「#define」の使い...
-
C言語 構造体の中に共用体を定...
-
構造体のデータを丸ごとコピー...
-
値が代入されてない時
-
typedefをプログラム中で解除す...
-
charとucharの違い
-
異なる構造体のデータのコピー
-
構造体のポインタにNULLが入らない
-
VBAにてcolorindexを変数に格納...
-
VBAの変数のデータ型を変更する...
-
long型のデータをバイト型の配...
-
整数から16進数への変換 現在c...
-
構造体の初期化方法について
-
日付チェック関数について
-
1バイトデータの読み出しについて
-
命名規則 VB 構造体
-
GTKプログラミングで型宣言する...
おすすめ情報