3

return関数の句を書き忘れたときにこの問題に遭遇しましたが、 に警告もエラーもありませんでしたgcc私はそれを修正しましたが、なぜ関数がreturn. ここに私が試したいくつかの例があります:

#include "stdio.h"
#include "stdlib.h"

int func1 () {
        int i;
        i = 2;
}

int func2 (int a) {
        int i = a+3;
}

int func3 () {
        int i;
        for (i = 0; i <= 1; i++);
}

int main(void) {
        int a = 0;
        int b = 0;
        int c = 0;
        a = func1();
        printf("a = %d \n", a);
        b = func2(a);
        printf("b = %d \n", b);
        c = func3();
        printf("c = %d \n", c);
}

結果は次のとおりです。

a = 1 
b = 4 
c = 7 

私の質問:

1) なぜこのような結果になったのですか? これに関する一般的なルールはありますか?
2) エラーを報告するのではなく、なぜこのことを保持するのですか? どこかで「役に立つ」ことはありますか?

4

3 に答える 3

7

これは、セクション 6.9.1、パラグラフ 12 (C 標準の N1570 ドラフト) による未定義の動作です。

関数}を終了する に到達し、関数呼び出しの値が呼び出し元によって使用された場合、動作は未定義です。

戻り値が呼び出し元によって使用されない場合、動作は未定義ではありません。

単なる未定義の動作に必要な診断メッセージはないため、コンパイラは警告する義務はありません。gcc は ( -Wreturn-type、 によって暗示される-Wall) ように要求すると警告し、clang はデフォルトで警告します。

以外の型を返す関数の場合voidreturn6.8.6.4 のパラグラフ 1 に従って、値が返される式をステートメントに含める必要があります。

式を含むreturnステートメントは、戻り値の型が void である関数に現れてはなりません。式のreturnないステートメントは、戻り値の型が void である関数にのみ出現します。

于 2013-05-15T11:56:35.867 に答える
5

これは未定義の動作であり、使用されている呼び出し規約に依存します。呼び出し元がレジスター内の結果を期待している場合、レジスター内の最後の値が使用されます。

編集

セクション関数定義の段落12ドラフトC99標準には、次のように記載されています。6.9.1

関数を終了する } に到達し、関数呼び出しの値が呼び出し元によって使用された場合、動作は未定義です。

clangはデフォルトでgcc警告し-Wall、 で警告します。一般に、警告を有効にする必要があります。

于 2013-05-15T11:48:39.507 に答える
2

私のシステム (gcc 4.5.3、linux) で、コードを最適化せずにコンパイルすると、次のようになります。

main:
...
    call    func1
    movl    %eax, 28(%esp)
...

つまり、 の戻り値はfunc1eax レジスタから取得されますが、そのレジスタは 内func1で設定されることはありません。したがって、戻り値は、関数が呼び出されたときにたまたまそのレジスタにあったものです。

于 2013-05-15T11:55:36.717 に答える