mainがここで0を返さないのはなぜですか?


116

読んでただけ

ISO / IEC 9899:201x委員会草案— 2011年4月12日

5.1.2.2.3プログラムの終了で見つけた場所

..reaching the } that terminates the main function returns a value of 0. 

つまり、でreturnステートメントを指定せずmain()、プログラムが正常に実行された場合、mainの右中括弧}で0が返されます。

しかし、次のコードではreturnステートメントを指定していませんが、0を返しません

#include<stdio.h>
int sum(int a,int b)
{
return (a + b);
}

int main()
{
    int a=10;
    int b=5;
    int ans;    
    ans=sum(a,b);
    printf("sum is %d",ans);
}

コンパイル

gcc test.c  
./a.out
sum is 15
echo $?
9          // here it should be 0 but it shows 9 why?

69
仕様を読むための忍耐力があることに対する+1 ....
Asher

16
gccそれ自体(バージョン4.6.2の場合)は、非常によく似ていますがCとはまったく似ていない言語をコンパイルします。GnuC89をコンパイルします。これは、C89に基づいて「緩やかに」言語です。
pmg 2011

2
returnステートメントの括弧sum()は不要です。 int main()する必要がありますint main(void)
キース・トンプソン

24
混乱!=タイプミス。私のキーボードでは、 '0'と 'o'は、後者になりやすいほど十分に近いです。;-)
The111

2
IMHOは、暗黙の「戻り値0」を追加することによってコンパイラに特別な方法で「メイン」関数を管理させるため、非常に愚かな仕様です。したがって、「main」という名前の関数の動作は少し異なります。コンパイル時のチェックはどうですか(「戻り値なし」と同様)。
Giuseppe Guerrini、2011

回答:


141

その規則は、C標準の1999バージョンで追加されました。C90では、返されるステータスは未定義です。

-std=c99gccに渡すことで有効にできます。

余談ですが、興味深いことに9が返されprintfます。これは、9文字を書き込んだ結果であるからです。


40
またはreturn 0;、クロージングの前に追加することもできます}。これは無害であり、プログラムを古いコンパイラに移植できます。
キース・トンプソン

2
@ Mr.32:良い観察です。printf()は文字列の長さを返すため、メインの「戻り」である9になります(-std = c99を使用しない場合)。
Hicham 2011

1
@cnicutar:通常、関数はスタック上で小さな値を返しません。移動と戻りだけでなく、ポップ、プッシュ、およびジャンプが含まれるためです。そのため、eax特にx86では、ほぼ確実にレジスタになります。
Jon Purdy、2011

8
はい、x86 APIは通常、eaxレジスタを介して整数のような値を返します。詳細については、en.wikipedia.org / wiki / X86_calling_conventions#cdeclを参照してください。
Sylvain Defresne 2011

2
「int foo(void){bar();}」のようなコードを何度か見ましたが、「return bar()」が意図されていました。このコードは、明らかなバグにもかかわらず、ほとんどのプロセッサで問題なく動作します。
ugoren

15

printf実際に印字された文字数を返します。


質問は、プログラムの実行時にコンソールに何が出力されるかについては話していません。プログラムの戻り値について話します:(Linuxではschellコマンド:echo $?およびウィンドウでは:echo%errorlevel%で取得できます。 )
Hicham 2011

1
@eharvest %errorlevel%Windowsでチェックする方法がわかりませんがmain、Linuxでの終了コードと戻り値の違いはありますか?
Summer_More_More_Tea 2011

1
@eharvest:答えは、コンソールに出力される内容についても説明していません。それは語る戻り値printfこのような場合には、その後から終了コードとして「昇格」を取得します9であるmain特定のgccのバージョンを使用する場合に何とか。
AH

ごめんなさい。私の間違い。あなたが正しいです。私はこの答えを速く読みすぎました:/
Hicham

1
これは保証されていないことに注意してください。C89 / C90では、この場合に返されるステータスは未定義です。何でもかまいません。コンパイラーは他に何も返そうとしないため、たまたま9を返します。他のコンパイラはおそらく異なる動作をします。
キーストンプソン

6

関数からの戻り値は通常、CPUのeaxレジスタに格納されるため、「return 4;」というステートメントが返されます。通常コンパイルする

mov eax, 4;
ret;

xを返す(コンパイラによって異なります)は次のようになります。

mov eax, [ebp + 4];
ret;

戻り値を指定しない場合、コンパイラーは「ret」を吐き出しますが、eaxの値は変更しません。したがって、呼び出し元は、eaxレジスターに以前に残っていたものが戻り値であると考えます。この例では、通常、戻り値はprintfですが、コンパイラーが異なれば、生成されるマシンコードも異なり、一部のレジスターも異なります。

これは簡略化された説明であり、さまざまな呼び出し規則とターゲットプラットフォームが重要な役割を果たしますが、例で「裏で」何が起こっているかを説明するには十分な情報であるはずです。

アセンブラの基本を理解している場合は、さまざまなコンパイラの逆アセンブリを比較する価値があります。一部のコンパイラは、保護手段としてeaxレジスタをクリアしていることがあります。


11
コンパイラそれを行うかもしれません。未定義です。代わりに、プリンターカートリッジを使って頭を撃つこともできます。
オービットのライトネスレース

@LightnessRacesinOrbit undefinedは、予測不可能と同じではありません。つまり、「コンパイラーがXを実行する場合、標準に準拠します」から、「コンパイラーがXを実行する可能性がある」と論理的に従わない
Owen

@オーウェン:これを定義する標準的な文章を引用してください。
2013

2
@LightnessRacesinOrbitフォローしきれなくてごめんなさい。私が本当に言いたいのは、この回答で提供されたnoggin182などの内部の洞察はデバッグに非常に役立つと思います。プログラムが予期しない結果を生み出しているとき、多くの場合、それらがどこから来ているのか、あるいはコードのどこを見ればよいのかわからず、実装の詳細を理解することで正しい方向に導くことができます。
オーウェン

@オーウェン私は他に主張したことはありません
レース
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.