質問するログイン新規登録

回答編集履歴

2

誤字の修正など

2018/03/27 13:27

投稿

raccy
raccy

スコア21784

answer CHANGED
@@ -48,4 +48,4 @@
48
48
  }
49
49
  ```
50
50
 
51
- なぜなら、関数が終了する前にプログラムのシュル用処理が走ため、関数が終了して返り値が使われるということが起こりえないからです。`exit()`以外でも`longjump()`などの戻らない関数でも同じことが言えます。GCCではこのような場合には警告を出さず、正常にコンパイルできます。
51
+ なぜなら、関数が終了する前にプログラムの終了処理が走って、関数には戻らないため、関数が終了して返り値が使われるということが起こりえないからです。`exit()`以外でも`longjump()`などの戻らない関数でも同じことが言えます。GCCではこのような場合には警告を出さず、正常にコンパイルできます。

1

追記

2018/03/27 13:27

投稿

raccy
raccy

スコア21784

answer CHANGED
@@ -1,9 +1,51 @@
1
1
  質問のコードで全ての`if`文が偽であった場合、つまり、返り値の型が`void`では無いのに、`return`によって返り値を指定しない場合の動作は未定義です。
2
2
 
3
- C11最終ドラフト n1570 §6.9.1 Function definitons p.157
3
+ C11仕様書最終ドラフト n1570 §6.9.1 Function definitons p.157
4
4
  > 12 If the `}` that terminates a function is reached, and the value of the function call is used by the caller, the behavior is undefined.
5
5
  > 12 関数を終了する`}`に達し、関数呼び出しの値が呼び出し元によって使用されている場合、その動作は未定義です。
6
6
 
7
7
  Cでの未定義の動作というのは、コンパイラの種類、バージョン、コンパイル時オプション、実行時の環境、実行時の状態によって、どのような動作になるのかはわからないことを意味します。ですので、何が起きても不思議ではなく、Cの仕様通りの動作になります。
8
8
 
9
- なお、`main`のみ例外で、暗黙的に最後に`return 0`がある場合と同等の動作になります。(§5.1.2.2.3 Program termination 1 p.14)
9
+ なお、`main`のみ例外で、暗黙的に最後に`return 0`がある場合と同等の動作になります。(§5.1.2.2.3 Program termination 1 p.14)
10
+
11
+ ---
12
+
13
+ 【追記】
14
+ maisumakunさんがコメントで指摘していることについての追記です。
15
+
16
+ さて、n1570やmaisumakunさんが書いて有るとおり、未定義になるのは関数の返り値が使われたときです。
17
+
18
+ ```C
19
+ int x = sample();
20
+ ```
21
+
22
+ とあったときに、`x`がどうなるのかはわかりませんが、
23
+
24
+ ```C
25
+ sample();
26
+ ```
27
+
28
+ とだけあったときは、返り値を使わないことに意味があるのかどうかさておき、特に問題はありません。しかし、関数の返り値が使われるか、使われないかは関数側では制限できないため、実質使われる物であると判断しなければなりません。どうしても使われないということにしたいのであれば、関数に対するドキュメントとして「条件が○○で、かつ、その返り値を使用した場合の動作は未定義である。」と書いておく必要があります(Cでは、標準ライブラリの関数を含め、条件付きで未定義な動作をする関数は珍しくありません)。
29
+
30
+ ※ 返り値の型が`void`である場合は、返り値を使わないように強制できるため、問題は起きません。
31
+
32
+ このように、呼び出し側に依存するが、未定義にもならない場合もあると言うことあり、GCCでは`-Wall`を付けている場合、警告が表示されます。
33
+
34
+ さて、上とは異なり、呼び出し側がどのような場合であっても、次の場合は動作が未定義とはなりません。
35
+
36
+ ```C
37
+ int sample() {
38
+ if ( xxx ) {
39
+ return 1;
40
+ }
41
+ if ( yyy ) {
42
+ return 2;
43
+ }
44
+ if ( zzz ) {
45
+ return 3;
46
+ }
47
+ exit(0);
48
+ }
49
+ ```
50
+
51
+ なぜなら、関数が終了する前にプログラムのシュル用処理が走るため、関数が終了して返り値が使われるということが起こりえないからです。`exit()`以外でも`longjump()`などの戻らない関数でも同じことが言えます。GCCではこのような場合には警告を出さず、正常にコンパイルできます。