配列のポインタ変数とは一体何のことでしょうか 

ここで ソーラーさんは皆を笑わせようとおもいっきり😋間違ってきます。


ご注意ください。


てんC「ではでは(^^)


変数aのアドレス&aをポインタ変数ptaに格納できたところで


配列とポインタ変数はどのような関係になっているのでしょうか?


考察してみましょう、ソーラーさん」



ソーラー「ええっ😝😝😝


配列とポインタ変数の関係?????


?????

?????


はい?なんのこと?



まるで


うまか納豆とおかきの関係を聞かれてるみたいだね。



なんの関係もな~し



配列とポインタ変数の関係はなしです!


これで終了


配列とポインタ変数の関係は簡単です!


てんC 「おしいです。 はずれです。」


ソーラー 「近いのかい?


?う、うう~~~


配列は確か・・・


例えば int hairetu [ ]={1,2,3};


と配列宣言、初期化したら


この配列hairetu内に自動的に


3つの配列変数


hairetu[0]

hairetu[1]

hairetu[2]


が作製されて


数値1,2,3が、それぞれ


配列変数

hairetu[0]には1

hairetu[1]には2

hairetu[2]には3


が格納されるシステムだった。


______________________________________________


配列宣言、初期化を


int hairetu[ ]={1,2,3};


ではなく


もっと丁寧に


int hairetu[3]; (要素数3)


hairetu[0]=1;

hairetu[1]=2;

hairetu[2]=3;


と記述する方法もあるね。


______________________________________________


配列を使えば


int a=1;

int b=2;

int c=3;


👆


このようにa,b,cのint型変数宣言&初期化をおこなわなくても


int hairetu[3]={1,2,3};


で済むから便利なんだよね~~~


で済むから便利なんだよね~~~


       配列


その仕組みは簡単、簡単(^^)/


ふっ ふふふふふふ


そして


ポインタ変数とはアドレスを格納する変数のことだった。


(前のエピソードでも見てきたようにポインタ変数に


ポインタ変数を代入して


ポインタ変数に格納されているアドレスを


ポインタ変数に渡すこともできるよ)


これも簡単だね。(^^)/


ふはは


ところで


まえから思っていたんだけど


先程の配列変数に数値を代入する


hairetu[0]=1;

hairetu[1]=2;

hairetu[2]=3;


において


変数aを変数宣言後に


a=1;


と数値1が代入できたように


hairetu[0]に数値1が代入できるのなら


《《配列変数hairetu [0]って普通に変数なんじゃない?,


もしくは変数としての性質をもつ?》》


そう思わないかい? てんC?


そこで


配列変数hairetu[0]をふつうの変数とみなす。


すると


配列変数hairetu[0]に数値1が格納されているということは


配列変数hairetu[0]という名前の付いたメモリに数値1が格納されている


(または配列変数hairetu[0]がアクセスしているメモリに数値1が格納されている)


ということであり


もちろん


そのメモリにはアドレスがあることになる。


つまり


変数aに変数aのアドレス&aがあるように


配列変数hairetu[0]にも


配列変数hairetu[0]のアドレス&hairetu[0]があることになる。


そこで変数であろう配列変数hairetu[0]のアドレスである&hairetu[0]


がどのような値を持っているのかを


プログラムを組んで求めてみる。」


#include <iostream>


using namespace std;


int main() {


int hairetu[3]={1,2,3};


cout<<&hairetu[0]<<"\n";


return 0;


}


ビルド実行結果


0118FD4C



ソーラー「


数値データ1を格納しているhairetu[0]のアドレスは0118FD4C(*^^)v


ちゃんと無事に求めることができた・ ・ ・




おおウウウぉぉぉ   いいんじゃな~い



ねえ、てんC


コンピュータが


hairetu[0]と&を組み合わせた


&hairetu[0]のアドレスを読み取れたとなると


やはり


配列変数hairetu[0]は


変数hairetu[0]としての性質をもっているみたいだね。


となると


pthairetu[0]をポインタ変数宣言することにより


