プロが教える店舗&オフィスのセキュリティ対策術

構造体のプログラムで、

#include<stdio.h>
struct example{
char str[4];/* 文字列*/
int num;/* 番号*/
};

int main()
{
struct example data[5];/* 全て0で初期化されているとする*/
struct example *ps;

ps = &data[2];

ps->str[3]='A';/* (1)この処理でどこが書き変わるか?*/
ps += 2;
(*ps).num = 15;/* (2)この処理でどこが書き変わるか? */

ps -= 4;
ps->str[0] = 'z';/* (3)この処理でどこが書き変わるか? */
}

と、いうものがあるのですが、質問が、「上のようなプログラムのような処理を行った場合に、ポインタ変数psの中身はその時点でどのようになるか。 構造体変数data[]のアドレスは0x1000、0x1000、0x1010、0x1018、0x1020であるとする。」

という問題で、それぞれ、printf文を使ってpsの変化を実際に見てみればよいと思ったのですが、問題文の意図がよく分からないことと、仮にprintf文を入れた場合、どこにどのような中身で入れたら良いのか教えて欲しいと思っています。
お願いします。

A 回答 (7件)

★もう24日ですが、締め切られていないので私のアドバイス(解説)を追加します。


・まず最初に回答者 No.2、No.3 さんのご指摘どおり質問のアドレスがおかしいです。
 その後に質問者 No.4 さんの通りに2番目の構造体アドレスが 0x1008 ならば
 納得します。→入力のタイプミスですね。
・ここで問題をもう一度整理します。

正しい質問:
・上記の構造体変数 data[] のアドレスが 0x1000、0x1008、0x1010、0x1018、0x1020 としたとき
 ps というポインタで変更した箇所がどこなのかを printf 文で確認して見たい。
・ということですよね。

最初に:
・まずは構造体変数 data を初期化しましょう。質問者さんはコメントでは『0で初期化されている』
 と記述していますが、回答者 No.4 さんの『回答へのお礼』で恐らくは初期化していません。
・そこで、宣言を『static struct example data[5];』という風にします。→『static』で 0 に初期化される
・私は printf 文をどこに入れて確認すればよいかの回答はしません。
・メモリ・イメージだけで解説します。

解析:
(1)ps = &data[2]; [0x1010]{[\0][\0][\0][\0],0}{[\0][\0][\0][\0],0}『[\0][\0][\0][\0],0』{[\0][\0][\0][\0],0}{[\0][\0][\0][\0],0}
(2)ps->str[3]='A'; [0x1010]{[\0][\0][\0][\0],0}{[\0][\0][\0][\0],0}『[\0][\0][\0][A ],0』{[\0][\0][\0][\0],0}{[\0][\0][\0][\0],0}←(1)の時点
                                                     ↑
(3)ps += 2;     [0x1020]{[\0][\0][\0][\0],0}{[\0][\0][\0][\0],0}{[\0][\0][\0][A ],0}{[\0][\0][\0][\0],0}『[\0][\0][\0][\0],0』
(4)(*ps).num = 15; [0x1020]{[\0][\0][\0][\0],0}{[\0][\0][\0][\0],0}{[\0][\0][\0][A ],0}{[\0][\0][\0][\0],0}『[\0][\0][\0][\0],15』←(2)の時点
                                                                                  ↑
(5)ps -= 4;     [0x1000]『[\0][\0][\0][\0],0』{[\0][\0][\0][\0],0}{[\0][\0][\0][A ],0}{[\0][\0][\0][\0],0}{[\0][\0][\0][\0],15}
(6)ps->str[0]='z'; [0x1000]『[z ][\0][\0][\0],0』{[\0][\0][\0][\0],0}{[\0][\0][\0][A ],0}{[\0][\0][\0][\0],0}{[\0][\0][\0][\0],15}←(3)の時点
                  ↑
解説:
・上記の{}で囲った部分が、1つの構造体データを表します。
・上記の『』で囲った部分が、ポインタ(ps)の位置を表します。
・上記の[]で囲った部分が、ポインタ(ps)のアドレスを表します。
・上記の矢印が変更された部分を表します。→画面の解像度からずれるかも。注意。
・『(*ps).num』という指定は『ps->num』と同じ動作になります。→記述の違いだけです。

