わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

constの位置

1年後期,2年前期向けに担当している,Cプログラミングの授業では,constはあまり積極的には取り上げていません.とはいえ,strlenなどで出現するので,文法として知っておかないといけません.そこで,最低限のことを説明しています.具体的には以下の3点です.

  • const修飾された変数は,その値を変更できません.
  • 文字列を操作するいくつかの標準ライブラリ関数では,仮引数となるポインタ変数がconst修飾されています.
  • Tを任意の型として,T *型のポインタ変数の値をconst T *型のポインタ変数に代入するのは問題ありません*1

さて…

●ルール2
 次の条件に当てはまる場合は、常にconst修飾子を使って宣言する。
(略)
(2)関数に値を参照(ポインタ)で渡し(call-by-reference)、その値を関数内で変更することがないならば、その関数の宣言時にその引数にconst修飾子を付ける。例「char const *p_data」。
(略)
 これらの理由は、コンパイラの機能を使ってデータをリード・オンリーにして、意図しない書き込みからデータを保護するためである。

http://eetimes.jp/article/23004/2.html

少し探すと,原文も見つかりました

Rule: The const keyword shall be used whenever possible, including:
(snip)

  • To define call-by-reference function parameters that should not be modified (e.g., char const * p_data),

(snip)
Reasoning: The upside of using const as much as possible is compiler-enforced protection from unintended writes to data that should be read-only.

http://www.embeddedgurus.net/barr-code/2009/03/coding-standard-rule-2-use-const.html

何に引っかかったのかというと,「char const *p_data」のところです.
文法上,これは「const char *p_data」と等価です.どちらを使うべきかは,プログラマの好みがあるでしょうし,複数人で書くなら,コーディング標準として定めれば済む話です.
ただ,「char const *p_data」を見ると,「char *const p_data」と書きたかったのか,と思わずにはいられません.
この2つの宣言は,意味が異なります.すなわち,

  • const char *p_dataあるいはchar const *p_dataと宣言し,この変数に適切な値が代入されたとき,
    • p_dataの指し示す先の値(*p_dataだけでなくp_data[2]といった値も)は変更できません.
    • p_dataの持つ値を,char *型のポインタ変数に代入することはできません.
    • p_dataの持つ値は変更できます.すなわち p_data++; と書くことができます.
  • char *const p_dataと宣言し,この変数に適切な値が代入されたとき,
    • p_dataの指し示す先の値(*p_dataだけでなくp_data[2]といった値も)は変更できます.
    • p_dataの持つ値を,char *型のポインタ変数に代入することもできます.
    • p_dataの持つ値を変更することはできません.すなわち p_data++; と書くことができません.

理由のところや,実用から,「char const *p_data」はこれが意図であり「char *const p_data」の語記ではないことが,想像できます.
しかしながらconstは,ポインタを絡めた変数宣言で上記の通り混乱しやすいので,それを避けるためにも,const使用のルールを決めるべきですね.「俺コーディング標準」なら,こんなところ.

  • const修飾をするのは以下のいずれかとする.
    • 型付きの定数を宣言するとき.
    • 関数の仮引数の宣言で,指し示す先(典型的には,配列の中身)を書き換えないことを明示するとき.
    • const修飾されているポインタ型の値が代入される変数を宣言するとき*2
  • const修飾を持つポインタ変数は,「const T *var」の形式で宣言し,「T const *var」の形式は使用しない.また「const *T var」の形式は使用しない.

そういえば,constについては過去に書いていました:constの使い道 - わさっき

*1:逆に,const T *型のポインタ変数の値をT *型のポインタ変数に代入しようとすると,コンパイル時に警告が出ます.ただしこれは授業の範囲外にしています.

*2:仮引数をconstつきポインタ変数として関数を定義していて,その中で,その変数を実引数として呼び出すことになるような関数を別途定義する,という状況が典型的です.