作製されたポインタ変数pthairetu[0]に


配列変数hairetu[0]のアドレス&hairetu[0]を


格納することができるはずだよね。


ポインタ変数とは


変数のアドレスを格納するための変数だから


変数ともいえるhairetu[0]のアドレス&hairetu[0]を


格納することのできるポインタ変数


pthairetu[0]


を作ることができるのは普通のことだもんね💖」



さあ どうかな?


         solarplexussより



わおおぅ  いい流れじゃん・・・


さっそく


ポインタ変数pthairetu[0]をつくってhairetu[0]のアドレス、


&hairetu[0]を


ポインタ変数pthairetu[0]に格納してみよぅ


その手順は次のようになるね!


まず


配列宣言int hairetu[3]={1,2,3};


によって


hairetu[0]に1を格納した後


配列変数hairetu[0]のアドレスを格納するためのポインタ変数


pthairetu[0]を


int *pthairetu[0];



ポインタ変数宣言して作製する。


その後


作製された


ポインタ変数pthairetu[0]を初期化するために


配列変数hairetu[0]のアドレス&hairetu[0]を


pthairetu[0]=&hairetu[0];と


ポインタ変数pthairetu[0]に代入する。


後は


ポインタ変数pthairetu[0]に格納されているアドレスを


cout出力表示してみる。


ちゃんと配列変数hairetu[0]のアドレス&hairetu[0]が表示されるかな😊


アドレスだから16進数になるはずだよね。


それでは


その手順に沿ったプログラムを構成してみます


#include <iostream>


using namespace std;


int main() {


int hairetu[3] = { 1,2,3 };


cout << &hairetu[0] << "\n";/*ここで、まず hairetu[0]のアドレスを確かめています*/



int*pthairetu[0]; /*〇pthairetu[0]のポインタ変数宣言を行う*/



pthairetu[0] = &hairetu[0];/*ポインタ変数pthairetu[0]にhairetu[0]のアドレス&hairetu[0]を代入して初期化する*/


cout << pthairetu[0] << "\n";

/*ここで、ポインタ変数pthairetu[0]に

どのようなアドレスが格納されているか確認する*/


return 0;


}



ソーラー「まあ  cout << pthairetu[0] << "\n";


を行うと



ふつうに ポインタ変数pthairetu[0]に格納されている



hairetu[0]のアドレス&hairetu[0]が表示されるはずだよね。」





うちもそう思います


            solarplexussより



てんC「ソーラーさん どのようなビルド結果がえられるのか楽しみですね。」



ソーラー「そうだね。うまく実行できるかな?


それでは😊😊😊



              


             🍅ビルドっ🍅 



ビルド実行結果



エラー (アクティブ) E0094 配列のサイズは 0 より大きくなければなりません

エラー C2466 サイズが 0 の配列を割り当てまたは宣言しようとしました。

エラー C2133 'pthairetu': サイズが不明です。


ソーラー「あ、あれぇ?


予想された設定となんか違う・・・


ビルドエラーが表示されてる・・・


 hairetu[0]のアドレスを格納するためのポインタ変数pthairetu[0]を


作製することができない🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎・・・


な、なぜ・・・」


てんC「ソーラーさん 


 hairetu[0]のアドレスを格納するためのポインタ変数pthairetu[0]を


作製することはできませんでしたが


 hairetu[1]のアドレスを格納するためのポインタ変数pthairetu[1]を


作製することができるかもしれません。


今のプログラムで


 hairetu[0]の部分をhairetu[1]で置き換えてビルドしてみたら


良いのではないですか?」


ソーラー「そうだね。


プログラムをかきかえてみるよ。


#include <iostream>


using namespace std;


int main() {


int hairetu[3] = { 1,2,3 };


cout << &hairetu[1] << "\n";/*ここで、まず hairetu[1]のアドレスを確かめています*/



int* pthairetu[1]; /*pthairetu[1]のポインタ変数宣言を行う*/



pthairetu[1] = &hairetu[1];/*ポインタ変数pthairetu[1]にhairetu[1]のアドレス&hairetu[1]を代入して初期化する*/


cout << pthairetu[1] << "\n";

/*ここで、ポインタ変数pthairetu[1]に

どのようなアドレスが格納されているか確認する*/


return 0;


}


