このC関数は常にfalseを返す必要がありますが、そうではありません

319
Dimitri Podborski 2016-04-08 02:27.

ずっと前にフォーラムで面白い質問に出くわしましたが、その答えを知りたいです。

次のC関数について考えてみます。

f1.c

#include <stdbool.h>

bool f1()
{
    int var1 = 1000;
    int var2 = 2000;
    int var3 = var1 + var2;
    return (var3 == 0) ? true : false;
}

false以来、これは常に戻るはずvar3 == 3000です。mainこの関数は次のようになります。

main.c

#include <stdio.h>
#include <stdbool.h>

int main()
{
    printf( f1() == true ? "true\n" : "false\n");
    if( f1() )
    {
        printf("executed\n");
    }
    return 0;
}

以来f1()、常に返す必要がありfalse、1プログラムが一つだけ印刷するように期待される偽の画面へ。ただし、コンパイルして実行すると、実行済みも表示されます。

$ gcc main.c f1.c -o test
$ ./test
false
executed

何故ですか?このコードには、ある種の未定義の動作がありますか?

注:私はそれをでコンパイルしましたgcc (Ubuntu 4.9.2-10ubuntu13) 4.9.2

4 answers

405
Lundin 2016-04-08 03:09.

他の回答で述べたように、問題は、gccコンパイラオプションを設定せずに使用することです。これを行うと、デフォルトで「gnu90」と呼ばれるものになります。これは、1990年から廃止された古いC90標準の非標準実装です。

古いC90標準では、C言語に大きな欠陥がありました。関数を使用する前にプロトタイプを宣言しなかった場合、デフォルトでint func ()( )「任意のパラメーターを受け入れる」を意味します)になります。これにより、関数の呼び出し規約はfunc変更されますが、実際の関数定義は変更されません。大き以来boolint異なっている関数が呼び出されたときに、あなたのコードを呼び出すには、動作が未定義。

この危険なナンセンスな動作は、1999年にC99標準のリリースで修正されました。暗黙の関数宣言は禁止されました。

残念ながら、バージョン5.xxまでのGCCは、デフォルトで古いC標準を使用します。コードを標準C以外のものとしてコンパイルする必要がある理由はおそらくないでしょう。したがって、25年以上前の非標準のGNUがらくたではなく、最新のCコードとしてコードをコンパイルする必要があることをGCCに明示的に指示する必要があります。 。

プログラムを常に次のようにコンパイルして、問題を修正します。

gcc -std=c11 -pedantic-errors -Wall -Wextra
  • -std=c11 (現在の)C標準(非公式にはC11として知られている)に従ってコンパイルするために中途半端な試みをするように指示します。
  • -pedantic-errors 上記を心を込めて実行し、C標準に違反する誤ったコードを記述した場合にコンパイラエラーを発生させるように指示します。
  • -Wall あるとよいかもしれないいくつかの追加の警告を私に与えることを意味します。
  • -Wextra あるとよいかもしれない他のいくつかの追加の警告を私に与えることを意味します。
143
dbush 2016-04-08 02:34.

f1()main.cでプロトタイプが宣言されていないため、暗黙的にとして定義されint f1()ています。つまり、不明な数の引数を取り、を返す関数ですint

場合intbool異なるサイズのものであり、これはになります未定義の動作。たとえば、私のマシンでintは、は4バイトで、boolは1バイトです。関数はを返すように定義されているため、戻るboolときに1バイトをスタックに置きます。ただし、main.cから戻るように暗黙的に宣言されているためint、呼び出し元の関数はスタックから4バイトを読み取ろうとします。

gccのデフォルトのコンパイラオプションは、これを実行していることを通知しません。しかし、でコンパイルすると-Wall -Wextra、次のようになります。

main.c: In function ‘main’:
main.c:6: warning: implicit declaration of function ‘f1’

これを修正するにはf1、main.cの前にの宣言を追加しますmain

bool f1(void);

引数リストが明示的にに設定されていることに注意してくださいvoid。これは、引数の数が不明であることを意味する空のパラメーターリストとは対照的に、関数が引数を受け取らないことをコンパイラーに通知します。f1これを反映するために、f1.cの定義も変更する必要があります。

37
Owen 2016-04-09 23:24.

Lundinの優れた回答で言及されているサイズの不一致が実際にどこで発生するかを見るのは興味深いと思います。

でコンパイルすると--save-temps、見ることができるアセンブリファイルが得られます。ここでは一部だf1()== 0、比較し、その値を返しますが:

cmpl    $0, -4(%rbp)
sete    %al

