malloc関数にとりかかかる前にvoid*型とchar*型、int*型のポインタ変数にアドレスを代入する場合の違いについて学んでみましょう

int*型のポインタ変数はint型の変数のアドレス,char*型のポインタ変数はint型の変数のアドレス, void* 型のポインタ変数はすべての型の変数のアドレスを格納することができます



ところで


みなさん


データの型


int

unsigned int

float

double

♬などなど…♬


さまざまな


データの型が登場してきました


そもそも型の本質とは



     メモリに格納されたデータがどのように取り扱われるかを決定するためにあるんだ



例えば


int a;


によって生成される


int 型の変数には4バイトのデータ格納容量があります


int 型の変数は4バイトのメモリを管理することになります


int 型の変数は4バイトのデータを管理することになるといっても


ただ単に4バイトのメモリを確保しているわけではないんだよ


たとえば


unsigned int 型の変数にも4バイトのデータ格納容量があります


unsigned int型の変数は4バイトのメモリを管理することになります


int型の変数とunsigned int型の変数


では

ともに

4バイトのデータ格納容量をもつ


4バイトのメモリを管理することになりますが


メモリに格納したデータの取り扱い方が違います




たとえば


4バイトのメモリにint型の形式で


11111111 11111111 11111111 11111111


というデータが格納されている場合


-2147483648


を表すことになりますが


4バイトのメモリにunsigned int型の形式で


11111111 11111111 11111111 11111111


というデータが格納されている場合


4294967295


を表すことになります


このように


int型

unsigned int 型


では


メモリに格納されているデータが


同じ


11111111 11111111 11111111 11111111


でも


異なる数値データを表していることがわかります







int型の変数では


-2147483648 ~ 2147483647


までの

(符号あり)整数値データを

(符号ありとはマイナス符号がつくということです)


unsigned int型の変数では


0 ~ 4294967295


までの


(符号なし)整数値データを


取り扱うことができます


int型やunsigned int型のデータ型の変数によって


たんに4バイト分メモリが確保されたのではなく


その確保したメモリ格納されたデータには


その型に応じた意味づけが行われることになるんだね



このように


 💖あるデータ型の変数によって管理されるメモリに格納された0と1のデータは💖


     💖そのデータ型によって定められた性質を持つことになります💖



これが


int

unsigned int

float

double


などの


データ型の役割なんだよ


ところで


voidも


データ型なんだけど


void型の形式では


メモリを確保(管理)することができません


確保(管理)されるメモリがないので


確保(管理)されたメモリに確保されたデータが


意味合いをもつということもありません


そもそも


メモリに格納されるデータもありません


確保(管理)されるメモリがないので


void型の変数は存在できないんだね


でも


voidがプログラムで用いられることがよくあります


たとえば


自作関数functionのプロトタイプ宣言


int function(void);


で用いられているvoidは


このfunction関数が


引数を持たない関数であることが


はっきりわかるように用いられます


次の自作関数newfunctionのプロトタイプ宣言


void newfunction(int a);


で用いられている


voidは


このnewfunction関数が


戻り値を持たないことを表しています


このように


voidは結構登場してきます


では


void*型っていうのは


なんなのでしょうか



char型の変数のアドレスを格納するのが


char*型のポインタ変数




int型の変数のアドレスを格納するのが


int*型のポインタ変数


なら



void型の変数のアドレスを格納するのが


void*型のポインタ変数


なのでしょうか?」




ソーラー「はあっ~


どう


この壮大な叙事詩は🌞😊


アレサ」



アレサ「はい😊 


voidがメインの話題みたいですね


ソーラーさん


このあとお話はどう続くのでしょうか?」


ソーラー「


void型の変数のアドレスを格納するのが


void*型のポインタ変数


なのでしょうか?


void型の変数がないんだから


void型の変数に対応する


void*型のポインタ変数はない


ってなりそうだけど


void*型のポインタ変数はあるんだよ」



アレサ「??


void*型のポインタ変数が存在するのですか?」



ソーラー「


データの型

char

int

unsigned int

float

double

などなど…♬


さまざまな


データの型が登場してきました


そもそも型の本質とは



     メモリに格納されたデータがどのように取り扱われるかを決定するためにあるんだ


同様に



データの型


char*

int*

など


     メモリに格納されたデータがどのように取り扱われるかを決定するためにあるんだ


まず

int*型のポインタ変数の管理するメモリには


int型の変数のアドレスのみが格納されることになります


ですから


以下のプログラムのように


int*型のポインタ変数に


