6

CとC++の場合も、2つのファイルでの同じ関数とグローバル変数の異なる宣言に関して2つの質問があります。

  1. さまざまな関数宣言

    次のコードフラグメントを検討してください。

    file_1.c

    void foo(int a);
    
    int main(void)
    {
        foo('A');
    }
    

    file_2.c

    #include <stdio.h>
    
    void foo(char a)
    {
        printf("%c", a); //prints 'A' (gcc)
    }
    

    ご覧のとおり、プロトタイプは file_2.cにある定義とは異なりますが、関数は期待値を出力します。

    foo(int)C ++の場合、リンク時の参照が未定義であるため、上記のプログラムは無効です。これはおそらく、他の関数シグネチャの存在が原因です。Cと比較すると、関数名には関数の引数のタイプを示す余分な文字が含まれていません。

    しかし、Cになると、それでは何ですか?同じ名前のプロトタイプは、引数の数とそのタイプに関係なく同じシグネチャを持っているため、リンカはエラーを発行しません。しかし、ここではどの型変換が実行されますか?次のようになりますか? 'A'-> int->戻るchar?または、この動作は未定義/実装定義ですか?

  2. グローバル変数のさまざまな宣言

    同じグローバル変数の2つのファイルと2つの異なる宣言があります。

    file_1.c

    #include <stdio.h>
    
    extern int a;
    
    int main(void)
    {
        printf("%d", a); //prints 65 (g++ and gcc)
    }
    

    file_2.c

    char a = 'A';
    

    CとC++の両方で、出力は65です。

    両方の基準がそのような状況について何を言っているのか知りたいのですが。

    C11標準では、次のフラグメントが見つかりました。

    J.5.11複数の外部定義 (付録J.5共通の拡張機能)
    キーワードexternの明示的な使用の有無にかかわらず、オブジェクトの識別子には複数の外部定義が存在する場合があります。定義が一致しない場合、または複数が初期化されている場合、動作は未定義です(6.9.2)。

    これは2つ以上の定義の存在を示していることに注意してください。私のコードには、1つしかないため、この記事がこの場合の参考になるかどうかはわかりません...

4

4 に答える 4

5

Q1。C99仕様のセクション6.5.2.2.9によると、これはCでは未定義の動作です。

関数が、呼び出された関数を示す式が指す(式の)型と互換性のない型で定義されている場合、動作は未定義です。

式は、をとる関数を「指し示し」ますintが、関数はをとることとして定義されcharます。

Q2。変数の場合も未定義の動作です。これは、intから/への読み取りまたは割り当てを行っているためですchar。4バイトの整数を想定すると、これは有効なメモリ位置を超えて3バイトにアクセスします。次のように、さらに変数を宣言することで、これをテストできます。

char a = 'A';
char b = 'B';
char c = 'C';
char d = 'D';
于 2012-08-27T11:17:15.980 に答える
2

そのため、宣言をヘッダーに入れて、Cコンパイラーでも問題をキャッチできるようにします。

1)

この結果はかなりランダムです。あなたの場合、「char」パラメーターはintとして渡される可能性があります(レジスターのように、またはスタック上でアライメントを維持するためなど)。または、最下位バイトを最初に保持するエンディアンが原因で幸運に恵まれました。

2)

エンディアンとセグメントを埋めるために「0」バイトが追加されたため、幸運な結果になる可能性があります。繰り返しますが、それに依存しないでください。

于 2012-08-27T11:21:08.363 に答える
1

ご存知のとおり、私は誤って両方の問題をカバーするC11標準の段落を見つけました-それは6.2.7.2です:

同じオブジェクトまたは関数を参照するすべての宣言は、互換性のある型を持っている必要があります。それ以外の場合、動作は定義されていません。

于 2012-08-30T06:07:36.510 に答える
1

C ++のオーバーロードされた関数は、コンパイラがそれぞれの一意のメソッドとパラメータリストの組み合わせをリンカの一意の名前にエンコードするために機能します。このエンコードプロセスはマングリングと呼ばれ、逆プロセスはデマングリングと呼ばれます。

しかし、Cにはそのようなことはありません。コンパイラが現在のモジュールで定義されていないシンボル(変数または関数名)を検出すると、他のモジュールで定義されていると見なし、リンカーシンボルテーブルエントリを生成します。リンカが処理できるようにしておきます。ここでは、パラメータチェックはありません。

また、ここに型変換がない場合。主に、fooに値を送信します。これがアセンブリコードです:

movl    $65, (%esp)
call    foo

そして、fooはスタックからそれを取り除くことによってそれを読み取ります。これはcharとして定義された入力値であるため、入力値をalレジスタ(1バイト)に格納します。

movb    %al, -4(%ebp)

したがって、256を超える入力が与えられた場合a、fooに変数が表示され、256を循環します。

2番目の質問について、初期化された変数と関数のIn Cシンボルは強力なものとして定義され、複数の強力なシンボルは許可されていませんが、C++の場合かどうかはわかりません。

于 2012-08-27T12:09:35.580 に答える