C ++コンパイラとリンカプロセス記述

詳細リンク
一部の人々は、C / C ++は、未解決の外部リンクまたは損失で複製外部simbolエラーメッセージのためのプログラム、(ので、このエラーメッセージの行を見つけることができません)(以下、C ++とする)書き込みます。または言語の一部は、なぜ(またはしない)設計など知りません。この記事を知ることは、いくつかの答えがあるかもしれません。
    まず、我々はプログラムを書くためにどのように見てください。あなたはIDE(Visual Studioの、Elicpse、デベロッパーのいくつかの種類を使用している場合は 、C ++ など、)、あなたはおそらく(多くの人が初心者IDEに反対された)プログラムが編成されている方法を見つけることができません。あなたはクリックメニューに「コンパイル」書いた後にIDEを使用するので、あなたが何が新しいの.cppと.hファイルのシリーズは、プロジェクト内にある、すべてが正常になります。しかし、実際には過去、プログラマはプログラムを書く場合ではありません。彼らは、最初のコード記述された同じテキストファイルの書き込みのように、エディタを開く必要があり、その後、下のコマンドラインでノック
    CCの1.cpp -o 1.oの
    CCの2.cpp -o 2.Oの
    CCの3.cpp -o 3。 O
C / C ++コンパイラのここでのcc代わっは、密接にCPPファイルをコンパイルし、その後、あなたは(例として、任意の人気のコンパイラを使用していないための私を許してください)-o出力を指定するファイル。このように、現在のディレクトリが表示されます。
    1.o 2.O 3.O
最後に、プログラマが入力する必要があります
    1.o 2.O 3.O -o a.outのリンクを