double型の変数のアドレスを格納することはできません




#include <stdio.h>


int main() {


int* p;


double a=1.2345;


p = &a;//👈🌞int*型のポインタ変数pにdouble型の変数aのアドレス&aを代入しています

}



プログラムの実行結果



エラー (アクティブ) E0513 型 "double *" の値を型 "int *" のエンティティに割り当てることはできません

エラー C2440 '=': 'double *' から 'int *' に変換できません。



(エンティティとは実体という意味です


この場合の

型"int *" のエンティティは

int*型の形式で管理されるメモリ

もしくは

int*型のポインタ変数を表すことになります)



ソーラー「



int*型のポインタ変数の管理するメモリには


int型の変数のアドレスのみが格納されることになります


のなら


void*型のポインタ変数の管理するメモリには


void型の変数のアドレスのみが格納されることになる!?

(そのようなことはありません)



そうだとしても


いったいぜんたい


void*型のポインタ変数とはなんなのって思うよね



int*型のポインタ変数の管理するメモリには


int型の変数のアドレスのみが格納されるという制限がかかることになりますが


void*型のポインタ変数には


どの型のタイプの変数のアドレスでも格納することができます



ですから


void*型のポインタ変数には


int型の変数のアドレスを格納することもできれば


double型の変数のアドレスを格納することもできます



(void*型のポインタ変数は


どんなタイプの型の変数のアドレスでも格納できるので


汎用ポインタ変数とも呼ばれます)


実際に


void*型のポインタ変数に


char型

int型

double型


の変数のアドレスを格納してみます


そのプログラムはこちらです」



#include <stdio.h>



int main() {


int a = 1;

char b = 'a';

double c = 1.2345;


void* d;


d = &a;


printf("%p\n",d);


d = &b;

printf("%p\n", d);


d = &c;

printf("%p\n", d);


return 0;


}


プログラムの実行結果


00AFF990

00AFF987

00AFF974


C:\Users\solar\source\repos\Project6\Debug\Project6.exe (プロセス 2888) は、コード 0 で終了しました。

このウィンドウを閉じるには、任意のキーを押してください...



ソーラー「


int型の変数aのアドレス

00AFF990


char型の変数bのアドレス

00AFF987


double型の変数cのアドレス

00AFF974


が表示されています


int型の変数aは4バイトのメモリを管理しており


00AFF990



その4バイトの先頭の1バイトのメモリのアドレスを表しています



char型の変数bは1バイトのメモリを管理しており


00AFF987



その1バイトのメモリのアドレスを表しています



double型の変数cは8バイトのメモリを管理しており


00AFF974



その8バイトのメモリの先頭のメモリのアドレスを表しています



このように



void*型のポインタ変数dには


char型の変数aのアドレス&a

int型の変数bのアドレス&b

double型の変数cのアドレス&c


などの


さまざまな型の変数のアドレスを格納することができます」



アレサ「void*型のポインタ変数dには


そのようなはたらきがあったのですね」




ソーラー「



ところで



int a = 1;


int* d=&a;


を実行した場合



int*型のポインタ変数dにアスタリスクをくっつけた


*d



int型の変数aが管理しているメモリにアクセスすることになります


*d



いいかえると


int型の変数aを表すことになります


*d



さらにいいかえると


ポインタ変数dに格納されたアドレスのメモリを先頭とする


(💖intに対応する💖)4バイトのメモリに格納されているデータ


この場合1を表します



char b = 'a';


char* d=&b;


を実行した場合




char*型のポインタ変数dにアスタリスクをくっつけた


*d



変数bが管理しているメモリにアクセスすることになります


いいかえると


char型の変数bを表すことになります


さらにいいかえると


*d



ポインタ変数dに格納されたアドレスのメモリを先頭とする


(💖charに対応する💖)1バイトのメモリに格納されている文字データ'a'を表します




double c = 1.2345;


double* d=&c;


を実行した場合


double*型のポインタ変数dにアスタリスクをくっつけた


*d



変数cが管理しているメモリにアクセスすることになります


いいかえると


double型の変数cを表すことになります


さらにいいかえると


*d



double*型のポインタ変数dに格納されたアドレスのメモリを先頭とする


(💖doubleに対応する💖)8バイトのメモリに格納されているデータ1.2345を表します



アレサ「つまり


dがint*型のポインタ変数なら


*d



int型に対応する4バイトのメモリ領域にアクセスし



dがchar*型のポインタ変数なら


*d



char型に対応する1バイトのメモリ領域にアクセスし