ソーラー「それでは ビルドっ!!!」



ビルド実行結果


0075FEA8

0075FEA8


さらに


コマンドプロンプト画面とは


別の画面が表示されます。


画面には


program:

C:\Users\solarplexuss\source\repos\Project8\Debug\Project8.exe


module:

C:\Users\solarplexuss\source\repos\Project8\Debug\Project8.exe

File:


Debug Error!


Run-Time Check Failure#2-Stack around the variable 'pthairetu' was corrupted.


(Press Retry to debug the application)



と文章が表示され


さらに


中止  再試行  無視


の3つのボタンが表示されます


ソーラー「なんだろ これは??


どうなってるのかな?」



てんC「

ビルド結果の

0075FEA8

0075FEA8

cout << &hairetu[1] << "\n";

pthairetu[1] = &hairetu[1];

cout << pthairetu[1] << "\n";

の実行結果ですね。



まず

cout << &hairetu[1] << "\n";


により

0075FEA8

が表示されます。


&hairetu[1] のアドレスは0075FEA8なのですね。


そして


pthairetu[1] = &hairetu[1];


により


ポインタ変数pthairetu[1] に


アドレス0075FEA8をあらわす&hairetu[1]を代入したので


cout << pthairetu[1] << "\n";


の実行結果として


2回目の

0075FEA8


が表示されたのですね。


そして


別の画面に表示された


Stack around the variable 'pthairetu' was corrupted.


の文章ですね。


訳すと


変数pthairetuまわりのスタックが


破壊されています。


という意味のエラー文が表示されています。」


ソーラー「


変数pthairetuまわりのスタックが


破壊されています。


なんのことかな?



int hairetu[3] = { 1,2,3 };


cout << &hairetu[1] << "\n";/*ここで、まず hairetu[1]のアドレスを確かめています*/


の命令文に問題はなさそうだから


この


Stack around the variable 'pthairetu' was corrupted.

(変数pthairetuまわりのスタックが破壊されています。)


が表示される原因は・・・


・・・・・・・・


問題があるとすれば


int* pthairetu[1]; /*pthairetu[1]のポインタ変数宣言を行う*/


pthairetu[1] = &hairetu[1];/*ポインタ変数pthairetu[1]にhairetu[1]のアドレス&hairetu[1]を代入して初期化する*/


の部分なはずなんだけど・・・・・


よくわからないな・・・


(おまけのコーナーにつづく💖)」


ソーラー「それはともかく


このエピソードでのポイントは


🌞  🌞  🌞  🌞  🌞  🌞  🌞

配列宣言


int hairetu[] = { 1,2,3 };


により


作製される配列変数


hairetu[0]

hairetu[1]

hairetu[2]


のアドレス


&hairetu[0]

&hairetu[1]

&hairetu[2]


を格納するポインタ変数


pthairetu[0]

pthairetu[1]

pthairetu[2]


をうまく作成することができないことにあるんだ。


これは


どうなっているのかな?


面白い仕組みになっているね。



🌞  🌞  🌞  🌞  🌞  🌞  🌞




おまけのコーナー

__________________________________________________________


面白い展開になりましたね


エラー文はなんだったのでしょうか


実は

そこらあたりのことについては後に学ぶことになります。


ですが


先走って説明すると~~~



てんC「先走って説明したいのですね。😊」



ちょっとだけっ説明したいの~


実は

int* pthairetu[1];

が実行された場合


ポインタ変数pthairetu[1]が作製されているわけではありません。


int* pthairetu[1];


は[1]がついているし配列の雰囲気がありますね。


その通りで


int* pthairetu[1];


が実行されると


int* pthairetu[1];


の[]の中の要素数が1


となっていることにより


アドレスを格納するためのポインタ変数

pthairetu[0]

