天国にいけるC言語入門 ヘキサ構造体 ver3.2128
int*型のポインタ変数はint型の変数のアドレス,char*型のポインタ変数はint型の変数のアドレス, void* 型のポインタ変数はすべての型の変数のアドレスを格納することができます
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が
コマンドプロンプト画面に表示されることになります」
新規登録で充実の読書を
- マイページ
- 読書の状況から作品を自動で分類して簡単に管理できる
- 小説の未読話数がひと目でわかり前回の続きから読める
- フォローしたユーザーの活動を追える
- 通知
- 小説の更新や作者の新作の情報を受け取れる
- 閲覧履歴
- 以前読んだ小説が一覧で見つけやすい
アカウントをお持ちの方はログイン
ビューワー設定
文字サイズ
背景色
フォント
組み方向
機能をオンにすると、画面の下部をタップする度に自動的にスクロールして読み進められます。
応援すると応援コメントも書けます