dがdouble*型のポインタ変数なら


*d



double型に対応する8バイトのメモリ領域にアクセスすることになるですね」


ソーラー「そう


となると・・・


dがvoid*型のポインタ変数なら


*d



void型に対応する0バイトのメモリ領域にアクセスすることになるのかな?」


アレサ「え?


え~と


dがvoid*型のポインタ変数です・・か・・


となると


dには


さまざまなタイプの型の変数のアドレスを代入することができます


が・・・・・


dがvoid*型のポインタ変数の場合


*d



どのタイプの型に対応するメモリ領域にアクセスすることになる


ということですか?


たとえば


int型の変数aのアドレスを


void*型のポインタ変数dに格納した場合


*d



int型に対応する4バイトのメモリ領域にアクセスすることになる


つまり


*d



int型の変数aを表すことになるのではないでしょうか?」


ソーラー「そう そう思っちゃう


僕もそう思ったよ


でも


実際は


違うんだな


int型の変数aのアドレスを


void*型のポインタ変数dに格納する


int a = 1;


void* d=&a;


を実行した場合


確かに


void*型のポインタ変数dに


int型の変数aの管理する


4バイトのメモリの先頭のメモリのアドレスが代入されるのですが


void*型のポインタ変数dにアスタリスク*をくっつけた


*d



int型の変数aの管理する


4バイトのメモリにアクセスすることはできないんだ


ちょっと言い換えると


*d



void*型のポインタ変数dに格納されているアドレスのメモリを先頭とする


4バイトのメモリにアクセスすることはできないんだ


さらに


いいかえると


void*型のポインタ変数dにアスタリスク*をくっつけた


*d



int型の変数aそのものを表すことはできないんだよ


このことは


int a = 1;


void* d=&a;


を実行して



       void*型の形式でメモリに格納されたデータ



int a = 1;


int* d=&a;


を実行して



       int*型の形式でメモリに格納されたデータ



同じ


int型の変数aの管理する4バイトのメモリの先頭の1バイトのメモリのアドレス


なのだけど


取り扱われ方が違うということを表しているんだ



       void*型の形式でメモリに格納されたデータ



単に


💖int型の変数aの管理する4バイトのメモリの先頭の1バイトのメモリのアドレス


だけの情報を持っている💖のに対し


       int*型の形式でメモリに格納されたデータ



単に


💖int型の変数aの管理する4バイトのメモリの先頭の1バイトのメモリのアドレス


だけの情報を持っている💖のではなく


💖int型の変数aの管理する4バイトのメモリにアクセスするための情報も持っているんだ💖


これが


 void*型の形式でメモリに格納されたデータ

int*型の形式でメモリに格納されたデータの違いだね




同じアドレスデータがメモリに格納されていても


型によって


そのアドレスデータは取り扱われ方が違うということを表しているんだ



void*型のポインタ変数dにたとえ


int型の変数aのアドレスを代入したとしても



*dは


int型に対応する4バイトのメモリ全体にはアクセスできないんだね

  

そのことを示すプログラムはこちらです

👇

#include <stdio.h>



int main() {


int a = 1;


void* d;


d = &a;


printf("%d\n", *d);


return 0;


}



プログラムの実行結果


エラー C2100 間接指定演算子 (*) の使い方が正しくありません。

エラー (アクティブ) E0852 式は完全なオブジェクト型へのポインターである必要があります


アレサ「void*型のポインタ変数dには


様々なタイプの型の変数のアドレスが代入できても


dにアスタリスクをくっつけた


*d



機能しないのですね」


ソーラー「


そうなんだ


だけど


        💖void*型のポインタ変数には💖


     💖様々なタイプの型の変数のアドレスを💖


    💖代入することができるという利点があるので💖




        💖void*型のポインタ変数💖




    💖単純にアドレスのやり取りをする場合に用いられるんだよ💖


アドレスをやりとりするだけで


そのアドレスのメモリを先頭とするメモリ領域にアクセスできないのは


不便


かと思いきや


そうではないんだ


例えば


さきほどのプログラムにおいて


void*型のポインタ変数dに


int型の変数aのアドレスを格納していますが


もちろん


*d



int型の変数aを表すことはできません


しかし


void*型のポインタ変数dに


int型の変数aのアドレスを格納したのち

(int型の変数aのアドレスをvoid*型の形式でメモリに格納したのち)


void*型のポインタ変数dに格納されているint型の変数aのアドレスを


int*型にキャスト(int*型の形式でメモリに格納し直す)


