8-5 ポインタを正しく使う

この記事は約4分で読めます。

ポインタにconstを使うときの注意点

ポインタ変数を定義するときに気を付けなければならない点があります。それは、ポインタ変数に格納するアドレスが定数なのか、ポインタが定数を指すのかという点です。

アドレスが定数のとき(ポインタが指すオブジェクトは変更可能)

型名 * const ポインタ名;

定数型へのポインタ(格納するアドレスは変更可能)

const 型名* ポインタ名;

この2つの違いを確かめます。

#include <stdio.h>
int main(void) {
	char str[]="abcdef";
	char STR[]="ABCDEF";
	char * const add=str;
	const char* chp=str;
	printf("add=%s\nchp=%s\n",add,chp);
	//add=STR;//アドレス変更:不可
	add[1]='z';
	chp=STR;
	//chp[1]='Z';//ポインタが指すオブジェクトへの書き込み:不可
	printf("add=%s\nchp=%s\n",add,chp);
	return 0;
}

実行結果

add=abcdef
chp=abcdef
add=azcdef
chp=ABCDEF

なお、定数型を指し、アドレスも変更不可能なポインタは次のように定義します。

const 型名* const ポインタ名;

ただ、constを使う場合多くは定数を指すポインタです。

ポインタを使うときの注意点

文字列を変数として使用するとき、その文字列がcharの配列型か、文字列へのポインタ型かで微妙に異なります。

#include <stdio.h>
void strset(char *a){
	a[0]='0';
}
int main(void) {
	char str1[]="abcdef";
	char *str2="aiueo";
	puts(str1);
	puts(str2);
	strset(str1);
	strset(str2);
	puts(str1);
	puts(str2);
	return 0;
}

実行結果 Borland C++ 5.5.1

abcdef
aiueo
0bcdef
0iueo

実行結果 VS2022

abcdef
aiueo
0bcdef
aiueo

文字列リテラルは、const charの配列型となります。str1はchar配列をconst char配列で初期化しているのに対して、str2はconst char配列へのポインタ型として定義しています。ですから、配列str1の要素を書き換えることは可能ですが、ポインタstr2が指す配列の値を書き換えてはいけません。よって、上のプログラムは正しい書き方ではありません。

#include <stdio.h>
void strset(char *a){
	a[0]='0';
}
int main(void) {
	const char str1[]="abcdef";
	const char *str2="aiueo";
	puts(str1);
	puts(str2);
	strset(str1);
	strset(str2);
	puts(str1);
	puts(str2);
	return 0;
}

実行結果

警告表示(筆者の環境の場合)
警告 10行目 問題のあるポインタの変換(関数 main )
警告 11行目 問題のあるポインタの変換(関数 main )
abcdef
aiueo
0bcdef
0iueo
(実行結果は環境により異なります)

こちらのプログラムでは明示的にstr1,str2ともにconstとして定義しました。constを付している変数に対し、値を書き換える関数を呼び出しているのに、コンパイルエラーとなりませんでした(処理系によります)。このプログラムでは、const charへのポインタ型が通常のcharへのポインタ型に暗黙にキャストされて呼び出されます。そのため、constを付して定義していても値が書き換えられます。

まとめ

ポインタ変数に対してconstを使用するとき、アドレスが定数の場合とポインタが指す変数が定数の場合があるので注意する必要がある。

コメント

タイトルとURLをコピーしました