最後に:
・画面の関係上で折り返されていたらごめんなさい。
・過去に似たような質問がありました。
http://oshiete1.goo.ne.jp/qa2627072.html→『ポインタの基礎について』回答者 No.4
・こちらも参考にして下さい。
・以上。おわり。→何か感想をお願いします。場合によってはまた回答しますよ。
    • good
    • 0

基本的なところからの理解が必要ということですね。



方眼用紙を用意してください。(方眼用紙って古いか…、エクセルシートでかまいません)
横8マス、縦5マスの大きさに切ってください。
左上のマスを起点として、横書きの方向で0から39まで順に数字で埋めてください。
それぞれのマスにいろいろな色を付けてください。

一応、メモリマップのつもりです。
マスがメモリを表します。1マスで1バイトです。
数字がメモリを識別するためのアドレスです。
色はメモリに格納された値を意味します。

「上から3段目、左から4番目のマスを赤色で塗りました、そのマスに書いてある数字は何ですか?」
というのが(1)の質問内容です。

> 「(1)アドレス0x1008の値が2293584に書き換わる
> (2)アドレス0x1018の値が2293600に書き換わる
> (3)アドレス0x1020の値が2293568に書き換わる」
psの値が3つありますが、それぞれから 2293568 を引いて見てください。

方眼の1段は、struct example 1レコードを意味します。
char str[4]; のサイズは4バイトです。
int num; のサイズは4バイトです。(とする)

左上マスのアドレスは 0x1000 ということになります。

念のために補足しておきます。
0x1000 は16進数です、10進数に直すと 4096 です。
C言語では、配列の添え字は0から始まります。

これで、理解してもらえるといいのですが…。

以下、独り言です。
(2)はエンディアンの問題があって、2通りの答えが考えられますねぇ。
課題を出した人は、そこまで考慮しているのやら…。
    • good
    • 0

ANo.2 = Interest です。



> > 構造体変数data[]のアドレスは0x1000、0x1000、0x1010、0x1018、0x1020であるとする。
> の部分についてですが、多分2番目は「0x1008」の間違いと思われます。

これならわかります。

> 答えとして、
> 「(1)アドレス0x1008の値が2293584に書き換わる
> (2)アドレス0x1018の値が2293600に書き換わる
> (3)アドレス0x1020の値が2293568に書き換わる」
> は合っていますか??

違います。出題者はそういうことを聞きたかったんじゃなくて、「ポインタってなんなの?どういう仕事をするかわかる?」と聞きたかったんだと思いますよ。

ポインタのイメージは、「仮につけられた名前」のようなものです。
例えば。
0番目の人:xiamei723さん
1番目の人:yonfaさん
2番目の人:Interestさん
3番目の人:nerosukeさん
として、Aさん(誰かを指し示す)というポインタを作ったとします。

>ps = &data[2];

Aさん=2番目の人; として

>ps->str[3]='A'; /* (1)この処理でどこが書き変わるか? */

Aさんはアホだ。 といった場合、誰がアホでしょうか?
と聞いてるわけです。

ポインタを進める/戻すということは、Aさんの次の人/前の人ってことですね。

「アドレス0x1013の値が書き換わる」は、わかっている人には伝わると思いますが、構造体の配列がどのようにメモリに割付けられるかイメージできないと誤解を生みそうです。(アドレスが書き換えられるんじゃなくて、アドレスに格納されている値が書き換えらえるんです。)

0x1000 : data[0]
0x1008 : data[1]
0x1010 : data[2]
(以下省略)

> ps = &data[2];
> ps->str[3]='A';

なので、0x1010 + 3 のアドレスに 'A' が入った。これが
「アドレス0x1013の(に格納されている)値が書き換わる」
という意味です。
    • good
    • 0

> 答えが数値でも良いのでしょうか?


ポインタ変<数>はメモリ上のアドレスを示すものですので、数値となります。