、最終的な実行可能ファイルa.outを生成します。さてIDEは、実際には、これと同じ手順に従いますが、すべてが自動化されています。
    私たちは、上記のプロセスを分析してみましょう、私たちが見つけることができるかを参照してください。
    まず、ソースコードがコンパイルされ、個々のcppのファイルに対して行われます。あなたは除外した場合、各コンパイルのために他のcppファイルは、(これはC ++のコード非常に間違った言葉遣いで書かれている)状況を含む、コンパイラは、現在、他のcppファイルに、CPPファイルにコンパイルされることをcppのファイルを知っています全く気付いていない存在。
    第二に、生成された各コンパイルのcppファイル、.oファイルの後、最終的に実行可能ファイルを生成するために、読んリンク(リンク)になることです。
    さて、これらの認識は持っていた後、私たちは組織化されているC / C ++プログラムを見てみましょう。
    
    あなたは、最初のいくつかの概念を知っている必要があります:
    コンパイラ:ソースコードをコンパイルするコンパイラは、テキストがターゲットファイルのプロセスの機械語形式にソースコードの形で存在して変換されます。
    コンパイル単位は:C ++の場合は、すべてのCPPファイルがコンパイル単位です。コンパイルプロセスの前にプレゼンテーションからわかるように、各ユニット間のコンパイラでは、お互いに知られていません。
    オブジェクトファイル:コンパイル単位内でマシンコードの形式で、コンパイラによって生成されたファイルには、すべてのコードとデータ、およびその他の情報が含まれています。
    
    ここでは、特定のコンパイルを見てください。私たちは、オブジェクトファイルを生成するために、直接、文法解析をスキップします。我々は、ファイル1.cppを有すると仮定
     INTを1 = N-;

    (Fを無効)
     {
        ++ N-;
    }

    その1.oコンパイルされたオブジェクトファイルは、領域(バイナリセグメント名を想定)を有するであろう、それはより多くを含んでいますN、F、ファイルの形で与えられる含むデータ/機能は、おそらくオフセットされる:
    コンテンツ長をオフセット
    0x000のN 4
    0x004 F ??
    注:これはただの推測では、ターゲット・ファイルの実際のレイアウトを表すものではありません。各データオブジェクトファイルはもちろん、最初から必ずしも0x000のない、そのためには必ずしも、必ずしも連続的ではありません。
    今、私たちは、コンテンツ0x004から始まるf関数を見て(0x86でプラットフォームを推測):
    0x004 INC DWORD PTR [$ 000]。
    $ 00のRETの?
    注N ++は次のように翻訳されています:このユニットには、株式会社DWORD PTR [$ 000]、つまり$ 000位置のDWORD(4バイト)+1。
    
    別の以下2.cpp、存在する場合
    にextern INT N-、
    空隙Gを()
    {
        ++ N-であり;
    }
    次に、セグメントバイナリオブジェクトファイルは、2.Oはなければならない
    コンテンツの長さにオフセット
    0x000のG?
    なぜショーnは、内部の別のコンパイル単位で定義されるようにされているスペースは、Nがない(Nの定義である)、nはexternと宣言されているため、。で[???]他のコンパイル単位の状況を知ることは不可能である時間をコンパイルすることを忘れないでください、そうコンパイラは正確にここで、nを知らないので、INC DWORD PTRを埋めるために、この時間は、バイナリコードグラムの手立て?セクション。どのようにそれを行うには?この作品は、リンカに対処するために、後にハンドオーバすることができます。リンカはアドレスの一部が完了していない知っているようにするためには、ターゲット・ファイルも未解決のシンボルテーブルで、「未解決のシンボルテーブル」を持っている。また、それは、1.oである(nは対象のファイルの定義を提供します)また、彼らが提供できるアドレスリンカを伝えるために、「エクスポートシンボルテーブル」、エクスポートシンボルテーブルを提供しています。
    今、私たちは、それぞれのターゲットファイルは、独自のデータやバイナリコードに加えて、少なくとも2つのテーブルを提供しなければならないことを知っている:のヘアカットのアイデアを聞かせて、未解決のシンボルテーブルと、彼らが必要とするものをリンカに伝えるために、それぞれ、シンボルテーブルをエクスポートそして何を提供することができます。次の質問は2つのテーブル間の対応関係を確立する方法です。記号:ここでは新しい概念があります。C / C ++では、各変数および関数は、独自のシンボルを有します。例えば、変数nは、シンボル「N」です。サイン関数は一意の文字列を取得するなど、関数名とそのパラメータと呼び出し規則の組み合わせを必要とする、より複雑です。fは記号「_f」(コンパイラに依存して変化し得る)であってもよいです。
    だから、1.oエクスポートシンボルテーブルがある
    シンボリックアドレス
    のn-$ 000
    _f 0x004
    シンボルテーブルを解決せずに空である
    ため2.Oエクスポートしたシンボルテーブル
    のシンボリックアドレス
    _g $ 000
    のための未解決のシンボルテーブル
    のシンボリックアドレス    
    N-0x001    
    ???バイナリコードに格納されている[???]開始から出発し、本明細書に0x001 $ 000のINC DWORD PTRアドレス(ここでINCマシンコードを想定絶対アドレス+1に2~5バイトあなたは)マニュアルを確認することができ、正確な状況を知る必要があります。これは、アドレス値は不明であるが、符号nを有する、リンカーテーブル、0x001符号化部上に存在する位置のアドレスを伝えます。
    その後、リンク時、未解決のシンボルで2.Oで見つかったリンカnは、すべてのコンパイル単位を見つけ、我々は、エクスポートシンボルを見つけN 1.oにおいて、リンカーは、nに対応するために$ 000に入力されます上の2.Oの0x001位置。
    「一時停止は、」あなたはおそらく私を責め扇動でしょう。これが行われる場合、そうでないコンテンツとなるでG INC DWORD PTR [$ 000]、以前にではなく、対応する1.oの、$ 000細胞に加え、本の1 4バイトのアドレスであることは理解されるようにプラス1位。はい、各コンパイル・ユニットのアドレスはゼロベースであるため、これアドレスが繰り返されると共に、最終的なステッチ。したがって、リンカーは、縫合時に各部に対処するために調整されます。この例では、2.Oアドレス0x00000000のが0x00001000を実行ファイル上に配置され、そして0x00000000の0x00002000の1.oアドレスは、実行可能ファイル上に配置され、それが実際リンカーであり、1.o仮定しますシンボルテーブルが実際にエクスポートする
    シンボリックアドレス
    のn-$ 000 + 0x2000で
    _f 0x004 + 0x2000で
    シンボルテーブルを解決せずには空である
    ため2.Oエクスポートシンボルテーブル
    のシンボリックアドレスは
    $ 000 + 0x1000を_G
    のための未解決のシンボルテーブルを
    シンボリックアドレス            
    N 0x001 + 0x1000を
