童貞のプログラミング

童貞の頭の中を垂れ流します。

C言語のポインタについて 3.宣言する型が違う場合どうなっているのか?

C言語のポインタについて3

 

1.そもそもポインタとは何か?
2.ポインタで出てくる&と*はなに?

3.宣言する型が違う場合どうなっているのか?
4.ポインタのポインタとは?

 

今回はポインタ変数の型について考えていこうと思います。
正直、ポインタ変数に入る値はアドレスという数字と文字の羅列(16進表記のメモリアドレス)なので、変数の型なんてポインタ変数であれば、どうでもいいように感じますが一応意味があります。

 

それは、ポインタ変数からの代入ができないことと、アドレス操作時のバイト数の違いです。

 

 

 

①ポインタ変数からの代入ができない

1つ目のポインタ変数から代入できないとは、

int A;

A=1;

char *Ap = &A;

*Ap = 123;

printf(" A = %d\n", A);

 

上記のプログラムだと、文字通り普通に値を代入ではなくポインタ変数経由で値を入れるとき(4行目)、型が一致していないとうまくいかない。逆に型を一致させていれば、

 

int A;

A=1;

int *Ap = &A;

*Ap = 123;

printf(" A = %d\n", A);

 

代入ができます。(これだけです。)

 

 

②アドレス操作時のバイト数の違い

2つ目にアドレス操作時のバイト数の違いは、型によってバイト数が決められています。

そのバイト数分を利用してアドレス操作をする必要があるため、そのときに型を意識する必要が出てきます。

【型サイズ一覧表】

型名 サイズ (32 ビット) サイズ (64 ビット)
char 1 バイト 1 バイト
short int 2 バイト 2 バイト
int 4 バイト 4 バイト
long int 4 バイト 8 バイト
float 4 バイト 4 バイト
double - 8 バイト
long - 8 バイト

 ※32bitのdoubleとlongはどのような扱いになるかわからなかったので記載しまていません。

 

試しに、まず私の開発環境は何bitどうなっているのか確認してみようと思います。

やり方は簡単で、ポインタ変数のサイズを測ればわかります。

測った際のサイズが8の時は64bit、4の時は32bitとなります。

f:id:SitimiMayo:20210120173634p:plain

開発環境の確認

 

 

f:id:SitimiMayo:20210120173714p:plain

開発環境の結果

ポインタ変数のサイズが8なので、私の開発環境は64bitとなります。

次は、型のサイズが上記通りか確認してみます。

3行目~9行目は変数の宣言

11行目~17行目は変数に対応したポインタ変数の代入

を行っています。

 

f:id:SitimiMayo:20210121142818p:plain

型のサイズの確認

f:id:SitimiMayo:20210121142849p:plain

型のサイズの結果

 

変数名
char sizeof A 1
Ap 0x7ffcba727ca5
Ap+1 0x7ffcba727ca6
short int sizeof B 2
Bp 0x7ffcba727ca6
Bp+1 0x7ffcba727ca8
int sizeof C 4
Cp 0x7ffcba727cac
Cp+1 0x7ffcba727cb0
long int sizeof D 8
Dp 0x7ffcba727cc0
Dp+1 0x7ffcba727cc8
float sizeof E 4
Ep 0x7ffcba727ca8
Ep+1 0x7ffcba727cac
double sizeof F 8
Fp 0x7ffcba727cb8
Fp+1 0x7ffcba727cc0
long sizeof G 8
Gp 0x7ffcba727cb0
Gp+1 0x7ffcba727cb8

出力結果をわかりやすく表にしてみました。

ひとつづつ見ていきます。

①.char型

char型のサイズは1バイトです。なので、アドレス操作(+1の奴)をした際にアドレスは1だけ値がずれます。

Ap:0x7ffcba727ca5

↓アドレス操作で+1したらアドレスの値が1バイト増える。

Ap+1:0x7ffcba727ca6

 

②short int型

short int型のサイズは2バイトです。なので、アドレス操作(+1の奴)をした際にアドレスは2だけ値がずれます。

Bp:0x7ffcba727ca6

↓アドレス操作で+1したらアドレスの値が2バイト増える。

Bp+1:0x7ffcba727ca8

 

③int型

int型のサイズは4バイトです。なので、アドレス操作(+1の奴)をした際にアドレスは4だけ値がずれます。

Cp:0x7ffcba727cac

↓アドレス操作で+1したらアドレスの値が4バイト増える。

Cp+1:0x7ffcba727cb0

 

※アドレスは16進数で決められているから0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,まで行くと桁が繰り上がる。今回だと

0x7ffcba727cac

↓+1バイト(計1バイト)