> 構造体変数data[]のアドレスは0x1000、0x1000、0x1010、0x1018、0x1020であるとする。
の部分についてですが、多分2番目は「0x1008」の間違いと思われます。
「~であるとする」というのは、あくまでも仮定の話なので、実際にプログラムを作成して実行すると、実行するたびに異なる値となるはずです。
実際に実行してみて、変数psの値はどうだったでしょうか?
絶対値は大きな数値になったと思いますが、変動の差分を追ってみるとなんとなく法則みたいなものが見えるのではないでしょうか?

求められる回答としては、ps への代入が行われた後の ps の値を「0x1000、…」から選択してあげればよいと思います。

たぶん、(1)の答えは、「アドレス0x1013の値が書き換わる」というような感じになるのではないでしょうか。
    • good
    • 0
この回答へのお礼

ありがとうございます。

間違ってたらすみません。
答えとして、
「(1)アドレス0x1008の値が2293584に書き換わる
(2)アドレス0x1018の値が2293600に書き換わる
(3)アドレス0x1020の値が2293568に書き換わる」
は合っていますか??

お礼日時:2007/01/17 10:47

>構造体変数data[]のアドレスは0x1000、0x1000、0x1010、0x1018、0x1020であるとする。



これは明らかにおかしいですね。
理由はNo2さんが回答している通りです。

でも、とりあえずprintfで出力してみれば、わかるんじゃないですか?

とりあえず、構造体の配列を0で初期化します。
<初期化例>
memset(&data[0], 0, sizeof(struct example)*5);
単純に全てに0を代入してもいいです。
これは前提じゃなくてちゃんと実装しないと、不具合がでますよ

最後に構造体の内容を表示すればどの構造体に値が反映されているか
わかります。 それで影響のあったところを纏めればお終いです。

 for(i=0; i<5; i++){
  printf("data[%d] >> ",i);
  //ちなみに1文字づつ出力しているのは、0クリアされているからですよ。
  for(j=0; j<4; j++){
   printf(" str[%d]==%c", j,data[i].str[j] );
  }

   printf(" num==%d\n",data[i].num);
 }
    • good
    • 0

これは printf で表示させてもダメかな。


というのは、設問に

> 構造体変数data[]のアドレスは0x1000、0x1000、0x1010、0x1018、0x1020であるとする。

と書いてあるものを実際のプログラムに入れるわけには行かないから。

と思ったら。構造体のアドレスがこれだと、設問が変じゃないですか?
配列なのにメモリ空間に連続で割り当てられていないというか、同じアドレスを指す要素があるとか。intを32bit、構造体の「穴(※)」なしと仮定したら、8Byteずつアドレスが大きくなるんじゃないかと思います。<設問のアドレス変だよって話です。

※穴・・・構造体の記憶領域割り当ては構造体メンバのそれぞれのサイズを足したものより大きくなることがあります。このとき、メンバに割り当てられていない部分を「穴」と呼んでいます。

> 問題文の意図がよく分からないことと

(1)の要点「ポインタはアドレスを保持する」
(2)の要点「ポインタを1進めると、型の大きさ分だけ先のアドレスを指す」
(3)の要点=(2)の要点の引き算バージョン
でOK?

何を言っているのか分からないようでしたら、次のように問題を読み替えてください。

char data[5] = {0, 0, 0, 0, 0};
char *p;
p = &data[2];
*p = 1; /* (1)この処理でどこが書き変わるか? */
p += 2;
*p = 2; /* (2)この処理でどこが書き変わるか? */
p -= 4;
*p = 3; /* (3)この処理でどこが書き変わるか? */

これがわかったら、あとは char を struct example に換えれば・・・もう答えは出てますね。
    • good
    • 0

宿題のようですので、まず、自分で「ここ」と思ったところに printf 文を入れてみたらよいと思います。


すべての行間に挿入するのもありかと思いますが…。

まず、自分でやってみることが大事です。

問題の意図は、「ポインタ変数の加減算に対する理解」ということになりますね。

この回答への補足

質問前にやってみたのですが、すべて数値になり、その答えが心配だったので聞いてみたかったのです。
答えが数値でも良いのでしょうか?

補足日時:2007/01/15 21:56
    • good
    • 0

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