2009年10月30日

cast-as-lvalue

長らく 3.x だった MinGW の GCC も、ようやく今年の 6 月に 4.0 がリリースされ、4.x 移行はだいたい終了したのではないかと思われます。今後は 4.x を無視することはできないと思われます。

gcc 3.x から 4.x に移行する際に問題となる変更の 1 つとして、cast-as-lvalue という GCC 拡張の削除があります。
cast-as-lvalue とは、キャストの結果を左辺値として使える、という機能です。


以下のように、3.x では警告で済んでいたコードが、4.0 からは通らなくなっています。
$ cat cast_as_lvalue.c
int main()
{
    float f;
    (int)f = 0;
    return 0;
}

$ gcc -v
。。。
gcc version 3.4.5 (mingw-vista special r3)

$ gcc cast_as_lvalue.c
cast_as_lvalue.c: In function `main':
cast_as_lvalue.c:5: warning: use of cast expressions as lvalues is deprecated

$ mingw32-gcc-4.4.0.exe cast_as_lvalue.c
cast_as_lvalue.c: In function 'main':
cast_as_lvalue.c:5: error: lvalue required as left operand of assignment
多少冗長ですが、以下のように、キャスト結果それ自体を左辺値にしないように書けば大丈夫です。
$ cat cast_as_lvalue1.c
int main()
{
    float f;
    *((float*)&f) = 0;
    return 0;
}

$ mingw32-4.4.0-gcc.exe -ansi -pedantic cast_as_lvalue1.c
$
cast-as-lvalue は、簡単な代入文の場合は便利ですが、ちょっと複雑になると、容易にメモリアクセスのセマンティクスが曖昧になってしまいます。

例えば、以下のようなプログラムの場合、後置の ++ は、一体どの型で p をインクリメントするのでしょうか ?
$ cat cast_as_lvalue2.c
#include <stdio.h>
int main()
{
    int a[10];
    char *p;

    p = a;
    *((int *)p)++ = 0x123;
    *((int *)p)++ = 0x456;

    printf("0x%x\n", a[1]);

    return 0;
}
p は int * にキャストされた後、右辺値として dereference され、その結果の int 型の変数を左辺値として 123 が代入されます。ここまでは何も問題無いです。
しかし、その後、p は int * のまま 4 (sizeof int) インクリメントされるのでしょうか ? それとも、元の char * として 1 インクリメントされるのでしょうか ?

手元の GCC 3.4.5 では 0x456 が出力されるので、int * としてインクリメントされているようです。 VC++ 2008 の cl でも同じでした。(cl では通るようです。gcc-4.4.0 では lvalue required as increment operand というメッセージとともにエラーになります。) しかし、一般にはどちらの解釈も可能だと思われます。

もし cast-as-lvalue のセマンティクスを厳密に定義しようとするならば、非常に複雑な仕様になるだろうと予想されます。GCC の採った、機能自体の完全削除という選択は、一番すっきりした妥当な選択だと思います。

トラックバックURL

コメントする

名前
 
  絵文字
 
 
記事検索
最新コメント
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード