プロが教えるわが家の防犯対策術!

こんにちは.C++初心者です.
以下のプログラムは,
オブジェクトの2次元配列の作成と
そのアクセスをポインタで行うことを
目的としています.

以下の□■部が質問箇所です.
なぜobをsamp型でキャストするのか分かりません.
obはすでにsamp型で宣言しているのに…
それと※部において
2度目のp++処理について教えていただきたいです.
メモリーイメージを書いてもらえると
ありがたいです。
よろしくおねがいします。





#include <iostream.h>
using namespace std;

class samp {
int a;
public:
samp(int n) { a = n; }
int get_val() { return a; }
};


int main(void)
{
samp ob[3][2] = {
1, 2
3, 4,
5, 6
};
int i;

samp *p;

// □■□■□■□■
p = (samp *) ob;

for(i = 0; i < 3; i++) {
cout << p->get_val() << ' ';
p++;
※ cout << p->get_val() << endl;
※ p++;
}

cout << endl;

return 0;
}


}

A 回答 (5件)

>なぜobをsamp型でキャストするのか分かりません.


>obはすでにsamp型で宣言しているのに…
おしい。obはsamp**形の定数と考えたほうがいいです。
また、キャストされた型はsamp*形です。
C/C++では、ポインタ形と元の型とでは明確に異なります。

>それと※部において
>2度目のp++処理について教えていただきたいです.
>メモリーイメージを書いてもらえると
>ありがたいです。
配列obの各要素は、メモリ上では次のように並んでいます。
 ob[0][0],ob[0][1],ob[1][0],ob[1][1],ob[2][0],ob[2][1]
そして、obという値はob[0][0]を指すポインタ値となります。
このポインタ値の型は前述のとおりsamp**なのですが、これをsamp*型にキャストすることで上記の並びをあたかも1次配列であるかのように扱えるようになります。
最初のcout<<…ではob[i][0]の内容を出力し、p++することでメモリ上での次の要素であるob[i][1]をpが指すようにしてやっています。
2回目のcout<<…でob[i][1]の内容を出力し、p++することでメモリ上での次の要素であるob[i+1][0]をpが指すようにしてやっています。

…しかし、もし私がこのプログラムを監査する立場にあったら、この部分を添字を使って書くようにさせます(処理速度が問題になってるならば別ですが)。
なぜなら、このようなコードはのちのメンテナンスで処理を追いにくくする要因だからです。

この回答への補足

さっそく詳細な解説ありがとうございます。
内容は大筋理解できました。
ところでメンテナンスで処理を追いにくくする要因とは、
具体的にはどのような影響があるのですか?
私は,学生のみで実際の開発現場はよく分かりませんので
ご教示いただけれるとこれから注意してコードを
書くことができるのですが。

ところで、添え字とすれば.。。。。

/*
ob[i * J_SIZE * j]
但し,J_SIZE は,#define J_SIZE ○ とマクロ定義されているものとします.
*/

といったコードでしょうか?

以上,よろしくおねがいします。

補足日時:2002/02/22 18:47
    • good
    • 0

> p = (samp *) ob;


2次元配列の ob を単純に参照すると、型は samp** です。p = ob としてもコンパイル時にワーニングが出るだけでバイナリに違いはでません。

1回目のループで考えると、
>cout << p->get_val() << ' ';
で p は ob[0][0]をさしています。
>p++;
p++することで ob[0][1] をさします
>※ cout << p->get_val() << endl;
p は ob[0][1]をさしています。
>※ p++;
で、ob[1][0] をさします

3回ループしたとき全ての ob配列は出力され、pはob配列の外側をさした状態で終了しますが、プログラム上で「使わないこと」を前提に問題有りません。

配列は連続したメモリイメージです。お絵かきをしてループを1ステップずつ追いかければ見えてくると思いますよ。

この回答への補足

こんにちは。非常にわかりやすくかったです。
ポインタpの「後始末」というか
free(p)とメモリを解放せずにいました…
ありがとうございました。

補足日時:2002/02/22 18:47
    • good
    • 0

どう見ても初心者が勉強すべきコードじゃないな。



hitomuraさんのいう「添字を使って書く」ということですが、
p->getval()
と書いてあるところを、
ob[i][0].get_val()
という書き方にするということで良いと思います。
どうしてもポインタにこだわるのであれば、
そもそもobを二次元配列にする必然性のあるプログラムには見えないので、
samp ob[6];
という定義にしてしまえば、元々のご質問であるキャストも
不要になりますし、C++のコードとしては、その方がすっきりします。
samp (*p)[2];
という定義の仕方もありますが、これも初心者にはお勧めしません。
    • good
    • 0

>free(p)とメモリを解放せずにいました…


これは、まずいですよ^^;)。freeで解放するのは mallocしたものです。同様に deleteしていいのは newした領域です。
このプログラム上では、p は ob配列の領域を指すアドレスを一時的に格納する為の変数です。

#1の方の補足部分ですが
>ところでメンテナンスで処理を追いにくくする要因とは、
今回の場合、ポインタでループする特別な理由がない限り普通に配列で参照した方があとで見直したときに解りやすいと思います。
for (i = 0; i < 3; i++) {
 cout << ob[i][0].get_val() << ' ';
 cout << ob[i][1].get_val() << endl;
}
もっと大きなプログラムですと顕著です。1ヶ月後の自分でも解るプログラムを書くようにしましょう>自分(T_T)
    • good
    • 0

>ところでメンテナンスで処理を追いにくくする要因とは、


>具体的にはどのような影響があるのですか?
それは、あなたが1番理解しているはずです。
あなたは、このコードを見て、どうしてこういう処理で動くのか理解できずにこちらに質問されました。

プログラムのメンテナンスというのはプログラムを作った人間とは別の人間が行うことが多々あります。
メンテナンスとはようするにコードの修正なのですが、修正するにはそのコードの動きを理解しなくてはなりません。
そのとき、このように理解しづらいコードがあると、その分メンテナンス作業が遅れることになります。
    • good
    • 0

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