が1つ生成されます


ポインタ変数

pthairetu[1]

ではありません。



ですが


今のプログラムでは


int* pthairetu[1];


pthairetu[1] = &hairetu[1];/*ポインタ変数pthairetu[1]にhairetu[1]のアドレス&hairetu[1]を代入して初期化する*/


の命令文が実行されています。


つまり


int* pthairetu[1];

によって

作製されるのは


ポインタ変数

pthairetu[0]

なのに


作製したわけでもない

ポインタ変数pthairetu[1]に アドレス&hairetu[1]


が代入できているのです。


実は


一度

int* pthairetu[1];


のような

ポインタ変数の配列宣言を行うと

ポインタ変数

pthairetu[0]だけでなく

ポインタ変数

pthairetu[1]

pthairetu[2]

を用いて

メモリにデータ格納ができることになっています。


ですが


正式に


int* pthairetu[1];


により作製されたポインタ変数


pthairetu[0]


以外のポインタ変数をもちいて


アドレスデータを格納することは


壊してはいけない


データを破壊する可能性があります。


なぜなら


int* pthairetu[1];


により作製されたポインタ変数


pthairetu[0]



あるメモリにアドレスを格納することができます。



もちろん


そのメモリは ほかのプログラムによって使用されていないメモリです。


ほかのプログラムによって使用されていないメモリを


コンピュータは選んだというわけです。


このとき

ポインタ変数

pthairetu[0]につづいて


pthairetu[1]

pthairetu[2]

pthairetu[3]


をもちいて


メモリにアドレスを格納することができます


アドレスは4バイトのデータ量をもつので


たとえば

ポインタ変数

pthairetu[0]が


アドレス004FF8F8


アドレスを格納したなら


pthairetu[1]は


アドレス004FF8FCのメモリ


アドレスを格納することになります。



pthairetu[2]は


アドレス004FF900


アドレスを格納することになります。


もうすこしくわしく説明すると


ポインタ変数

pthairetu[0]は

004FF8F8

004FF8F9

004FF8FA

004FF8FB

のメモリにわたってアドレスを


ポインタ変数

pthairetu[1]は

004FF8FC

004FF8FD

004FF8FE

004FF8FF

のメモリにわたってアドレスを


ポインタ変数

pthairetu[2]は

004FF900

004FF901

004FF902

004FF903

のメモリにわたってアドレスを


格納することになります。


ですが

ポインタ変数

pthairetu[0]は


正式に


ポインタ変数宣言


int* pthairetu[1];


により作製されたポインタ変数


なので


004FF8F8

004FF8F9

004FF8FA

004FF8FB


のアドレスのメモリは他のプログラムによって使用されていないメモリが


選ばれているのですが


ポインタ変数

pthairetu[1]が


アドレスを格納するメモリ

004FF8FC

004FF8FD

004FF8FE

004FF8FF


他のプログラムによって使用されているかもしれません。


そこで


次の2つの命令文


int* pthairetu[1];/pthairetu[0]のポインタ変数宣言を行うことになります*/


pthairetu[1] = &hairetu[1];

/*ポインタ変数pthairetu[0]でなく

ポインタ変数pthairetu[1]に配列変数hairetu[1]のアドレス&hairetu[1]を代入して初期化しています*/


が実行されて


ポインタ変数pthairetu[1]に配列変数hairetu[1]のアドレス&hairetu[1]


が格納されると


Stack around the variable 'pthairetu' was corrupted.

(変数pthairetuまわりのスタック(メモリに格納されているデータ)が破壊されています。)


とエラー文が表示されることになります。


ポインタ変数pthairetu[1]に配列変数hairetu[1]のアドレス&hairetu[1]


が格納されていることにより


メモリに格納されているデータが破壊されたというわけです。


そこで


正式に

作製されていない

ポインタ変数

pthairetu[1]

をもちいず


正式に作製された

ポインタ変数

pthairetu[0]

&hairetu[1]を格納するようにすると


このようなエラー表示はでてこなくなります。


実際に