戻り部分はsete %alです。Cのx86の呼び出し規約では、リターン(含む4バイト以下の値intとはbool)レジスタを介して戻されます%eax%alの最下位バイトです%eax。したがって、の上位3バイトは%eax制御されていない状態のままになります。

main()

call    f1
testl   %eax, %eax
je  .L2

どうか、このチェック全体のは%eax、それがint型のテストだと考えているので、ゼロです。

明示的な関数宣言を追加すると、次のように変更さmain()れます。

call    f1
testb   %al, %al
je  .L2

それが私たちが望むものです。

27
jdarthenay 2016-04-08 02:37.

次のようなコマンドでコンパイルしてください。

gcc -Wall -Wextra -Werror -std=gnu99 -o main.exe main.c

出力:

main.c: In function 'main':
main.c:14:5: error: implicit declaration of function 'f1' [-Werror=impl
icit-function-declaration]
     printf( f1() == true ? "true\n" : "false\n");
     ^
cc1.exe: all warnings being treated as errors

そのようなメッセージで、あなたはそれを修正するために何をすべきかを知っているべきです。

編集:(現在削除されている)コメントを読んだ後、フラグなしでコードをコンパイルしようとしました。さて、これにより、コンパイラエラーの代わりにコンパイラ警告のないリンカーエラーが発生しました。そして、それらのリンカーエラーは理解するのがより難しいので、-std-gnu99必要でなくても、少なくとも-Wall -Werrorそれはあなたのお尻の多くの痛みを救うので、常に使用するようにしてください。

Related questions

MORE COOL STUFF

ケイト・ブランシェットは3日間一緒に夫と一緒に寝て、25年経ってもまだ夫と結婚しています

ケイト・ブランシェットは3日間一緒に夫と一緒に寝て、25年経ってもまだ夫と結婚しています

ケイト・ブランシェットは、夫に会ったとき、典型的な交際のアドバイスに逆らいました。

マイケルシーンが非営利の俳優である理由

マイケルシーンが非営利の俳優である理由

マイケルシーンは非営利の俳優ですが、それは正確にはどういう意味ですか?

ホールマークスターのコリンエッグレスフィールドがRomaDramaLiveでスリル満点のファンと出会う![エクスクルーシブ]

ホールマークスターのコリンエッグレスフィールドがRomaDramaLiveでスリル満点のファンと出会う![エクスクルーシブ]

特徴的なスターのコリン・エッグレスフィールドは、RomaDrama Liveでのスリル満点のファンとの出会いについて料理しました!加えて、大会での彼のINSPIREプログラム。

「たどりつけば」をオンラインでストリーミングできない理由

「たどりつけば」をオンラインでストリーミングできない理由

ノーザンエクスポージャーが90年代の最も人気のある番組の1つになった理由を確認するには、Blu-rayまたはDVDプレーヤーをほこりで払う必要があります。

バイオニック読書はあなたをより速く読むことができますか?

バイオニック読書はあなたをより速く読むことができますか?

BionicReadingアプリの人気が爆発的に高まっています。しかし、それは本当にあなたを速読術にすることができますか?

ドミニカのボイリング湖:アクセスは簡単ではありませんが、ハイキングする価値があります

ドミニカのボイリング湖:アクセスは簡単ではありませんが、ハイキングする価値があります

ドミニカのボイリング湖は、世界で2番目に大きいボイリング湖です。そこにたどり着くまでのトレッキングは大変で長いですが、努力する価値は十分にあります。

私たちの水をきれいに保つのを助けるためにあなたの髪を寄付してください

私たちの水をきれいに保つのを助けるためにあなたの髪を寄付してください

サロンからのヘアトリミングや個人的な寄付は、油流出を吸収して環境を保護するのに役立つマットとして再利用できます。

ホワイトハウスの最も記憶に残る結婚式を見てください

ホワイトハウスの最も記憶に残る結婚式を見てください

過去200年以上の間にホワイトハウスで結婚したのはほんの数人です。彼らは誰でしたか、そしてそこで結婚式を獲得するために何が必要ですか?

Netflixのジョエルマクヘイルとのジョエルマクヘイルショーは、ジョエルマクヘイルにぴったりの車を復活させます

Netflixのジョエルマクヘイルとのジョエルマクヘイルショーは、ジョエルマクヘイルにぴったりの車を復活させます

ジョエル・マクヘイル、マイク・コルター(スクリーンショット:Netflix)「私の神よ、これは1つのことを変えます。」これは、ジョエル・マクヘイルとのジョエル・マクヘイルショーの最後のジョークです。リアリティ番組の嘲笑と寛大なスナキネスの時間は、なじみのある顔を見つけます。

