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

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
史上最悪のデスペナ
記事: 521
登録日時: 14年前

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

#1

投稿記事 by 史上最悪のデスペナ » 13年前

コード:

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

たかぎ
記事: 328
登録日時: 14年前
住所: 大阪
連絡を取る:

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

#2

投稿記事 by たかぎ » 13年前

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

コード:

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も初期化していないので、まったく"あさって"の方向に代入しようとしているからです。

初級者B

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

#3

投稿記事 by 初級者B » 13年前

変数宣言時の 「*」 はポインタ宣言し、その後の初期化はそのポインタの変数になります。
宣言した後の、変数として「*」を用いるときは、ポインタで指さしてるアドレスの中身の値を参照します。

//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)にはポインタ変数は入らないはずなので、エラーが出ます。

史上最悪のデスペナ
記事: 521
登録日時: 14年前

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

#4

投稿記事 by 史上最悪のデスペナ » 13年前

なるほど・・・・・・・・。少し判った気になりました。
今回の問題に関しては理解できたつもりになりましたが、今後出てきたポインタが理解できるかどうか・・・・・・・・・
ポインタって難しいですねぇ

beatle
記事: 1281
登録日時: 13年前
住所: 埼玉
連絡を取る:

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

#5

投稿記事 by beatle » 13年前

もっと簡単な例を見れば納得しますかね?

コード:

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

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

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

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

#6

投稿記事 by softya(ソフト屋) » 13年前

コード:

図にして見ました。

 +------------+    +------------+    +-------------+ 
 | 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];
を図にしてみましょう。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

史上最悪のデスペナ
記事: 521
登録日時: 14年前

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

#7

投稿記事 by 史上最悪のデスペナ » 13年前

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'のアドレス
って感じですか?
なんかすっごい自信ないんですが。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

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

#8

投稿記事 by softya(ソフト屋) » 13年前

とりあえず途中ですが。

コード:

私の書き方も悪かったので訂正します。
●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];は説明がややこしいので準備中。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

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

#9

投稿記事 by softya(ソフト屋) » 13年前

こういう風なイメージです。

コード:

●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;
	}
}
単なるポインタと配列のポインタの違いをじっくり考えてみてください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

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

#10

投稿記事 by softya(ソフト屋) » 13年前

ついでに課題も出しておきましょう。上のとは少し変えてあります。

コード:

#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)で得られる値はそれぞれなんでしょうか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

史上最悪のデスペナ
記事: 521
登録日時: 14年前

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

#11

投稿記事 by 史上最悪のデスペナ » 13年前

これは難問だ・・・・・・・・・
そもそも

コード:

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

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

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

#12

投稿記事 by softya(ソフト屋) » 13年前

コード:

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]);
です。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

史上最悪のデスペナ
記事: 521
登録日時: 14年前

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

#13

投稿記事 by 史上最悪のデスペナ » 13年前

printfの方が分かりやすいですね

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

beatle
記事: 1281
登録日時: 13年前
住所: 埼玉
連絡を取る:

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

#14

投稿記事 by beatle » 13年前

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

史上最悪のデスペナ
記事: 521
登録日時: 14年前

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

#15

投稿記事 by 史上最悪のデスペナ » 13年前

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

史上最悪のデスペナ
記事: 521
登録日時: 14年前

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

#16

投稿記事 by 史上最悪のデスペナ » 13年前

分かりました!!!
*(*(var3+1)) = var3[1][0] = 0 + 1*10 = 10
*(var4+1) = var4[1] = var3[2][1] = 1 + 2*10 = 21

これであってますか?

beatle
記事: 1281
登録日時: 13年前
住所: 埼玉
連絡を取る:

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

#17

投稿記事 by beatle » 13年前

合ってるかどうかは課題を実行してみれば分かることでは・・・?

史上最悪のデスペナ
記事: 521
登録日時: 14年前

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

#18

投稿記事 by 史上最悪のデスペナ » 13年前

beatle さんが書きました:合ってるかどうかは課題を実行してみれば分かることでは・・・?
それはそうなんですが、あくまでポインタが理解できたかの課題なので
自分で答え合わせするのは筋違いだと思いました。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

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

#19

投稿記事 by softya(ソフト屋) » 13年前

合ってます!大分わかってきた感じでしょうか?
まぁ。答え合わせは自分でしてもらって構いません。理解できたことが大事ですので。

思うイメージのメモリ構造を作り出せるためには実践有るのみです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

史上最悪のデスペナ
記事: 521
登録日時: 14年前

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

#20

投稿記事 by 史上最悪のデスペナ » 13年前

はい。分かってきた感じがします。
もう少しいろんなサイトで問題を解いてみて定着させようと思います。

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

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

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

#21

投稿記事 by みけCAT » 13年前

beatle さんが書きました:なんでC++ではprintfじゃなくて,あんな面倒な書き方をするのかというと,C++の書き方のほうが安全だからです.
printfを使うとき,書式指定子と違った型の値を渡してもコンパイルエラーにはなりませんが,実行時におかしな値が表示されますよね?例えば%dなのにdouble型を渡しちゃったり.
coutならそういうエラーがないのです.
gccなら-Wformatオプションで警告を出せます。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

閉鎖

“C言語何でも質問掲示板” へ戻る