DCL13-C. 関数の引数が関数自身によって変更されない値を参照するポインタならば、関数の引数をconstとして宣言する
関数の引数を const
として宣言すると、その関数が引数の値を変更しないことを約束するということを意味する。
C 言語では、関数の引数は参照ではなく値として渡される。関数は渡された値を変更するかもしれないが、これらの値は、ひとたび関数が呼び出し元に戻ると破棄される。そのため、多くのプログラマは、関数はその引数を変更せず、関数の引数を const
として宣言する必要はないと考えがちである。
void foo(int x) { x = 3; /* 関数が exit するまで有効 */ /* ... */ }
ポインタも同様の動作をする。関数はポインタを他のオブジェクトや NULL
を参照するように変更するかもしれないが、そのような変更は関数が exit する際に破棄される。したがって、ポインタを const
として宣言することは不要である。
void foo(int *x) { x = NULL; /* 関数が exit するまで有効 */ /* ... */ }
違反コード
値渡しの引数やポインタとは違い、ポインタによって参照される値は問題になる。関数はポインタが参照する値を変更する可能性があり、これは関数が exit した後にまで影響する副作用につながる。コンパイラはポインタによって「参照される値」の変更は診断せず、値の変更は意図した動作であるとみなす。
void foo(int *x) { if (x != NULL) { *x = 3; /* 関数外から可 */ } /* ... */ }
関数の引数が const
修飾されている場合、ポインタによって参照される値を変更しようとするとコンパイラが致命的エラーを出す。
void foo(const int *x) { if (x != NULL) { *x = 3; /* コンパイルエラーを出す */ } /* ... */ }
結局、問題を解決しなければコードをコンパイルできない。
適合コード
以下の適合コードでは、定数引数を変更しないようにすることで、問題に対処している。
void foo(const int * x) { if (x != NULL) { printf("Value is %d\n", *x); } /* ... */ }
違反コード
以下のコードでは、標準関数 strcat()
の架空の関数 strcat_nc()
を定義している。この関数が strcat()
と異なる点は、第二引数がconst
修飾されていない点である。
char *strcat_nc(char *s1, char *s2); char *str1 = "str1"; const char *str2 = "str2"; char str3[9] = "str3"; const char str4[9] = "str4"; strcat_nc(str3, str2); /* コンパイラは str2 が const であると警告する */ strcat_nc(str1, str3); /* 文字列リテラルを上書きしようとしている! */ strcat_nc(str4, str3); /* コンパイラは str4 が const であると警告する */
この関数は strcat()
と同様に動作するが、コンパイラは間違った位置で警告を出し、正しい位置で警告を出し損ねる。
一番目のstrcat_nc()
呼び出しでは、コンパイラはstr2
のconstをキャストによりはずそうとすることに警告を出す。警告がでるのは、strcat_nc()
は第二引数を、変更しないにもかかわらず、const
として宣言していないからである。
二番目のstrcat_nc()
呼び出しでは、コンパイラは警告を出さずにコンパイルするが、結果のコードはリテラル"str1"
を変更しようとしている。これは「STR05-C. 文字列リテラルの参照には const へのポインタを使用する」と「STR30-C. 文字列リテラルを変更しない」に違反する。
最後の strcat_nc()
呼び出しでは、コンパイラは str4
の const
をキャストによりはずそうとすることを警告するが、これは正しい警告である。
適合コード
以下の適合コードでは、C90のstrcat()
プロトタイプを使用している。C90にはrestrict
型修飾子は存在しなかったが、const
は存在した。一般に、関数の引数は関数のセマンティックスと一貫した形で宣言されるべきである。strcat()
の場合、第一引数は関数によって変更されるが、第二引数は変更されない。
char *strcat(char *s1, const char *s2); char *str1 = "str1"; const char *str2 = "str2"; char str3[9] = "str3"; const char str4[9] = "str4"; strcat(str3, str2); /* 引数を逆にして文字列リテラルが変更されないようにしている */ strcat(str3, str1); strcat(str4, str3); /* コンパイラは str4 が const であると警告する */
第二引数を const
修飾することにより最初の関数呼び出し時にでる嘘の警告が出ないようにしているが、const
修飾されたオブジェクトを第一引数(変更される)に渡している最後の関数呼び出しでは、正しい警告が出るようにしている。また、二番目のstrcat()
呼び出しは正しい呼び出しになっている。str3
は結果を格納する先として有効であり、安全に値を変更できる。
リスク評価
不変の値を const
として宣言していないと、関数は const
としてキャストされた値を使って動作することはできない。この問題は、const
修飾をキャストによりはずすことで回避しうるが、そうすると今度は「EXP05-C. const 修飾をキャストではずさない」に違反する。
レコメンデーション |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
DCL13-C |
低 |
低 |
低 |
P3 |
L3 |
自動検出(最新の情報はこちら)
ツール |
バージョン |
チェッカー |
説明 |
---|---|---|---|
Compass/ROSE |
|
|
レコメンデーション「DCL00-C. 不変(immutable)オブジェクトは const 修飾する」の違反をチェックする際にこのレコメンデーションの違反を検出することができる。 |
ECLAIR |
1.1 |
cnstpnte |
実装済み |
LDRA tool suite |
V. 8.5.4 |
62 D |
実装済み |
PRQA QA-C | 8.1 |
3673 |
実装済み |
関連するガイドライン
CERT C++ Secure Coding Standard | DCL13-CPP. Declare function parameters that are pointers to values not changed by the function as const |
ISO/IEC TR 24772:2013 | Passing Parameters and Return Values [CSJ] |
翻訳元
これは以下のページを翻訳したものです。
DCL13-C. Declare function parameters that are pointers to values not changed by the function as const (revision 66)