2016-04-07 4 views
3

同じ構造体Aを使用する2つの翻訳単位がある場合は、通常、その構造体をヘッダーに配置します。 e。プリプロセッサに両方のTUに定義をペーストさせます。リンカーは、2つの構造体定義が等しくないことを無視できますか?

私たちはそれをしないとしましょう。異なる定義を使用することが許されていますか?どうして?

createA.c:

typedef struct { 
    int data; 
} A; 

A createA() { 
    A a = {10}; 
    return a; 
} 

のmain.c:

#include <stdio.h> 

typedef struct { 
    int value; 
    float x; 
} A; 

A createA(); 

int main(int argc, char *argv[]) 
{ 
    A a = createA(); 
    printf("value is %d, x is %f\n", a.value, a.x); 
    a.x = 3.1f; 
    printf("value is %d, x is %f\n", a.value, a.x); 
} 

VS 10 2つの構造体のサイズが異なっていても、喜んで一緒にこれをリンクするように見えます。つまり、createAへのコールは、2つのTU(スタックの変更が異なる)で異なって動作します。これは少し奇妙です。関数に引数を追加すると、実行時にプログラムがクラッシュしますが、リンカーエラーは発生しません。

これはなぜ興味深いのですか?私は、impl* impl_dataの代わりにunsigned char impl_data[impl_size]のようなものをパブリックヘッダに使用し、インプリメンテーションファイルの実際のインプストラクチャにキャストするPimplのバリアントにこのメカニズムを使用することができないのかどうか疑問に思っていました。実践的な考慮事項(例えば、impl_sizeの決定など)は許可されていますか?

+0

構造体の定義が複数あることは、コードのコピー貼り付けをしたい人のための壮大なデバッグセッションです。 1つの場所が、別の場所はありません)。すべてのコンパイラの安全チェックをバイパスし、リンカに依存します。それらは私の孫に伝えたい私のお気に入りのバグです。 – Drop

+0

@Drop Yep、[楽しいように聞こえる](https://stackoverflow.com/questions/4339697/type-checking-across-source-files) –

答えて

4

リンカーはどのように構造体が定義されているのか分かりません。あるいは、構造体が存在することさえあります。リンカが気にするのは、外部リンケージで名前を解決することです。構造体はコンパイル時のアーチファクトです。

リンカは関数プロトタイプについても知らない。したがって、ある翻訳単位で呼び出される関数への引数が、実際には、別の翻訳単位にコンパイルされた関数によって期待される引数に実際に対応するかどうかは分かりません。リンカーが行うことは、呼び出しがその名前の関数を呼び出すことを確認することです。

したがって、リンカは確かにいくつかのエラーを無視することができます。しかし、それは誤りを正しいものにしません。彼らはまだ誤りです。あなたのプログラムが正しいこと、およびある翻訳単位の構造定義とプロトタイプが別の翻訳単位のものと互換性があることを確認することは、プログラマーとしての責任です。 Cはあなたのガーディアンの天使として行動しません。

これは完全にユーザーフレンドリーではないように見えるかもしれませんが、多くの点でそうではありません。そのようなエラーを検出するように設計された言語があります。しかしCはその哲学を持っていません。

struct s { char _[sizeof_struct_s]; };を1つの翻訳単位に、実際のstruct sを別のものに使用した具体例について、結果は未定義の動作であり、エラーメッセージは必要ありません。したがって、それは準拠ポータブルコードで使用すべきではありません。それにもかかわらず、文字配列の適切な配置が保証され、サイズを正確に計算することができれば(それは大きな保守性の頭痛になります)、おそらく多くのアーキテクチャで動作します。もちろん、保証はありません、そして、それが壊れば、あなたはその部分を保つようになります。

+0

タイトルの編集もありがとうございます。 "それが壊れば、あなたはその作品を保つようになる"私はリリースノートのためにそれを保つだろう。 –

1

2つの翻訳単位が同じ構造体型の異なる宣言を使用する場合は、それは問題ありませんが、それほど一般的ではありません。

異なるシンボルが異なる2つの変換単位で定義されている場合(異なるシグネチャタイプが異なるシグネチャと異なるシグネチャの場合)、動作は未定義です。

プログラムの2つの部分は、同じ構造体型の異なる宣言を持つことができますが、2つの定義が作成することなく決して互いと接触できないので、この矛盾を見つける方法はありません未定義の動作。

あなたの質問にお答えしますか?

+0

2番目の段落を少し言い換えるか、例を挙げることができますか?私はそれを少し難しいと思う。 – skrrgwasme

+0

私はそれが質問に答えると思うが、「違う」とはどういう意味なのかを説明できるのであれば、私は感謝するだろう。または定義がある場合。標準が "未定義の振る舞い"という簡単な方法を取ったように思えます。これは、リンカが矛盾を無視して黙って許可することを意味します。 –

+0

@AndreasHaferburgどのようにリンカーに伝えることができますか?構造体を返すことは、期待していたものとは違って、 'strcpy()'に小さすぎるバッファを渡すことと同じです。リンカーは、異なるコンパイル単位での異なる解釈から保護する情報がありません。 –

1

リンカは実際にはここには含まれていません。ある翻訳単位で定義され、別の言語から呼び出された関数f()があり、2つの翻訳単位で異なる型で宣言されている場合は、(a)かなり重大な問題であり、(b)従来のリンカーここで「伝統的」とは、Cが設計された比較的単純なものを意味する)を検出することができます。関数が定義されている構造体の型と呼び出された場所の宣言が異なることは、実際には1つの変換単位でdoubleを返す関数を定義することとは異なりますが、別の宣言でintを返すように宣言して呼び出します。 (私の指摘は、そこのリンカから苦情を受け取る傾向がないということです。)

関連する問題