pthairetu[1] = &hairetu[1];


でなく


pthairetu[0] = &hairetu[1];


とプログラムを書き換えて実行してみると


#include <iostream>


using namespace std;


int main() {


int hairetu[] = { 1,2,3 };


cout << &hairetu[1] << "\n";/*ここで、まず hairetu[1]のアドレスを確かめています*/



int* pthairetu[1];/*pthairetu[0]のポインタ変数宣言を行う*/



pthairetu[0] = &hairetu[1];/*ポインタ変数pthairetu[0]にhairetu[1]のアドレス&hairetu[1]を代入して初期化する*/


cout << pthairetu[1] << "\n";

/*ここで、ポインタ変数pthairetu[1]に

どのようなアドレスが格納されているか確認する*/


return 0;


}


ビルド実行結果

00D3FC50

CCCCCCCC



Stack around the variable 'pthairetu' was corrupted.

(変数pthairetuまわりのスタックが破壊されています。)


エラー表示のでることなくプログラムは実行されます。


えっ


CCCCCCCC


はなんなのか?ですか


このプログラムの


cout << &hairetu[1] << "\n";

により

表示される&hairetu[1] のアドレスは

00D3FC50

ですね。


そして

cout << pthairetu[1] << "\n";

/*ここで、ポインタ変数pthairetu[1]に

どのようなアドレスが格納されているか確認する*/


によって表示される

ポインタ変数pthairetu[1]に格納されているアドレスは


CCCCCCCC


となっているわけです。


cout << &hairetu[1] << "\n";

cout << pthairetu[1] << "\n";


のhairetuにptがついているかいないかの違いに注意してください



今のプログラムでは


ポインタ変数pthairetu[0]には00D3FC50のアドレスの値をもつ&hairetu[1]が代入されていますが

ポインタ変数pthairetu[1]にはアドレスが代入されていません。


そのため

cout << pthairetu[1] << "\n";

が実行されると


ポインタ変数pthairetu[1]にアドレスが代入されていないときに


つまり

ポインタ変数pthairetu[1]が初期化されていないときに


最初から

ポインタ変数pthairetu[1]に格納されているアドレス


CCCCCCCC


がcout出力表示されます。


ですので


初期化されていないポインタ変数pthairetu[1]の格納しているアドレスを表示する命令文


cout << pthairetu[1] << "\n";



&hairetu[1]により初期化されたポインタ変数pthairetu[0]のアドレスを表示する命令文


cout << pthairetu[0] << "\n";


に書き換えた


次のプログラムを実行してみると


#include <iostream>


using namespace std;


int main() {


int hairetu[] = { 1,2,3 };


cout << &hairetu[1] << "\n";/*ここで、まず hairetu[1]のアドレスを確かめています*/



int* pthairetu[1];/*pthairetu[0]のポインタ変数宣言を行う*/



pthairetu[0] = &hairetu[1];/*ポインタ変数pthairetu[0]にhairetu[1]のアドレス&hairetu[1]を代入して初期化する*/


cout << pthairetu[0] << "\n";

/*ここで、ポインタ変数pthairetu[0]に

どのような数値が格納されているか確認するしています*/


return 0;


}


ビルド実行結果


0053FC1C

0053FC1C


綺麗にビルド結果が表示されます。              

__________________________________________________________






  • Twitterで共有
  • Facebookで共有
  • はてなブックマークでブックマーク

作者を応援しよう!

ハートをクリックで、簡単に応援の気持ちを伝えられます。(ログインが必要です)

応援したユーザー

応援すると応援コメントも書けます

新規登録で充実の読書を

マイページ
読書の状況から作品を自動で分類して簡単に管理できる
小説の未読話数がひと目でわかり前回の続きから読める
フォローしたユーザーの活動を追える
通知
小説の更新や作者の新作の情報を受け取れる
閲覧履歴
以前読んだ小説が一覧で見つけやすい
新規ユーザー登録無料

アカウントをお持ちの方はログイン

カクヨムで可能な読書体験をくわしく知る