はじめに
最近,配列とポインタが気になっていたので,char型の配列とポインタを用意していろいろ試し,「同じように扱える」,「同じようには扱えない」の例をまとめてみました.
追記: コメントで指摘をいただきましたが,今回はあくまでchar型についての話になっています.
最後に全体を示しますが,以下で示すコードは上から順に一続きのコードだと思ってください.
コンパイラはgcc 7.3.0です.
配列・ポインタを宣言・初期化する
下の2つとも可能なコードです.
char array[4] = "ABC"; // 可
char* pt = "DEF"; // 可,const修飾推奨
配列は要素数4と明示していますが,そうせずにchar array[] = "ABC"
と書くこともできます.
その場合も要素数は4になり,いずれも最後の要素は'\0'
(null文字)です.
ポインタの方は,"DEF"
という文字列リテラルをメモリに置いてそれをポイントするように初期化しています.この文字列リテラルは基本的に書き換えが保証されていません.
(例えば,この記事のあとで紹介しますがstrcpy()
で内容を変えることはできませんでした)
なお,配列には下のような初期化の書き方もありましたが,ポインタではできませんでした.
char array2[4] = {'A','B','C','\0'}; // 可
char* pt2 = {'D','E','F','\0'}; // 不可
追記: 下のようにすると可能だそうです.
これは文字列リテラルをポイントするのではなく,配列の実体ができ,内容の書き換えも可能です.
char* pt2 = (char[]){'D','E','F','\0'}; // 可,配列の実体ができる
// 下のようにしているのとほぼ同じ
char noname[]={'D','E','F','\0'};
char* pt2=noname;
配列・ポインタの相互の代入
配列の要素への代入はできますが,配列への代入はできません.
ポインタへは代入できます.
array = pt; // 不可
pt = array; // 可
配列・ポインタの文字列の書き変え
配列はstrcpy()
を使って中身を書き変えることができ,配列が格納しているアドレス値は変わりません.
一方,ポインタはpt = "文字列"
でポイントする文字列を変更できますが,pt
が格納するアドレス値も変わります.
先ほど初期化した時の"DEF"をメモリに置いたまま,別の領域に"JKL"を格納してそれをポイントしているようですが,正確なことはわかりません.
ちなみに,配列にはarray = "文字列"
ができず,= "文字列"
で初期化したポインタにはstrcpy()
ができませんでした.
char array[4] = "ABC";
array = "GHI"; // 不可
strcpy(array, "GHI"); // 可
char* pt = "DEF";
strcpy(pt, "JKL"); // 不可
pt = "JKL"; // 可
配列はstrcpy()
を使っても要素数を超える長さの文字列を格納させることはできません.
一方,ポインタは最初に初期化した文字列の長さを超える文字列をポイントさせることができました.配列と違い,新しく別のところに領域を確保して文字列リテラルを格納しそれをポイントするので,最初に確保した領域のサイズは関係ないということですね.
あと不思議なことに"DEF"
をポイントさせたポインタは初期化した直後も含めて格納しているアドレス値が同じでした.メモリ上には"DEF"は1つしか乗っていないようです.
pt = "DEFGHI";
printf("pt\t\"%s\": %p\n", pt, pt);
pt = "DEF";
printf("pt\t\"%s\": %p\n", pt, pt);
char* pt3 = "DEF";
printf("pt3\t\"%s\": %p\n", pt3, pt3);
pt "DEFGHI": 0x100403033
pt "DEF": 0x100403010
pt3 "DEF": 0x100403010
要素へのアクセス
配列もポインタも,添え字でアクセスする方法と,ポインタ演算と間接演算子でアクセスする方法が使えます.
printf("arrayの2,3番目の文字\n");
printf("%c", array[1]);
printf("%c", *(array + 2));
printf("\n");
printf("ptの2,3番目の文字\n");
printf("%c", *(pt + 1));
printf("%c", pt[2]);
printf("\n");
arrayの2,3番目の文字
HI
ptの2,3番目の文字
EF
まとめ
コンパイルの通る全体のコードとその出力はこれです.
不可の部分はコメントアウトしてます.
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(void){
char array[4] = "ABC"; // 可
char array2[4] = {'A','B','C','\0'}; // 可
printf("array\t\"%s\": %p\n", array , array);
char* pt = "DEF"; // 可
// char* pt2 = {'D','E','F','\0'}; // 不可
printf("pt\t\"%s\": %p\n", pt, pt);
char* pt2 = (char[]){'D','E','F','\0'};
printf("pt2\t\"%s\": %p\n", pt2, pt2);
// array = pt; // 不可
pt = array; // 可
pt = "DEF"; // 初期化時の状態に戻す
// array = "GHI"; // 不可
strcpy(array, "GHI"); // 可
printf("array\t\"%s\": %p\n", array, array);
// strcpy(pt, "JKL"); // 不可
pt = "JKL"; // 可
printf("pt\t\"%s\": %p\n", pt, pt);
pt = "DEFGHI";
printf("pt\t\"%s\": %p\n", pt, pt);
pt = "DEF";
printf("pt\t\"%s\": %p\n", pt, pt);
char* pt3 = "DEF";
printf("pt3\t\"%s\": %p\n", pt3, pt3);
printf("arrayの2,3番目の文字\n");
printf("%c", array[1]);
printf("%c", *(array + 2));
printf("\n");
printf("ptの2,3番目の文字\n");
printf("%c", *(pt + 1));
printf("%c", pt[2]);
printf("\n");
return 0;
}
array "ABC": 0xffffcc00
pt "DEF": 0x100403010
pt2 "DEF": 0xffffcc04
array "GHI": 0xffffcc00
pt "JKL": 0x10040302f
pt "DEFGHI": 0x100403033
pt "DEF": 0x100403010
pt3 "DEF": 0x100403010
arrayの2,3番目の文字
HI
ptの2,3番目の文字
EF
配列やポインタの扱い方の参考になればと思います.
ポインタについてはC言語でポインタ渡し・ポインタ演算をいろいろ試したも参考になるかもしれません.