再び配列の代わりにポインタ変数をつかって文字列データをメモリに格納してみます 

Visual Studio2019以降ではポインタ変数を使って文字列データをメモリに格納する機能は削除されました


ちゃん ちゃん


以下は古いバージョンのVisual Studioでのお話となります


🌞  🌞  🌞  🌞  🌞  🌞


このエピソードは


文字列データ"neko"を


配列を使って

コンピュータのメモリに格納する


char hairetu[]="neko";


文字列データ"neko"を


ポインタ変数を使って

コンピュータのメモリに格納する


char* hairetu="neko";


では何がちがうのでしょうか?


のエピソードの導入部分になっています


お話はそちらに続いていきますので


そちらも参考になさってください


より詳しく解説がなされています。


配列とポインタ変数の仕組みがより理解できるようになっています。


🌞  🌞  🌞  🌞  🌞  🌞


てんC 「今までは文字列データを


次のように


配列宣言、初期化


char hairetu[]="cgenngotanosii";


char hairetu[]="nekonekofunnjyatta";


を実行し


配列hairetuに文字列データ


"cgenngotanosii"や


"nekonekofunnjyatta"


等をとりこんできました。


これ自体は、とても簡単なことでした。」


ソーラー「てんC、ここは重要な箇所なんだね。」


てんC「はい そうなのです。


配列hairetuをつかって


char hairetu[]="nekoneko";


と文字列データ"nekoneko"をメモリに格納できたように


ポインタ変数hairetuをつかって


char* hairetu="nekoneko";


と記述し


文字列データ"nekoneko"ををメモリに格納することができます🍊



マックス「ふはは  俺の出番のようだな~~


ははは  よしよし


どころでなんですが??


ポインタ変数はアドレスを格納する変数だった気がするのですが・・・


私の気のせいでしたでしょうか?


文字列データをポインタ変数に格納・・・・・


一体、何がおこっている・・・」




(🌞実はポインタ変数hairetuに文字列データが代入されているのではなく


ポインタ変数hairetuには


文字列データ"nekoneko"を格納しているメモリの先頭のメモリのアドレスが代入されています


このことはいたるところで登場することになる重要なポイントなのです🌞)


てんC「マックスさん


char* hairetu="nekoneko";


と記述すると


文字列データ"nekoneko"はメモリに格納されますが


ポインタ変数hairetuには


文字列データ"nekoneko"ではなく


文字列データ"nekoneko"を格納したメモリのアドレスが


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


これからご紹介していくことになるのですが


char* hairetu="nekoneko";


を実行して


作製されたポインタ変数hairetuを


文字列データ"nekoneko"により初期化をおこない


文字列データ"nekoneko"をメモリに格納することは


簡単です。


いろいろな場面で登場してくることになります。





それでは


実際にプログラムを構成し


配列hairetuに代わり


ポインタ変数hairetuをつかって


文字列データ"nekoneko"をメモリに格納していきます。


配列hairetuをつかった命令文


char hairetu[]="nekoneko";


の代わりに


ポインタ変数hairetuをつかった命令文


char* hairetu="nekoneko";


を使うことによって


文字列データ"nekoneko"をメモリに格納することとなります。


配列をつかって文字列データを格納する方法である


char hairetu[]="nekoneko";

ポインタ変数hairetuつかって文字列データを格納する方法である


char* hairetu="nekoneko";


は外見がとても良く似ています。


が違いがあります。


それでは


実際にポインタ変数hairetuをつかって


文字列データ"nekoneko"をメモリに格納したあと


coutを使って文字列データ"nekoneko"を


コマンドプロンプト画面にcout出力表示してみましょう。


そのプログラムは

以下のようになります

👇


#include <iostream>


using namespace std;


int main() {


char* hairetu="nekoneko";


cout<<hairetu<<"\n";


return 0;

}


ビルド実行結果


nekoneko



てんC「実は😊


