本文へスキップ
BecomeCoder

C言語コース · 第8章 動的メモリ管理 · レッスン33

ポインタのポインタ(`**`) ― 動的な文字列配列

ブラウザで完結

導入

文字列」は 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["&quot;Alice&quot;"]
  names --> n1["names[1](char*)"] --> s1["&quot;Bob&quot;"]
  names --> n2["names[2](char*)"] --> s2["&quot;Carol&quot;"]

やってみよう

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]); を足していきます。

実際に動かしてみよう

下のエディタにCを書いて「コンパイル & 実行」を押すと、ブラウザ内で本物のCコンパイラ(clang + dlmalloc)がその場でコンパイルして実行し、出力が表示されます。構造体・共用体・malloc/free・関数ポインタ・ファイル入出力などがそのまま動きます。本文の例を書き換えて試しながら進めましょう(初回だけコンパイラの読み込みに時間がかかります。以降はキャッシュされます。この回は標準入力(scanf)は使わず、値はコードに直接書きます)。

C/C++ — ブラウザ内で本物のコンパイラで実行

ブラウザ内で本物のC/C++コンパイラ(clang + libc++ / WebAssembly)を動かします。構造体・malloc/free・std::string・vector・クラス・テンプレート・STL などがそのまま実行できます(初回のみコンパイラの読み込みに時間がかかります。以降はブラウザにキャッシュされます)。
スクロールして表示された時点でも自動で読み込まれます。