« 社員1.0 と 社員2.0 | トップページ | 「私立」と「公立」と受け入れ拒否 »

2006/03/04

Const Statement does not mean "INVARIABLE number".

ありえるえりあのBlogで以下のような記述がありました。

他人のコードを見ていると、次のような箇所を見つけました。変数名や関数名は適当です。
void func(const char *input)
{
char *pt = strstr(input, "foo");
*pt = '\0';
...
}
const char*で渡っているのに、ポインタの先が書き変わっています。しかし、コンパイラは何も言ってくれません。
やはり C は危険です。

誤解している人が多いのですが、const キーワードは変数を定数に変えるものではありません。 const がついたシンボル経由では値を書き換えられないというだけで、他のシンボルを経由するなどして値を書き換えることは可能なのです。たとえば、以下のコードもコンパイルできますし、問題なく実行できます。

int main ( void )
{
const int hoge = 10;
int * ptr_int_change = & hoge;

*ptr_int_change = 50;

printf("%d", hoge);

return 0;
}

実行結果: 50

変数 hoge には const 修飾詞がついているので、hoge = 50; というコードはエラーになります。 ( error: assignment of read-only variable `hoge' というエラーが出力されます。) しかし、ポインタ ptr_int_change には const 修飾詞はついていませんから、 ptr_int_change を経由すれば hoge の値を書き換えることが可能なのです。

ありえるえりあにあるコードでは pt には const 修飾詞がついていません。よって、pt という別経路を使うことで、input の参照先を問題なく書き換えることができたのです。 input を使って値を変更しようとしているならば、エラーが出たでしょう。 引数として渡した文字列を書き換えることが目的ならば、仕様通りの動作ということで問題ないのでは? ( コメントで、関数の目的と動作を補足したほうがいいと思いますが。)

2006.03.06 20:50 修正

|

« 社員1.0 と 社員2.0 | トップページ | 「私立」と「公立」と受け入れ拒否 »

コメント

だからCが危険、という話では?
(ちなみにC++では例示のコードはコンパイルエラーが
でますね)
しかもCって、strstr()のような標準関数からして
const外したポインタを返してくれるのでタチが悪いです。

投稿: 通りすがり | 2006/03/04 14時25分

strstr()を知らない?
文字列検索を自分で書いちゃう人ですか?

投稿: ふまる | 2006/03/04 17時19分

通りすがりさん、ふまるさんご指摘ありがとうございます。
strstr() は標準関数ですね。
どっかでみたことあるな~と思いつつ、思い出せないのでそのままスルーしてしまいました。
申し訳ありません。

投稿: Fomalhaut | 2006/03/04 20時40分

>ありえるえりあにあるコードでは pt には const
>修飾詞がついていません。

つけなくてもコンパイルが通ってしまうことが問題
なのです。
strstrのプロトタイプ宣言が(少なくともC89では)
char* strstr(const char*, const char*);
となっており、返値をconstなしのchar*で受けることが
できてしまいます。
結果的にキャストをかけてconstを外したのと
同じ意味になってしまいます。Cの危険性の1つです。

>引数として渡した文字列を書き換えることが目的
>ならば、仕様通りの動作ということで問題ないので
>は? ( コメントで、関数の目的と動作を補足した
>ほうがいいと思いますが。)

void func(const char *input) としている以上、
渡した文字列を書き換えることは単なるバグです。

…Cは危険な罠がいっぱいなので気をつけましょう
(または型チェックが厳しい言語に乗り換えましょう)、
というのが引用元の主旨ではないか、と愚考する次第
です、はい。

投稿: 通りすがり | 2006/03/04 21時25分

strstr() の戻り値に const がついていないのは不自然なことではないと考えています。

strstr() は第一引数の文字列中に第二引数の文字列があるかどうかを確かめて、
合致した場合、その場所の先頭のポインタを返しますが、
const がついていたら処理に不便なこともあります。

また、void func(const char*) であっても引数として渡した変数を書き換えることはCのシステム上問題はありませんし、実際に見たこともあります。

C は危険というよりも、プログラマに依存する部分が大きいだけではないでしょうか?
これは、見方を変えると、 C の自由度の高さを表していると思いますが。

投稿: Fomalhaut | 2006/03/05 17時55分

# 元ネタ主の井上です。

> …Cは危険な罠がいっぱいなので気をつけましょう
[snip]
> というのが引用元の主旨ではないか、と愚考する次第です

その読み方で正しいです。
# ただ、Cを捨てろと他人には言うかもしれませんが、自分では(まだ)捨てません。


> void func(const char*) であっても引数として渡した変数を書き換えることはCのシステム上問題はありません

可能、不可能で言ってしまえば、可能ですね。
# 可能性の問題で言ってしまえば、呼び出された関数から、呼び出し元のローカル変数すら書き換え可能ですし。

引数の文字列をconst char*の型で受け取るということは、関数実装者が、「その文字列を書き換えませんよ」と、呼び出し側に対して約束(契約)しているものだ、と思っています。

まあ、Cでは、この契約を守るのがプログラマの意志だけに依存しているので、とほほ、というコードに出会うことになります。

投稿: 井上 | 2006/03/07 18時58分

>井上さん
おこしいただきありがとうございます。

恐らく、その「とほほ」なコードを量産しているへたれプログラマですが、
今後も遊びに来てください(笑

投稿: Fomalhaut | 2006/03/16 00時21分

この記事へのコメントは終了しました。

トラックバック


この記事へのトラックバック一覧です: Const Statement does not mean "INVARIABLE number".:

« 社員1.0 と 社員2.0 | トップページ | 「私立」と「公立」と受け入れ拒否 »