組込みソフト向けC言語コーディング規約|ポインタ

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

この記事ではポインタの注意すべき点について記載します。「ヌルポインタ」や「ポインタ演算」が、よくバグが発生する箇所です。

他のコーディング規約は下記のページを参照してくださいませ。
https://hangstuck.com/ccpp-coding-rule-fixedpage/

スポンサーリンク

ヌルポインタの表現には0でなく”NULL”を使用する

コンパイラは、値が0のポインタを「ヌルポインタ」と解釈するので、通常は、定数0とNULLとを区別しなくても問題無く動作します。ですが、コンパイラがポインタ型なのか整数型なのか判断できないときは、正常に動作しない可能性があります。特に可変引数関数の場合、コンパイラが型チェックをできないので注意が必要です。表現上もまぎらわしいので、ヌルポインタには0でなくNULLを使用してください。

違反コード

// 可変引数関数.
// 最後の引数は番兵としてヌルポインタを渡す仕様とする.

void va_list_func(char *string, ... );

void func_NG(void)

{
    // NG. 最後の番兵のヌルポインタを0で渡しているので、
    //     正常に動作しない可能性がある

    va_list_func("Hello,", "world.\n", 0);
}

適合コード

// 可変引数関数.
// 最後の引数は番兵としてヌルポインタを渡す仕様とする.
void va_list_func(char *string, ... );

void func_OK(void)

{
    // OK. NULLで渡しているので、コンパイラもポインタだと理解できる。

    va_list_func("Hello,", "world.\n", NULL);
}
スポンサーリンク

ポインタはNULLでないことを確認してから参照する

NULLポインタへのアクセスはしないでください。どこを向いているか分からないポインタにアクセスするときは、必ずNULLでないか確認してください。(特に関数の引数でポインタをもらう場合。)

違反コード

void func_NG(int *p)
{
   *p = 0xff;  // pがNULLを指しているかもしれない.
}

適合コード

void func_OK(int *p)

{
    if (p != NULL)
        *p = 0xff;
}
スポンサーリンク

ポインタをより小さい型へのポインタへキャストしない

異なるサイズの型には異なるアラインメントが適用される可能性があります。例えば処理系によっては、int型(4Byte)は4Byteアラインメントされ、char型(1Byte)は1Byteアラインメントされます。このとき、奇数バイトに配置されたchar型へのポインタ変数をlong型へのポインタ変数にキャストすると、long型は4Byteにアラインメントされている必要があるため正しくアクセスできません。大きい型へのポインタを小さい型へのポインタにキャストしないでください。

違反コード

// longへのポインタ型を引数とする関数.
void func_fuga(long *long_table);

void func_NG(void)

{
    char char_hoge[DATA_LENGTH];

    // NG.  もしchar_hogeが奇数バイトに置かれていたい場合、正常に動作しない。
    func_fuga((long *) char_hoge);

    return;
}

適合コード

// longへのポインタ型を引数とする関数.
void func_fuga(long *long_table);

void func_OK(void)

{
    long long_hoge[DATA_LENGTH / sizeof(long)];

    // OK.
    func_fuga(long_hoge);

    return;
}
スポンサーリンク

関数のアドレスを取得するときはアドレス演算子”&”をつける

関数のアドレスを取得するときは、括弧をつけずに単に関数名を書けば取得できます。しかしそれだと関数コールの記述ミスなのかどうかが分かりづらいので、関数のアドレスを取得するときも通常の変数と同じように”&”をつけてください。

違反コード

#include <string.h>

// 関数ポインタ変数.
char* (*func_ptr)(char *buf1, const char *buf2);

void func_NG(void)

{
    // NG, strcpyのコールミスなのか、strcpyのアドレスを取得しているのか分かりにくい.
    func_ptr = strcpy;

    // NG, func_ptrという関数自体をコールしているのか,
    //     func_ptrというポインタ変数を介して関数コールしているのか分かりにくい.
    func_ptr(s1, s2);
}

適合コード

// 関数ポインタ変数.
int (*func_ptr)(void);

void func_OK(void)

{
    // NG, strcpyのアドレスを取得しているのが明示的に分かる.
    func_ptr = &callback_func;

    // OK,  func_ptrというポインタ変数を介して関数コールしているのが明確.
    (*func_ptr)();
}

以上、ポインタについてのコーディング規約でした!

コメント