ポインタ変数をつかって文字列データをメモリにとりこむとき


面白い仕組みがかくされています。


ポインタ変数をつかって文字列データをメモリにとりこむため


char* hairetu="nekoneko";


とポインタ変数宣言、初期化した時点で


配列宣言

char hairetu[]="nekoneko";


をおこなったときと同じように


文字データを格納するchar型の配列変数

hairetu[0]

hairetu[1]

hairetu[2]

hairetu[3]

hairetu[4]

hairetu[5]

hairetu[6]

hairetu[7]

hairetu[8]


が生成されて


文字列データ”nekoneko”は


次のように


hairetu[0]='n'

hairetu[1]='e'

hairetu[2]='k'

hairetu[3]='o'

hairetu[4]='n'

hairetu[5]='e'

hairetu[6]='k'

hairetu[7]='o'

hairetu[8]='\0'


と格納されています。


これは配列宣言


char hairetu[]="nekoneko";

を行い


生成された配列変数


hairetu[0]

hairetu[1]

hairetu[2]

hairetu[3]

hairetu[4]

hairetu[5]

hairetu[6]

hairetu[7]

hairetu[8]

文字データを


hairetu[0]='n'

hairetu[1]='e'

hairetu[2]='k'

hairetu[3]='o'

hairetu[4]='n'

hairetu[5]='e'

hairetu[6]='k'

hairetu[7]='o'

hairetu[8]='\0'


と格納しているのと


同様な構造となっています。」


ソーラー

「へぇ~ 面白いシステムだね


char hairetu[]="nekoneko";(こちらは配列hairetu)



char*hairetu="nekoneko";(こちらはポインタ変数hairetu)


が実行されるとき


文字データを格納するのに同じ名前の配列変数

hairetu[0]

hairetu[1]

hairetu[2]

hairetu[3]

hairetu[4]

hairetu[5]

hairetu[6]

hairetu[7]

hairetu[8]


をつかっているんだ~


まあ おんなじhairetuという名前を使っているからね


ま、そういうこともあるかな?」


てんC「このあとのエピソードで述べることになりますが


同じ名前の配列変数を使っていますが・・・


char hairetu[]="nekoneko";(こちらは配列hairetu)


によって形成される

hairetu[0]

hairetu[1]

hairetu[2]

hairetu[3]

hairetu[4]

hairetu[5]

hairetu[6]

hairetu[7]

hairetu[8]


char*hairetu="nekoneko";(こちらはポインタ変数hairetu)


によって形成される


hairetu[0]

hairetu[1]

hairetu[2]

hairetu[3]

hairetu[4]

hairetu[5]

hairetu[6]

hairetu[7]

hairetu[8]


は名前が同じでも


アドレスがことなっている


つまり


文字データを格納するメモリの場所が異なっています」


ソーラー「ははあ


つまり


配列とポインタ変数を使った場合では


文字列データを格納するメモリがちがうということなんだね



これが

char hairetu[]="nekoneko";(こちらは配列hairetu)

char* hairetu="nekoneko";(こちらはポインタ変数hairetu)


の違いか・・・


というか 


       😝 それだけのことだったりして 😝


な~んてね


       😝 すこしはちがうんだよね 😝



マックス「う~むぅ???」


ソーラー「ただ何が違うかわからないな・・


両方とも同じ文字列データ"nekoneko"が


配列変数

hairetu[0]

hairetu[1]

hairetu[2]

hairetu[3]

hairetu[4]

hairetu[5]

hairetu[6]

hairetu[7]

hairetu[8]


hairetu[0]='n'

hairetu[1]='e'

hairetu[2]='k'

hairetu[3]='o'

hairetu[4]='n'

hairetu[5]='e'

hairetu[6]='k'

hairetu[7]='o'

hairetu[8]='\0'


と格納されているけど


配列hairetuを使った時と

ポインタ変数hairetuを使った時では


データを格納するメモリのアドレスが違う