チームロケットは20年ぶりにポケモンシリーズでアッシュを破った

チームロケットは20年ぶりにポケモンシリーズでアッシュを破った

画像経由:@pancakeparadox(Twitter)。1997年にポケモンシリーズが初公開されて以来、チームロケット(またはラテンアメリカではチームロケット)として知られる悪役のグループは、何度もアッシュに直面してきました。

今週の科学技術でトランプがめちゃくちゃになったことすべて

今週の科学技術でトランプがめちゃくちゃになったことすべて

画像:ゲッティ私たち全員が千年もの間生きていて、私たちの体が燃える風によってほこりと長引く悲鳴だけに押し流されたと考えるのは驚くべきことです。私たちがそうしていないことを除いて、それはトランプ政権の最初の週の終わりであり、驚くほど多くの恐ろしいことがすでに起こっています。

あなたの「マイクロピッグ」が代わりに通常のピッグになってしまったとしても驚かないでください

あなたの「マイクロピッグ」が代わりに通常のピッグになってしまったとしても驚かないでください

そして今、あることを手に入れていると思っていたが、まったく別のことをしてしまった男の話。CBSニュースは、彼女が「ミニブタ」であるという誤ったふりをしてエスターを養子にしたカナダ人のスティーブジェンキンスの心温まる物語をもたらします。これは、特にせいぜいゴールデンレトリバーまたはセントバーナードをストラップします。

Zendaya Wishes Boyfriend Tom Holland Happy Birthday with Cuddly Photo: He 'Makes Me the Happiest'

Zendaya Wishes Boyfriend Tom Holland Happy Birthday with Cuddly Photo: He 'Makes Me the Happiest'

Zendaya shared a sweet photo in honor of boyfriend Tom Holland's 26th birthday Wednesday

小さな女性:脳卒中を患った後に病院から解放されたアトランタのジューシーな赤ちゃん:「まだ癒し」

小さな女性:脳卒中を患った後に病院から解放されたアトランタのジューシーな赤ちゃん:「まだ癒し」

シーレン「Ms.JuicyBaby」ピアソンは、先月脳卒中で入院した後、「もう一度たくさんのことをする方法を学ばなければならない」ため、言語療法を受けていることを明らかにしました。

エマストーンは彼女のクリフサイドマリブビーチハウスを420万ドルでリストアップしています—中を見てください!

エマストーンは彼女のクリフサイドマリブビーチハウスを420万ドルでリストアップしています—中を見てください!

オスカー受賞者の世紀半ばの家には、3つのベッドルーム、2つのバス、オーシャンフロントの景色があります。

ジーニー・メイ・ジェンキンスは、母乳育児の経験の中で、彼女は「本当に、本当に落ち込んでいる」と言います

ジーニー・メイ・ジェンキンスは、母乳育児の経験の中で、彼女は「本当に、本当に落ち込んでいる」と言います

ジーニー・メイ・ジェンキンスは、生後4か月の娘、モナコに母乳育児をしていると語った。

投資ノート:Bioscout AU$300万シード

投資ノート:Bioscout AU$300万シード

Bioscoutは、農家を運転席に置くという使命を負っています。Artesian(GrainInnovate)やUniseedと並んで、最新のシードラウンドでチームを支援できることをうれしく思います。問題真菌症による重大な作物の損失は、農民にとって試練であることが証明されています。

リトルマーケットリサーチ1| 2022年のクイックグリンプス遠隔医療市場

リトルマーケットリサーチ1| 2022年のクイックグリンプス遠隔医療市場

遠隔医療は、パンデミック後の時代では新しいものではなく、時代遅れの分野でもありません。しかし、業界を詳しく見ると、需要と供給の強力な持続可能性と、米国で絶え間ない革命となる強力な潜在的成長曲線を示しています。

スタートアップ資金調達環境:タイのスタートアップエコシステムの次は何ですか?

スタートアップ資金調達環境:タイのスタートアップエコシステムの次は何ですか?

2021年は、世界的なベンチャーキャピタル(VC)の資金調達にとって記録的な年でした。DealStreetAsiaによると、東南アジアも例外ではなく、この地域では年間で記録的な25の新しいユニコーンが採掘されました。

ムーアの法則を超えて

ムーアの法則を超えて

計算に対する私たちの欲求とムーアの法則が提供できるものとの間には、指数関数的に増大するギャップがあります。私たちの文明は計算に基づいています—建築と想像力の現在の限界を超える技術を見つけなければなりません。

Language