0x7ffcba727cad

↓+1バイト(計2バイト)

0x7ffcba727cae

↓+1バイト(計3バイト)

0x7ffcba727caf

↓+1バイト(計4バイト)

0x7ffcba727cb0

となるから4バイト上がっていることがわかる。

 

④long int型

long int型のサイズは8バイトです。なので、アドレス操作(+1の奴)をした際にアドレスは8だけ値がずれます。

Dp:0x7ffcba727cc0

↓アドレス操作で+1したらアドレスの値が8バイト増える。

Dp+1:0x7ffcba727cc8

 

⑤float型

float型のサイズは4バイトです。なので、アドレス操作(+1の奴)をした際にアドレスは4だけ値がずれます。

Ep:0x7ffcba727ca8

↓アドレス操作で+1したらアドレスの値が4バイト増える。

Ep+1:0x7ffcba727cac

 

これも先程の16進数の考え通りすれば、 

0x7ffcba727ca8

↓+1バイト(計1バイト)

0x7ffcba727ca9

↓+1バイト(計2バイト)

0x7ffcba727caa

↓+1バイト(計3バイト)

0x7ffcba727cab

↓+1バイト(計4バイト)

0x7ffcba727cac

4バイト分増えていることがわかる。

 

⑥double型

double型のサイズは8バイトです。なので、アドレス操作(+1の奴)をした際にアドレスは8だけ値がずれます。

Fp:0x7ffcba727cb8

↓アドレス操作で+1したらアドレスの値が8バイト増える。

Fp+1:0x7ffcba727cc0

 

これも16進数の考え通りで、 

0x7ffcba727cb8

↓+1バイト(計1バイト)

0x7ffcba727ca9

↓+1バイト(計2バイト)

0x7ffcba727caa

↓+1バイト(計3バイト)

0x7ffcba727cab

↓+1バイト(計4バイト)

0x7ffcba727cac

↓+1バイト(計5バイト)

0x7ffcba727cad

↓+1バイト(計6バイト)

0x7ffcba727cae

↓+1バイト(計7バイト)

0x7ffcba727caf

↓+1バイト(計8バイト)

0x7ffcba727cc0

8バイト分増えていることがわかる。

 

 

⑦long型

long型のサイズは8バイトです。なので、アドレス操作(+1の奴)をした際にアドレスは8だけ値がずれます。

Gp:0x7ffcba727cb0

↓アドレス操作で+1したらアドレスの値が8バイト増える。

Gp+1:0x7ffcba727cb8

 

 

このように型自体にバイト数が決められていることが分かった。

 ※今回は+1を指定したが

char型で+2を指定すれば2バイトのアドレスの値が増える

char型で+3を指定すれば3バイトのアドレスの値が増える

char型で+4を指定すれば4バイトのアドレスの値が増える

int型で+2を指定すれば8バイトのアドレスの値が増える

int型で+3を指定すれば12バイトのアドレスの値が増える

int型で+4を指定すれば16バイトのアドレスの値が増える

double型で+2を指定すれば16バイトのアドレスの値が増える

double型で+3を指定すれば24バイトのアドレスの値が増える

double型で+4を指定すれば32バイトのアドレスの値が増える

 

一応、計算式を作るとすれば

【型のバイト数】×【+の数字】= アドレス操作時のバイト数

 

となります。 

これで、型ごとのバイト数の増え方が表どおりであることが分かりました。

じゃあ、この型のバイトを利用してアドレス操作ってなにするのかというと、

文字列や数列などから目的の値をとる時などに使います。

例)

char A[]=”ABCDEFGH”

ここからDEFGHだけを取り出して別の変数に入れる。

int B=123456789

ここから56だけを取り出して別の変数に入れる。

 

このようなときにこの型を意識して使う必要が出てきます。

 

③まとめ

ポインタ変数で型を意識する理由

長ったらしくいろいろ書きましたが、要は3点です。

1.ポインタ変数からの代入ができないこと

※正直、ポインタ変数から代入をする機会はそこまでないと思うので、あまり気にしなくていい。 

2.アドレス操作時のバイト数の違い

アドレス操作時に+1した際、型ごとに増え方が違う。

→使い道は、文字列や数列から目的の値をとる時などに意識する。

型名 サイズ (32 ビット) サイズ (64 ビット)
char 1 バイト 1 バイト
short int 2 バイト 2 バイト
int 4 バイト 4 バイト
long int 4 バイト 8 バイト
float 4 バイト 4 バイト
double - 8 バイト
long - 8 バイト

 3.特に型に思い入れがないなら、とりあえず変数と同じ型にしておけばいい

 

追記

2021年1月29日リンク先を追加しました。