つまり

配列変数

hairetu[0]

hairetu[1]

hairetu[2]

hairetu[3]

hairetu[4]

hairetu[5]

hairetu[6]

hairetu[7]

hairetu[8]

のアドレスが

配列hairetuを使った時と

ポインタ変数hairetuを使った時では


ちがうんだよね。」


てんC「はいっ😊」


ソーラー「で

ポインタ変数宣言

char* hairetu="nekoneko";

を使って格納された文字列データ"nekoneko"を表示するときも


配列宣言

char hairetu[]="nekoneko";

を使って格納された文字列データ"nekoneko"を表示するときも


cout<<hairetu<<"\n";

をもちいる


これは同じだよね。」


てんC「はいっ そうなんです」


ソーラー「??なにがちがうのかな?」


てんC「


ポインタ変数hairetuを使って文字列データ"nekoneko"をメモリに格納する場合と


配列を使って文字列データ"nekoneko"をメモリに格納する場合の違いは


文字列データを格納するメモリのアドレスが違うということ以外ないの?



と おもわれるかもしれません。



配列hairetuを使って


文字列データ"nekoneko"を


メモリに格納する場合にはできないのですが


ポインタ変数hairetuを使って


文字列データ"nekoneko"をメモリに格納する場合は


🍓同じポインタ変数名hairetuを使って🍓


次から次へとどんどん新たに別の文字列データを


別々のメモリに格納することができます。


配列hairetuを使って


文字列データ"nekoneko"をメモリに格納する場合と


ポインタ変数hairetuを使って


文字列データ"nekoneko"をメモリに格納する


場合の違いを述べてみたいと思います。


          配列hairetuをつかい


文字列データ"nekoneko"をメモリに


取り込んだ場合


格納された文字列データを


"nekoneko"から"nekofunnjyatta"に変更しようとして


次のようなプログラムを実行すると→」


#include <iostream>


using namespace std;


int main() {


char hairetu[]="nekoneko";

char hairetu[]="nekofunnjyatta";

/* 配列hairetuに格納された文字列データをnekonekoからnekofunnjyattaに変更しようとしています*/

cout<<hairetu<<"\n";

return 0;

}


ビルド実行結果



エラー C2088 '<<': class に対して正しくありません。

エラー C2374 'hairetu': 再定義されています。2 回以上初期化されています。


てんC「→このようにビルドエラーが表示されます。


Visual Studioでは同じ配列名をつかって


2度配列宣言できないので


プログラムを実行できないのです。



では


次のようにプログラムを記述してプログラムを実行してみましょう。


#include <iostream>


using namespace std;


int main() {


char hairetu[] = "nekoneko";

hairetu = "nekofunnjyatta";

/* 配列hairetuに格納された文字列データを"nekoneko"から"nekofunnjyatta"に変更しようとしています*/

cout<< hairetu << "\n";

return 0;

}


ビルド実行結果



エラー C3863 配列型 'char [9]' を割り当てることはできません。 8

エラー (アクティブ) E0137 式は変更可能な左辺値である必要があります


てんC「この場合も


メモリに


格納された文字列データを"nekoneko"から"nekofunnjyatta"に変更できません。


いいかえると


配列変数に格納された文字列データを"nekoneko"から"nekofunnjyatta"に変更できません。


そこで


👇のプログラムのように手を加えてみても


#include <iostream>


using namespace std;


int main() {


char hairetu[] = "nekoneko";

hairetu[] = "nekofunnjyatta";/*←[]をhairetuにつけてみました*/

/* 配列hairetuに格納された文字列データをnekonekoからnekofunnjyattaに変更しようとしています*/

cout<< hairetu << "\n";

return 0;

}



ビルド実行結果



エラー C2059 構文エラー: ']'

エラー (アクティブ) E0029 式が必要です


とメッセージがでてきてプログラムを実行することができません。


ですので


配列に格納された文字列データを"nekoneko"から"nekofunnjyatta"に変更できません。


