導入
「文字列」は C 言語では char の配列(char*)で表します。では「文字列を複数まとめて、個数も実行中に決めたい」場合はどうすればいいでしょうか。答えは「char* へのポインタ」、つまり ポインタのポインタ char** です。
説明
char** names は、「char* が並んだ領域」を指すポインタです。names 自身を malloc で確保し、さらに names[i] それぞれも malloc で1本ずつ文字列用の領域を確保します。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
int n = 3;
const char *src[] = {"Alice", "Bob", "Carol"};
char **names = malloc(n * sizeof(char *)); // char* が n個入る領域
int i;
for (i = 0; i < n; i++) {
names[i] = malloc(strlen(src[i]) + 1); // +1 は文字列の終わり '\0' の分
strcpy(names[i], src[i]);
}
for (i = 0; i < n; i++) {
printf("%s\n", names[i]);
}
for (i = 0; i < n; i++) {
free(names[i]); // 先に1本ずつ解放
}
free(names); // 最後にポインタの配列自体を解放
return 0;
}
names は「箱の配列」を指し、names[i] はその箱の中に入っている「文字列の先頭アドレス」です。解放するときは、内側(各文字列)を先に、外側(配列自体)を最後に free するのが決まりです。順番を逆にすると、内側の文字列のアドレスが分からなくなってしまいます。
flowchart LR names["names(char**)"] --> n0["names[0](char*)"] --> s0[""Alice""] names --> n1["names[1](char*)"] --> s1[""Bob""] names --> n2["names[2](char*)"] --> s2[""Carol""]
やってみよう
src の名前を自分の好きな文字列に変えて実行してみましょう。free の順番を「先に names を解放してから各文字列を解放する」順に変えるとどうなるか(動かなくなる理由)も考えてみてください。
演習
"Dan", "Eve", "Frank", "Grace" の4つの文字列を char** で管理してください。各文字列の長さ(strlen)を合計して total に足し込み、printf("%d\n", total); で表示してください(正しく書けると 16 になります)。最後にすべて free してください。
ヒント1を見る
strlen(names[i]) で1本ずつの長さが分かります。
ヒント2を見る
ループで total += strlen(names[i]); を足していきます。