Cの未定義の動作についてCの未定義の動作について

5873 ワード

Cの未定義動作について
回転元:http://www.guokr.com/blog/471312/
Cの初心者にとって、次のような問題を要求されるのは本当に脳障害で、これ以上脳障害にならない行為です.しかし、多くのC初級教程--意外にもこのような問題があります.最も典型的な例は
a+=a++; 

この場合、aは最後にどれだけに等しいのだろうか.
コンパイラはa+=a++をどのように理解すればいいのでしょうか.まず展開、a=a+a++次に、aとa++の値をそれぞれ計算し、それらを加算し、結果をaに付与する.しかし、ここでは、a++を実行した後、a++の値はa自体の値に等しいが、aの値はa+1になるという問題がある.だから肝心なのは処理順序です.
例えばint a=3;コンパイラが付与番号+=左のaの値を3と計算し、右のa++の値を3と計算すると、aは4になります.次に3+3=6を計算し、aに付与すると、aの現在の値は6になります.コンパイラが割り当て番号の右側のa++を計算すると、結果は3になり、同時にaは4になり、左のa=4になります.次に4+3=7と計算し,7という数をaに与えた.
つまり、異なる理解方法で、この例では意外にも異なる答えが得られますか?
どうして使うんだ?まさかこの結果は不思議ではないでしょうか.同じ表現で、ただコンパイラが違うだけで、違う結果が出てしまうのは悲劇ですね.コンパイラが同じ理解モードを採用することを要求する標準はありませんか?C言語の標準はANSI C標準に従う.しかし残念なことに、ANSI C規格では、このような状況に遭遇したらどのように処理すべきかという規定はなく、むしろ、コンパイラを見てやってくださいと指摘しています.これがC言語の「未定義の行為」です.
っていうか、GCD関数を1つ使っている間に某大神に突っ込まれただけで、このGCD関数は以下の通りです.
int GCD(int a,int b){ while (a %= b ^= a ^= b ^= a); return b; } 

上で分析したように、このプログラムはコンパイル中に、どのような順序が現れるか、これも標準に規定されていない、未定義の行為に属している.したがって,この関数で正確な結果が得られるとは限らない.
 
こんなことですか...コンパイラに渡した以上.これは実際にコード最適化のためだと思います.よく知られているように、Cは効率を重視する言語であり、効率のためにすべてを放棄したような感じがして、これを評価しないのはよくないが、どうせ最も人気のある言語ランキング1位の座に何年も座っている.コンパイラにより大きな自由を与えることで、コンパイラは生成されたバイナリコードをよりよく最適化することができます.境界配列のような他の側面もあります.こんな感じで
char str1[]="myworld"; str1[18]='\0'; 

配列str 1はどこから来たのか19項目ですね!!!こんなものがコンパイルできるなんて!!!境界配列を使用するのもCの「未定義の動作」です.Cの基準では、コンパイラがこのような状況に遭遇したらどうすればいいかは規定されていません.この时、コンパイラの考えは--多くのことは少なくないほうがいいと思います.私もここがこの配列の範囲かどうかをチェックしません.どうせあなたは私に書かせてくれたので、私は書きましょう.
もう1つの典型的な操作は、任意に指すポインタの読み書きを許可することです.たとえば、ダイナミック領域を申請して解放しました.
int *p; p=(int *)malloc(4*sizeof(int)); /*   p   */ free(p); p[0]=0; printf("%d%d%d%d",p[0],p[1],p[2],p[3]); 

誇張しないで、私自身のプログラムは何度もこのような場所で死んで、freeが後でprintします.のたとえば、
int *p;

p=0x1e642a80;

p[8]=24;

この指针操作もパスするとは???
さらに残念なことに、上記はCの「未定義行為」に属しています.つまり、これらの操作は可能ですが、コンパイラは実行結果を保証していません.つまり、このようなものは間違いなく通過しただけでなく、私たちが想像したように実行しないのではなく、コンパイラの性格で勝手に来ているのですか?
この角度から見ると、本当につらいです.私は脳障害のコードに出会ったことがあります.このようにしています.
char tips[]="No"; if(condition){ strcpy(tips,"Yes"); } 

このようなものに対して....ほほほ.Windowsの下で実行して中断されるのを待っていましょう..また、初期化されていない変数を使用するのも「未定義の動作」です.例えば:
int x;

printf("%d",x);

通常、あなたがスクリーンで何を見ているのか分かりません...GCCの第1版コンパイラはこのような状況に遭遇したとき、あなたのスクリーンで小さなゲームを始めます.初期化され、この特徴は非難されています.しかし、ANSI C自体は、これらの初期化操作を省略することで、実行速度を向上させたいに違いありません.結局、大きな配列を初期化したり、mallocで割り当てられた多くの空間を初期化したりするのは、やはり手間がかかります.
しかし、メリットは......配列境界をチェックせず、ポインタがアドレスを指す場合をチェックせず、強制タイプ変換が可能かどうかをチェックしないことです.プログラマープログラムの自覚次第で、Cのコード効率が高くなります.
だから、Cはプログラムセキュリティが必要な場合に多く使われていますが、「未定義の行為」の存在であるため、Cは決して安全な言語ではありません!!
しかし、多くの場合、このような理由で、Cで実現したいと思っています.
もう一つ説明しなければならないのは..最初の部分:
int a=3;

a+=a++;

ほとんどの現代コンパイラの結果は7です.
そのGCDの1行のアルゴリズムは、ほとんどの現代のコンパイラが正常に動作しています.「未定義アルゴリズム」ですが、これも黙々と合意に達しているのではないでしょうか.それを使うのは危険ですが.
本文は
飛ぶ魚ライセンス(
シェルネット)発表、文章著作権は原作者所有.