ページ 11

ポインタが良く分かりません

Posted: 2012年1月10日(火) 18:59
by 史上最悪のデスペナ

コード:

 //OK
char **Str = new char*[Num];
 
////////////////////////////////////////
 
//実行時エラー
char **Str;
**Str = new char*[Num];
上と下の違いが分かりません。何で下はエラーになるのでしょうか?
どんなデータ型(のアドレスでも可)でも保存できる変数ってありますか?からの分岐トピックです。分岐点はここから[80189]のbeatleさんまで、からです

Re: ポインタが良く分かりません

Posted: 2012年1月10日(火) 19:12
by たかぎ
史上最悪のデスペナ さんが書きました:

コード:

char **Str = new char*[Num];
この書き方が混乱の元になっています。

コード:

char** Str = new char*[Num];
上のように書けば、Strがchar**型だということがもっと明確になります。
つまり、char**型であるStrというオブジェクトを、newで生成したchar**型の値で初期化しているのです。

一方、
史上最悪のデスペナ さんが書きました:

コード:

//実行時エラー
char **Str;
**Str = new char*[Num];
こちらは、char**型のStrに関節参照演算子*を二つくっつけています。
つまり、**Strはchar型になります。
char型のオブジェクトにnewで生成したchar**型の値を代入することはできません。
ただ、これは実行時エラーではなく、コンパイルエラーになるはずです。

また、仮にこの問題をクリアしても、たとえば、

コード:

char** Str;
**Str = 0;
のようにしても、今度は本当に実行時エラーが発生します。
なぜなら、Strも*Strも初期化していないので、まったく"あさって"の方向に代入しようとしているからです。

Re: ポインタが良く分かりません

Posted: 2012年1月10日(火) 19:23
by 初級者B
変数宣言時の 「*」 はポインタ宣言し、その後の初期化はそのポインタの変数になります。
宣言した後の、変数として「*」を用いるときは、ポインタで指さしてるアドレスの中身の値を参照します。

//OK
char **Str = new char*[Num];

こちらは、ダブルポインタで宣言し、ポインタの値として(Strというポインタに)new char*[Num]のアドレスが代入されます。
////////////////////////////////////////

//実行時エラー
char **Str;
**Str = new char*[Num];

こちらは少しややこしくなりますが、
char **Str で、ダブルポインタ**Strを宣言し、
**Str = new char*[Num]; は、「Strポインタ」の指さしてるポインタのアドレスの中身をnew char*[Num]という値にしようとしています。
この時点ではStrポインタは初期化されておらず(ポインタとしてどこも指しておらず)、しかも代入しようとしている場所(**Str)にはポインタ変数は入らないはずなので、エラーが出ます。

Re: ポインタが良く分かりません

Posted: 2012年1月10日(火) 20:04
by 史上最悪のデスペナ
なるほど・・・・・・・・。少し判った気になりました。
今回の問題に関しては理解できたつもりになりましたが、今後出てきたポインタが理解できるかどうか・・・・・・・・・
ポインタって難しいですねぇ

Re: ポインタが良く分かりません

Posted: 2012年1月10日(火) 20:10
by beatle
もっと簡単な例を見れば納得しますかね?

コード:

char* str = "foo"; // OK
str = "foo"; // OK
*str = "foo"; // ERROR

char c;
c = *str; // OK
c = str; // ERROR

Re: ポインタが良く分かりません

Posted: 2012年1月10日(火) 20:36
by softya(ソフト屋)

コード:

図にして見ました。

 +------------+    +------------+    +-------------+ 
 | char** Str | → | char *     | → | char        | 
 +------------+    +------------+    +-------------+ 
  (1) x1=Str        (2) x2=*Str       (3) x3=**Str   
解説すると
(1)の値が確定しないと(2)のアクセスが出来ません。
(1)と(2)の値が確定しないと(3)のアクセスが出来ません。

**Str = new char*[Num];
は(3)から始めようとしているので(1)と(2)の値が確定していない訳です。

図を書ければ理解できると思います。
beatleさんが書いた
char *var2[4];
char (*var3)[4];
を図にしてみましょう。

Re: ポインタが良く分かりません

Posted: 2012年1月10日(火) 21:32
by 史上最悪のデスペナ
beatle さんが書きました:もっと簡単な例を見れば納得しますかね?

コード:

char* str = "foo"; // OK
str = "foo"; // OK
*str = "foo"; // ERROR

char c;
c = *str; // OK
c = str; // ERROR
これは良く分かります。
softya(ソフト屋) さんが書きました:解説すると
(1)の値が確定しないと(2)のアクセスが出来ません。
(1)と(2)の値が確定しないと(3)のアクセスが出来ません。

**Str = new char*[Num];
は(3)から始めようとしているので(1)と(2)の値が確定していない訳です。
たかぎさんや初級者Bさんに教えていただいたのがなかったら(講座だけだったら)多分、「そうなんだ」と判った気になって受け取るだけだったでしょうね。
beatleさん、softya(ソフト屋)さんの貼って下さったリンクとたかぎさん・初級者Bさんのお二方のどれかが掛けても中途半端な理解だったでしょう。本当にありがとうございました。

締めくくりとして最後に
softya(ソフト屋) さんが書きました:beatleさんが書いた
char *var2[4];
char (*var3)[4];
を図にしてみましょう。
これをやって・・・・・・・・・・・・・
えっと・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・・・あれ?

コード:

char *var2[4];-------->(char* var)が[4]
+-----------------+    +------+
| char* var2[4]   | -> | char |
+-----------------+    +------+
(1) x1 = var2[4]       (2)x2 = *var2

char (*var3)[4];-------->(char var[4])の*var3
+------------+    +--------------+
| char* var3 | -> | char var3[4] |
+------------+    +--------------+
(1)x1 = var3        (2)x2 = *var3[4]
・・・・・・・・・・・・???

コード:

char *var2[4];-------->(char* var)が[4]
var2 = {"文字列1","文字列2","文字列3","文字列4"}
*var2----->"文字列1"のアドレス

char (*var3)[4];-------->(char var[4])の*var3
var3 = {'A','B','C','\0'}
*var3 = 'A'のアドレス
って感じですか?
なんかすっごい自信ないんですが。

Re: ポインタが良く分かりません

Posted: 2012年1月10日(火) 22:56
by softya(ソフト屋)
とりあえず途中ですが。

コード:

私の書き方も悪かったので訂正します。
●char **Str;
 +------------+    +------------+     +--------+
 | char** Str | → | char*[0]   |  -> | char[] |
 +------------+    +------------+     +--------+        +--------+
                   | char*[1]   |  -------------------> | char[] |
                   +------------+                       +--------+
                   |  :         | 
                   |  :         | 
                   +------------+                  +--------+
                   | char*[n]   |  --------------> | char[] |
                   +------------+                  +--------+
char*[n]がそれぞれの指すアドレスはバラバラです。


●char *var2[4]; -------->(char* var2)が[4]
+------------+    +--------+
| char*[0]   | -> | char[] |
+------------+    +--------+        +--------+
| char*[1]   | -------------------> | char[] |
+------------+    +--------+        +--------+
| char*[2]   | -> | char[] |
+------------+    +--------+   +--------+
| char*[3]   | --------------> | char[] |
+------------+                 +--------+
char*[n]がそれぞれの指すアドレスはバラバラ。
char (*var3)[4];は説明がややこしいので準備中。

Re: ポインタが良く分かりません

Posted: 2012年1月11日(水) 09:42
by softya(ソフト屋)
こういう風なイメージです。

コード:

●char (*var3)[4];
+-------------+    +------------+
| char (*)[4] | -> | char[0][0] |
+-------------+    +------------+
                   | char[0][1] |
                   +------------+
                   | char[0][2] |
                   +------------+
                   | char[0][3] |
                   +------------+
                   | char[1][0] |
                   +------------+
                   | char[1][1] |
                   +------------+
                   | char[1][2] |
                   +------------+
                   | char[1][3] |
                   +------------+
動作確認プログラムも作りました。

コード:

#include <iostream>
using namespace std;

int main(void)
{
	//	宣言
	char (*var3)[4];
	char *var4;
	//	動的確保
	var3 = new char[2][4];
	var4 = new char[4];
	//	初期化
	for( int i=0 ; i<4 ; i++ ) {
		for( int j=0 ; j<2 ; j++ ) {
			var3[j][i] = i + j*10;
		}
	}
	var4 = var3[1];
	
	//	出力
	for( int j=0 ; j<2 ; j++ ) {
		for( int i=0 ; i<4 ; i++ ) {
			cout << "var3[" << j << "][" << i << "] = " << (int)var3[j][i] << endl;
		}
	}
	for( int i=0 ; i<4 ; i++ ) {
		cout << "var4[" << i << "] = "<< (int)var4[i] << endl;
	}
}
単なるポインタと配列のポインタの違いをじっくり考えてみてください。

Re: ポインタが良く分かりません

Posted: 2012年1月11日(水) 12:15
by softya(ソフト屋)
ついでに課題も出しておきましょう。上のとは少し変えてあります。

コード:

#include <iostream>
using namespace std;