最終的なコードは、G INC DWORD PTR [$ 000 + 0x2000で]となります 。
    最後のアドレスは0x2000でNとなるため、最終的には、前のコードINCのDWORD PTR F [$ 000]はの間違っている、抜け穴があります。だから、ターゲットファイルは、このテーブル与える必要があり、アドレスリダイレクションテーブルアドレスリダイレクトテーブルと呼ばれます。
    1.oについては、そのリダイレクトテーブルへの
    対処
    0x005
    このテーブルには、指示し始め、その後、リンカーは、このテーブルを処理する際、署名する必要がある場所0x005をリダイレクトする必要が上のアドレスへのアドレスを見つけ、0x005はありませんその上に0x2000でプラス4バイト。
    私たちは、総括してみましょう:cppのコンパイラは、書き込まれるデータとオブジェクトファイルに含まれているCPPのコードに加えて、対象のファイルをコンパイルするには、だけでなく、少なくとも3つのテーブルを提供する:未解決のシンボルテーブル、シンボルテーブルとエクスポートアドレスリダイレクションテーブル。
    未解決のシンボルテーブルが参照されますが、コンパイル単位で表示され、そのコンパイル単位でこのシンボルで定義されていないすべてのアドレスを提供します。
    このテーブルは、定義された符号化ユニットとエクスポート・シンボルを提供し、コンパイル単位で使用されるアドレスと他のシンボルを提供する意思があります。
    アドレス・リダイレクションテーブルには、独自のアドレスは、このコンパイル単位に引用されたすべてのレコードを提供します。
    場合、リンカーリンク、最初の最終的な実行可能ファイル内の各ターゲット・ファイルの場所を決定します。アドレスレコード(すなわち、符号化部に加えて、実行可能ファイルの実際の開始アドレス)をリダイレクトするために、請求次いで、すべてのオブジェクト・ファイル・アドレス・リダイレクション・テーブルにアクセスします。その後、未解決のシンボルテーブルをファイルのすべてのオブジェクトを横断して、シンボルテーブルにすべての輸出に一致するシンボルを見つけるとに記録された場所未解決のシンボルテーブルの実際のアドレスを記入(もプラス記号の定義を所有しています実際のコンパイル単位)の実行可能ファイルのアドレスを開始します。最後に、その後、それぞれの立場で書かれたオブジェクトファイルのすべての内容、および他のいくつかの作業を行うには、実行可能ファイルを焼いています。
    最後のリンク1.oの2.Oを....得られた実行可能ファイルは、おそらく
    0x00000000の????(いくつかの他の情報)
    ...
    0x00001000 INC。DWORD PTR [0x00002000] //ここでは2.Oあります開始、Gが定義され
    INC Gがの終わりで5バイトであると仮定// 0x00001005のRET
    ...
    //これは1.oの始まりである0x00002000 0x00000001のを、N(初期化1)に定義される
    0x00002004株式会社DWORD PTR [0x00002000]は//これは、Fの始まりである
    株式会社をfの終わりで5バイトであると仮定// 0x00002009のRET
    ...
    ...
    実際のターゲットファイルのデータ/コードはいくつかのゾーンに分け、およびリダイレクトYaoanゾーンが、原理は同じであるため、実際のリンクは、より複雑であるとき。


    
    今、私たちは、古典的なリンクエラーのいくつかを見ることができます:
    未解決の外部リンク..
    これが見つかりました。リンカが未解決のシンボルであることは明らかですが、エクスポートシンボルテーブル内の対応するエントリを見つけられませんでした。
    どのような解決策はもちろん、ライン上のコンパイル単位にこのシンボルの定義を提供することです。されていないリンクされたファイルへのリンクがある場合、あなたも見ることができ、(それが機能することができ、このシンボルは、変数であることに注意してください)
    外部simbolsが...重複
    ので、このエクスポートされたシンボルテーブルは、重複を紹介するが、リンカは1つが使用されるべきかを決定することはできません。これは、重複した名前を使用することができ、他の理由があるかもしれません。


    :私たちは、提供されるプロパティの数のためのC / C ++言語を見て
    のextern:これは、シンボルが行くように、未解決のシンボルテーブルに置かれ、このシンボルは、他のコンパイル単位で定義されているコンパイラに指示します。(外部リンク)
    
    静的:グローバル関数や変数の宣言の前にキーワードの場合、コンパイル・ユニットは、このシンボル関数/変数をエクスポートしないことを示します。したがって、別のコンパイル単位で使用することができません。(内部リンク)。もし静的ローカル変数、変数のストレージおよびグローバル変数が、それでもシンボルをエクスポートしません。
    
    デフォルトのリンクプロパティ:関数と変数のために、金型はconstの変数のために、外部リンクを認識し、内部リンクをデフォルト。(リンク特性はEXTERNと静的を追加することによって変更することができる)

    外部リンクシンボル(シンボル由来ため)プログラム全体の範囲のために使用することができる:外部リンクの長所と短所が。しかし、また、他のコンパイル単位を尋ねた同じシンボルをエクスポートすることはできません(または外部simbolsを重複しています)

    内部リンクの長所と短所:内部リンクのシンボルは、他のコンパイル単位で使用することはできません。しかし、別のコンパイル単位は、同じ名前の内部のシンボリックリンクを持つことができます。

    なぜヘッダファイルは、一般的な定義を宣言することはできません:ヘッダファイルは、ヘッダファイルが定義されている場合、複数のコンパイル単位は、含有することができ、その後、それぞれが同一のシンボルにコンパイル単位のヘッダファイルがあれば、定義される含みます外部リンクのシンボルは、外部simbolsを複製した結果だろう。あなたは、ヘッダーファイルを定義したいのであれば、あなたはシンボル定義が唯一の内部リンクを持つことができることを確認する必要があります。

    デフォルトでは、なぜ一定の内部リンクではなく、可変である:
        これは、のconst intとしてできることであるN =このようなヘッダファイルに定義された定数0。定数の読み取り専用であるため、各コンパイル単位は重要ではありません定義されている場合でも。ヘッダファイル変数の定義は内部リンクを有しているので、複数のコンパイル単位この変数、変数は、同じ変数の他のセルに影響を与えることなく、修飾すべきでコンパイル単位を定義現れた場合、予期せぬ生成する場合結果。

    なぜデフォルトの関数外部リンクです:
        関数が読み取り専用であるが、しかし、変数は、コードを書く時の機能は、関数が内部リンクを持っている場合は、デフォルトを変更することは非常に簡単で、人々はその後、ヘッダファイルに定義を機能させる傾向があります機能が変更されると、すべてのヘッダファイルはコンパイル単位は、再コンパイルする必要がありますが含まれています。さらに、静的ローカル変数の定義においても、ヘッダファイルに定義されます。

    なぜ静的クラス変数現場初期化:初期化は、いわゆるin situでの場合と同様である:
        クラスA
        {
            静的チャーMSG [] =「AHA」;
        };