する


(int*)d


を実行すれば


void*型の形式でメモリに格納されているint型の変数aのアドレス



int*型の形式でメモリに格納されているint型の変数aのアドレス


となります


あたかも


int*型のポインタ変数(int*)dにint型の変数aのアドレス


が格納されているような状態になります



ですので


int*型のポインタ変数(int*)dにアスタリスク*をくっつけた


*(int*)d



int*型のポインタ変数(int*)dがint型の変数aのアドレスを格納しているので


int型の変数aのアドレスを先頭とする4バイトのメモリ領域にアクセスすることになります


すなわち


*(int*)d



int型の変数aそのものを表すことになるんだよ




もう一度繰り返すと



確かに


void*型のポインタ変数dには


様々なタイプの型の変数のアドレスが代入できても


dにアスタリスクをくっつけた


*d



意味をなさない、機能しないのだけど


このように


void*型のポインタ変数dに


int型の変数aのアドレスを格納した後


あとから


void*型の形式でメモリに格納されているそのアドレスデータを



(int*)d


を実行して


int*型に


キャストすることにより


つまり


void*型の形式でメモリに格納されているそのアドレスデータを


int*型の形式でメモリに格納し直すことにより



(int*)dにアスタリスク*をくっつけた


*(int*)d



int型の変数aの管理する


メモリにアクセスすることができる


言い換えると


(int*)d


が格納しているアドレスのメモリを先頭とする


4バイトのメモリ領域にアクセスすることができるようになる


いいかえると


*(int*)d



int型の変数aと同じ役割をすることになるんだ



そのことを示すプログラムはこちらです」

👇


#include <stdio.h>


int main() {


int a = 1;


void* d;


d = &a;//🌞void*型のポインタ変数dにint型の変数aのアドレスを格納しています


printf("%p\n", d);//🌞void*型のポインタ変数dに格納されたint型の変数aのアドレスを表示しています


printf("%p\n", (int*)d);


//🌞void*型のポインタ変数dをint*型にキャストしています こうしてvoid*型の形式でメモリに格納されているint型の変数aのアドレスはint*型の形式でメモリに格納し直されます アドレス番号は同じままです


printf("%d\n", *(int*)d);


//🌞int*型の形式でメモリに格納されたint型の変数のアドレスを表す(int*)dにアスタリスク*をつけた*(int*)dは int*型の形式でメモリに格納された int型の変数aのアドレスを先頭とする4バイトのメモリ領域にアクセスすることになります すなわち*(int*)dはint型の変数aを表すことになります


return 0;


}



プログラムの実行結果


004FF8D8

004FF8D8

1


C:\Users\solar\source\repos\Project6\Debug\Project6.exe (プロセス 23192) は、コード 0 で終了しました。

このウィンドウを閉じるには、任意のキーを押してください...


ソーラー「このプログラムでは


int a = 1;


void* d;


d = &a;


を実行し


void*型のポインタ変数dに


int型の変数aのアドレス&aを代入しています


このとき


int型の変数aのアドレスは


void*型の形式でメモリに格納されることになります



printf("%p\n", d);


を実行して


void*型のポインタ変数dに格納されている


int型の変数aの管理している4バイトのメモリの先頭のメモリのアドレス


004FF8D8


を表示しています


次に


printf("%p\n", (int*)d);



(int*)dにより



void*型のポインタ変数dに格納されているint型の変数aのアドレスデータ


すなわち


void*型の形式でメモリに格納されているint型の変数aのアドレスデータ



int*型の形式でメモリに格納し直しています




この状態で



printf("%p\n", (int*)d);


を実行すると


int型の変数aのアドレス


004FF8D8


が表示されることになります


(int*)dはint*型の形式でメモリに格納されているint型の変数aのアドレスを表します


そして


int*型のポインタ変数である(int*)dにアスタリスク*をくっつけた


*(int*)d



int*型の形式でメモリに格納されているint型の変数aのアドレスのメモリを先頭とする4バイトのメモリ領域にアクセスすることになります


すなわち


 *(int*)dはint型の変数aを表すことになります



その状態で


printf("%d\n", *(int*)d);


を実行すると


*(int*)dはint型の変数aに等しい働きをするので


printf("%d\n", *(int*)d);



printf("%d\n", a);


を実行することと等しくなり


int型の変数aに格納されている数値データ1が


コマンドプロンプト画面に表示されることになります」



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

作者を応援しよう!

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

応援したユーザー

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

新規登録で充実の読書を

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

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

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