int main(void)
{
	//	宣言
	char (*var3)[4];
	char *var4;
	//	動的確保
	var3 = new char[3][4];
	var4 = new char[4];
	//	初期化
	for( int j=0 ; j<3 ; j++ ) {
		for( int i=0 ; i<4 ; i++ ) {
			var3[j][i] = i + j*10;
		}
	}
	var4 = var3[2];
	
	//	出力
	for( int j=0 ; j<3 ; j++ ) {
		for( int i=0 ; i<4 ; i++ ) {
			cout << "var3[" << j << "][" << i << "] = " << (int)var3[j][i] << endl;
		}
	}
	for( int i=0 ; i<4 ; i++ ) {
		cout << "var4[" << i << "] = "<< (int)var4[i] << endl;
	}
}
このプログラムにおいて
*(*(var3+1))と*(var4+1)で得られる値はそれぞれなんでしょうか?

Re: ポインタが良く分かりません

Posted: 2012年1月11日(水) 17:50
by 史上最悪のデスペナ
これは難問だ・・・・・・・・・
そもそも

コード:

cout << "var3[" << j << "][" << i << "] = " << (int)var3[j][i] << endl;
が何をしているのか分からないのでそれを調べるとこから始めないと。
しばらくだいぶお待ちください

Re: ポインタが良く分かりません

Posted: 2012年1月11日(水) 17:55
by softya(ソフト屋)

コード:

cout << "var3[" << j << "][" << i << "] = " << (int)var3[j][i] << endl;
C++でiostreamライブラリで標準出力するための書き方です。Cのprintfを使うと

コード:

printf( "var[%d][%d]=%d\n",j,i,(int)var3[j][i]);
です。

Re: ポインタが良く分かりません

Posted: 2012年1月11日(水) 18:06
by 史上最悪のデスペナ
printfの方が分かりやすいですね

まずはじっくりとコードを見つめなければ・・・・・・・・・

Re: ポインタが良く分かりません

Posted: 2012年1月11日(水) 18:28
by beatle
なんでC++ではprintfじゃなくて,あんな面倒な書き方をするのかというと,C++の書き方のほうが安全だからです.
printfを使うとき,書式指定子と違った型の値を渡してもコンパイルエラーにはなりませんが,実行時におかしな値が表示されますよね?例えば%dなのにdouble型を渡しちゃったり.
coutならそういうエラーがないのです.

Re: ポインタが良く分かりません

Posted: 2012年1月11日(水) 22:02
by 史上最悪のデスペナ
beatle さんが書きました:なんでC++ではprintfじゃなくて,あんな面倒な書き方をするのかというと,C++の書き方のほうが安全だからです.
printfを使うとき,書式指定子と違った型の値を渡してもコンパイルエラーにはなりませんが,実行時におかしな値が表示されますよね?例えば%dなのにdouble型を渡しちゃったり.
coutならそういうエラーがないのです.
それは凄いですねw
これも覚えておこう・・・・・・・・・・

Re: ポインタが良く分かりません

Posted: 2012年1月11日(水) 22:34
by 史上最悪のデスペナ
分かりました!!!
*(*(var3+1)) = var3[1][0] = 0 + 1*10 = 10
*(var4+1) = var4[1] = var3[2][1] = 1 + 2*10 = 21

これであってますか?

Re: ポインタが良く分かりません

Posted: 2012年1月11日(水) 22:38
by beatle
合ってるかどうかは課題を実行してみれば分かることでは・・・?

Re: ポインタが良く分かりません

Posted: 2012年1月11日(水) 22:44
by 史上最悪のデスペナ
beatle さんが書きました:合ってるかどうかは課題を実行してみれば分かることでは・・・?
それはそうなんですが、あくまでポインタが理解できたかの課題なので
自分で答え合わせするのは筋違いだと思いました。

Re: ポインタが良く分かりません

Posted: 2012年1月11日(水) 22:56
by softya(ソフト屋)
合ってます!大分わかってきた感じでしょうか?
まぁ。答え合わせは自分でしてもらって構いません。理解できたことが大事ですので。

思うイメージのメモリ構造を作り出せるためには実践有るのみです。

Re: ポインタが良く分かりません

Posted: 2012年1月11日(水) 23:01
by 史上最悪のデスペナ
はい。分かってきた感じがします。
もう少しいろんなサイトで問題を解いてみて定着させようと思います。

本当にありがとうございました。

Re: ポインタが良く分かりません

Posted: 2012年1月12日(木) 21:22
by みけCAT
beatle さんが書きました:なんでC++ではprintfじゃなくて,あんな面倒な書き方をするのかというと,C++の書き方のほうが安全だからです.
printfを使うとき,書式指定子と違った型の値を渡してもコンパイルエラーにはなりませんが,実行時におかしな値が表示されますよね?例えば%dなのにdouble型を渡しちゃったり.
coutならそういうエラーがないのです.
gccなら-Wformatオプションで警告を出せます。