そうすることを許可された場合の理由は、通常、ヘッダファイルのクラス宣言ので、そうすることは許可されていない、実際には、ヘッダファイルに定義されている非const変数に相当します。

    C ++では、constオブジェクトヘッダファイルがどうなるかを定義:
        各ユニットは、オブジェクトのこのヘッダファイルに定義される編集が含まれて一般的には、ヘッダファイルでこの定義のconst int型で起こる、及びCありません。しかし、オブジェクトはconstのため、ほとんど効果があるため。しかし、このような状況を損なう可能性例2種類がありあり:
        1。アドレスCONSTオブジェクトをフェッチに関し、アドレスに依存している場合、一意性は、異なるコンパイル単位が取られ、そしてアドレスが異なっていてもよいです。(しかしめったに行いません)
        2。このオブジェクトは、それを編集する変数可変、コンパイル単位を持っている場合は、同じことが他のコンパイル単位には影響しません。

    なぜ静的定数クラス初期配置することはできません:
        それは、ヘッダファイルに定義されたconstオブジェクトと等価であるからです。など例外、int型/文字として、これらの変数は、即時、直接それとマクロ用に最適化することができますので、所定の位置に初期化することができます。

    インライン関数:
        C ++によるマクロに類似でインライン関数なので、プロパティの問題をリンクしないでください。

    公共の使用のインライン関数はヘッダファイルで定義する理由:
        私は、インライン関数が.cppファイルに定義されている場合、コンパイル時のコンパイル単位間でお互いを知っている別のコンパイル単位をコンパイルしていないので見つける方法がない場合は、この機能を使用していますしたがって、定義された関数、および拡張機能することができません。インライン関数は、.cppファイルで定義されているのであれば、その後、唯一のcppファイルには、この機能を使用することができます。

    ヘッダファイルに関連する機能の中で何が起こるか拒否されています。
        ヘッダファイルのインライン関数で定義されたが拒否された場合には、コンパイラが自動的にシンボルでコンパイルユニットが含まれているヘッダファイルのそれぞれの関数を定義し、エクスポートされません。

    インライン関数の静的ローカル変数の定義に拒否した場合、この変数はここに定義される:
        初期コンパイラはそれぞれにコンパイル単位を定義し、したがって、より新しいコンパイラ誤った結果を生みますこの問題を解決するため、未知の意味。

    なぜ誰もexportキーワードを実現していない:
        コンパイラは達成することは非常に困難であるように、エクスポートは、関数定義を見つけるために、ユニットをコンパイルするクロスコンパイラが必要です。

ます。https://www.cnblogs.com/hongfenglee/archive/2012/02/18/2356808.htmlで再現

おすすめ

転載: blog.csdn.net/weixin_34221276/article/details/94102410