warning: operation on ‘x’ may be undefined
警告: ‘x’ に対する演算は未定義動作の可能性があります。
[-Wsequence-point]
■1.未定義動作はバグである
未定義動作はバグである。
意図した通りにプログラムが動いていてもいなくても。
未規定動作はバグではないが、
コンパイラによって動作が変わるので避けたほうが良い。
■2.最も単純な未定義動作
void f1(void)
{
int x ;
x = x = 0;//一式内で同一変数を2回以上更新
}
感覚的には問題なさそうですが変数 x を一式の中で2回更新しているので、C言語規格で未定義動作となります。
未定義動作とは文字道理コンパイラがどのように動くか定義されていないので動作保証がありません。
プログラマが意図した通り動くかもしれませんし動かないかもしれません。
たまたま意図した通り動いたとしても動作保証がないので
簡単に言うと、
未定義動作のコードはバグです。
■3.同一変数を一式で二回以上更新
void f2(void)
{
int y = 0 ;
for(int i = 0 ; i < 256;i++){
y = ++y ;//一式内で同一変数を2回以上更新
printf("y = %d\n",y);
}
}
これは初級者がよく記述してしまう未定義動作の例です。
y = ++y の式で
++ で一回更新
= で一回更新
合計2回 y を更新しているので未定義動作のバグとなります。
■4.カウンターストップマクロと未定義動作
//カウンターストップマクロ使用時の未定義動作
#define INC(z) ((z < 128)?(++z):(z))
void f3(void)
{
int z = 0 ;
for(int i = 0 ; i < 256;i++){
z = INC(z) ;//一式内で同一変数を2回以上更新
printf("x = %d\n",z);
}
}
これはカウンターが上限値に達したら上限値でカウントをストップしたいマクロ使用時によく発生する未定義動作です。
z = INC(z) は見かけ上問題ないのですがマクロを展開すると
z = ((z < 128) ? (++z) : (z)); となり
++ で一回更新
= で一回更新
合計2回 z を更新しているので未定義動作のバグとなります。
z = INC(z) ;を
INC(z) ;に変更すると問題は解決しますが
誤って代入できないように
#define INC(z) ((z < 128)?(++z):(z))
を
#define INC(z) (void)((z < 128)?(++z):(z))
とすることを推奨します。

■5.関数引数の評価される順番(未規定動作)
#include <stdio.h>
static int G = 1;
int one(void){
printf("A");
G = 2;
return 1;
}
int two(void){
printf("B");
G *= 3;
return 2;
}
int main(void){
printf("%d:%d:%d:%d\n",G,one(),two(),G);
}
printfの引数が
G→one()→two()→Gの順番で評価されるとは限りません。
gccの結果
BA2:1:2:1
clangの結果
AB1:1:2:6
■6.推奨する修正例
#include <stdio.h>
static int G = 1;
int one(void){
printf("A");
G = 2;
return 1;
}
int two(void){
printf("B");
G *= 3;
return 2;
}
int main(void){
int x = one();
int y = two();
printf("%d:%d:%d:%d\n",G,x,y,G);
}
実行結果
AB6:1:2:6
参考:
EXP30-C. 副作用が発生する式の評価順序に依存しない
https://www.jpcert.or.jp/sc-rules/c-exp30-c.html
C-FAQ 3.2: 同じオブジェクトが2回変更された
http://www.kouno.jp/home/c_faq/c3.html#2