いいかえると


配列変数に格納された文字列データを"nekoneko"から"nekofunnjyatta"に変更できません。



しかし、ポインタ変数hairetuをつかって


文字列データ"nekoneko"を


char* hairetu="nekoneko";のようにメモリに格納したなら


あとから


同じポインタ変数hairetuをつかって


hairetu="nekofunnjyatta";


と記述し


文字列データ"nekoneko"をメモリに格納したあと


さらに別のメモリに


文字列データ"nekofunnjyatta"


を格納することができます。



そのとき


cout<< hairetu << "\n";


によってコマンドプロンプト画面に表示される文字列データは


nekonekoからnekofunnjyattaに変更されます。


次のプログラムをみていきましょう。


#include <stdio.h>

int main(void){


char* hairetu="nekoneko";

hairetu="nekofunnjyatta";

/* ポインタ変数hairetuを使って文字列データ"nekoneko"をメモリに格納した後、次にポインタ変数hairetuを使って文字列データ"nekofunnjyatta"をメモリに格納しました*/

printf("%s\n",hairetu);

return 0;

}


ビルド実行結果


nekofunnjyatta



てんC「このビルド実行結果をみても おわかりになられますように


コマンドプロンプト画面に


nekonekoに代わって


nekofunnjyattaが


表示されています


たしかに


nekonekoにかわってnekofunnjyattaを


表示できたのですが


         

        文字列データ"nekoneko"の上に


       文字列データ"nekonekofunnjyatta"が上書きされて


         格納されているわけではありません。


つまり


普通の変数なら


例えばint型の変数aなら


int a;

a=1;


を実行した後


a=2;を実行すると


変数aに格納されている値は1から2に


上書きされてしまいますが


ポインタ変数をつかって文字列データをメモリに格納する場合では


char* hairetu="nekoneko";

を実行して

メモリに

文字列データ"nekoneko"を格納したあと


さらに


hairetu="nekofunnjyatta";

を実行して


メモリに

文字列データ"nekofunnjyatta"を格納したなら


文字列データ"nekofunnjyatta"は文字列データ"nekoneko"とはちがう


アドレスのメモリに格納されていきます。


つまり


文字列データ"nekoneko"と文字列データ"nekofunnjyatta"は


それぞれ別のアドレスのメモリに格納されています。


つまり古いデータも上書きされずメモリに保存されたままということに


なるのですね


ですので


#include <iostream>


using namespace std;


int main() {


char*hairetu = "nekoneko";

hairetu = "nekofunjytaa";

hairetu = "nekodaisuki";

hairetu = "nekokawaii";

hairetu = "nekouresii";

hairetu = "nekonyaonn";

cout<< hairetu << "\n";

return 0;

}


ビルド実行結果

nekonyaonn


👆このプログラムのように


どんどんポインタ変数hairetuを使って


文字列データ"nekoneko"とは別の文字列データを別のメモリに


格納していったとします


ビルド実行結果をみると


cout<< hairetu << "\n";

により

cout出力表示されるのは


最後にメモリに格納された文字列データ"nekonyaonn"の


nekonyaonnとなります



#include <iostream>


using namespace std;


int main() {


char*hairetu = "nekoneko";

hairetu = "nekofunjytaa";

hairetu = "nekodaisuki";

hairetu = "nekokawaii";

hairetu = "nekouresii";

hairetu = "nekonyaonn";

cout<< hairetu << "\n";

return 0;

}



#include <iostream>


using namespace std;


int main() {


char*hairetu;

hairetu = "nekoneko";

hairetu = "nekofunjytaa";

hairetu = "nekodaisuki";

hairetu = "nekokawaii";

hairetu = "nekouresii";

hairetu = "nekonyaonn";

cout<< hairetu << "\n";

return 0;

}

と書き換えることができます


このプログラム


では何が行われているのかというと


まず


ポインタ変数宣言


char* hairetu;


により作製される


ポインタ変数hairetuに


文字列データ"nekoneko"を格納しているメモリのアドレスが


代入されます。


次に


hairetu="nekofunjytaa";


が実行されると


ポインタ変数hairetuに格納されている


文字列データ"nekoneko"を格納しているメモリのアドレスに


文字列データ"nekofunjytaa"を格納しているメモリのアドレス


が上書きされて代入されます。



🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓


        🌞ここが重要なポイントです🌞


文字列データ"nekoneko"は文字列データ"nekofunjytaa"に上書きされませんが


🌞ポインタ変数hairetu🌞


文字列データ"nekoneko"を格納しているメモリの🍓アドレス🍓



文字列データ"nekofunjytaa"を格納しているメモリの🍓アドレス🍓


に上書きされるのです


🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓


マックス「わかった! つまり


文字列データ"nekoneko"が格納されているメモリに


文字列データ"nekofunjytaa"が上書きされるというわけではないんだな。」


てんC「


次に


hairetu="nekodaisuki";


が実行されると


ポインタ変数hairetuが格納している


文字列データ"nekofunjytaa"を格納しているメモリの🍓アドレス🍓に


かわり


文字列データ"nekodaisuki"を格納しているメモリの🍓アドレス🍓が


上書きされて代入されます。


つまり


ポインタ変数hairetuに


新たに


文字列データ"nekodaisuki"を格納しているメモリの🍓アドレス🍓が


上書きされます。


このときも


文字列データ"nekofunjytaa"は文字列データ"nekodaisuki"に上書きされることはありませんが


ポインタ変数hairetuに格納していた


文字列データ"nekofunjytaa"を格納しているメモリの🍓アドレス🍓は


文字列データ"nekodaisuki"を格納しているメモリの🍓アドレス🍓に上書きされます


つまり


ポインタ変数hairetuの格納しているアドレスは


文字列データ"nekodaisuki"を格納しているメモリの🍓アドレス🍓に


かわります。


こうして

hairetu="nekoneko";

hairetu="nekofunjytaa";

hairetu="nekodaisuki";

hairetu="nekokawaii";

hairetu="nekouresii";

hairetu="nekonyaonn";


が実行されていくと


最後にポインタ変数hairetuに格納されることになるアドレスは


hairetu="nekonyaonn";

により

文字列データ"nekonyaonn"


が格納されたメモリのアドレスとなるのです。


ポインタ変数hairetuに格納されることになるアドレスは


文字列データ"nekonyaonn"


が格納されたメモリのアドレスとなるので


ポインタ変数hairetuに格納されているアドレスのメモリに


格納されている文字列データを表示する命令文


cout<< hairetu << "\n";


が実行されると


コマンドプロンプト画面に表示される文字列は


文字列データ"nekonyaonn"の


nekonyaonn


となります。


このように


cout<< hairetu << "\n";


ポインタ変数hairetuが格納しているアドレスのメモリに格納されている


文字列データを表示する働きがあります



このとき

"nekonyaonn"以外の文字列データ


"nekoneko"

"nekofunjytaa"

"nekodaisuki"

"nekokawaii"

"nekouresii"



別のアドレスのメモリに格納されていますが


ポインタ変数hairetuを用いた命令文


cout<< hairetu << "\n";

をつかって


文字列

nekoneko

nekofunjytaa

nekodaisuki

nekokawaii

nekouresiiを


コマンドプロンプト画面に表示することはできなくなっています。


なぜなら


ポインタ変数hairetuは文字列データ"nekonyaonn"を格納しているメモリのアドレスを


格納しているからです



いかがでしたか


いままでの説明は簡易番となっていますが


  「配列の代わりにポインタ変数をつかって文字列データをメモリに格納する」


意外と簡単だったのではないでしょうか?」












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

作者を応援しよう!

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

応援したユーザー

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

新規登録で充実の読書を

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

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

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