Cプログラマへの道 #7 - ポインタ 続

Last Edited: 8/5/2024

このブログ記事では、ポインタについてまだ紹介していない概念を紹介します。

C Pointers

NULL ポインタ

ポインタを初期化せずに宣言した時、まだどのアドレスにも紐付いていないと考えるかもしれませんが、実際は そうではありません。

int main () {
    int *p;
    printf("%p\n", p); // => 0x16 (または他のアドレス)
    return 0;
}

ポインタをアドレスに紐付かせないようにするには、NULL ポインタを使うことができます。

int main () {
    int *p = NULL;
    printf("%p\n", p); // => 0x0
    return 0;
}

malloccalloc、または realloc を使って莫大すぎるメモリを要求すると、これらは NULL を返します。この場合、 返されたポインタが NULL かどうかをチェックして対処することができます。また、メモリを解放した後でもポインタが 同じアドレスを指し続けるダングリングポインタを防ぐためにも NULL ポインタを使用できます。

int main () {
    int *p = malloc(sizeof(int) * 10);
 
    if (p == NULL) {
        printf("Pointer is NULL");
    } // malloc 後に p が NULL かどうかをチェック
 
    free(p);
    p = NULL; // ダングリングポインタを防止
 
    return 0;
}
 

(特に大きなコードベースでは)メモリが既に解放されたことに気づかずにそれらをアクセスすることがあり、予測不可能な動作や セグメンテーションフォルトを引き起こす可能性があるため、ダングリングポインタを防ぐ必要があります。

Void ポインタ

ポインタに型を設定することで、ミスを避けることができますが、ポインタが指している変数のデータ型についてあまり 厳密にしたくない場合もあります。そのような場合には、void ポインタが役立ちます。

int main () {
    int a = 5;
    float b = 3;
 
    void *p; // Void ポインタ
 
    p = &a;
    p = &b; // 警告なし
 
    return 0;
}

任意の型のポインタを受け取る、または返す関数を作りたい場合に、void ポインタは特に役立ちます。 malloccallocrealloc はそのような関数の例です。void ポインタを直接デリファレンスすることはできず、型キャストせずに ポインタ演算を行うことはできないことに注意してください。

関数ポインタ

変数にポインタを設定できるように、関数にポインタを設定することもできます。

void function (int x) {
    printf("%d\n", x);
}
 
int main () {
    // 関数ポインタ
    void (*pFunction) (int);
 
    pFunction = function;
 
    int a = 5;
    
    pFunction(a); // "5"
 
    return 0;
}

関数ポインタは、他の関数への引数として関数を渡したい場合に役立ちます。これをコールバック関数と呼びます。

// コールバック関数
// 摂氏
int freezing_C (float a) {
    if (a <= 0) {
        return 1;
    } else {
        return 0;
    }
}
 
// 華氏
int freezing_F (float a) {
    if (a <= 32) {
        return 1;
    } else {
        return 0;
    }
}
 
void is_freezing ( int (*freezing)(float), float a ) {
    if (freezing(a) == 1) {
        printf("寒いです!");
    } else {
        printf("寒くないです。");
    }
}
 
int main () {
    char scale;
    float temperature;
    printf("温度の単位をFまたはCで選択してください: ");
    scanf("%c", &scale);
    printf("温度: ");
    scanf("%f", &temperature);
 
    // 関数を引数として渡す
    if (scale == 'F') {
        is_freezing(freezing_F, temperature);
    } else {
        is_freezing(freezing_C, temperature);
    }
 
    return 0;
}

void ポインタと同様に、関数ポインタでもポインタ演算を行うことはできないので、注意が必要です。

クイズ

この記事では、学習した内容を確認するためのクイズを設けます。記事のメイン部分を読んだ後に、ぜひ自分で問題を解いてみることを強くお勧めします。各問題をクリックすると答えが表示されます。

リソース