サーチ…
前書き
C言語は、伝統的にコンパイルされた言語です(解釈するのではなく)。 C標準は翻訳フェーズを定義し、それを適用する製品はプログラムイメージ(またはコンパイルされたプログラム)です。 c11では、フェーズが5.1.1.2にリストされています。
備考
ファイル名の拡張子 | 説明 |
---|---|
.c | ソースファイル。通常、定義とコードが含まれています。 |
.h | ヘッダーファイル。通常、宣言が含まれます。 |
.o | オブジェクトファイル。機械語でコンパイルされたコード。 |
.obj | オブジェクトファイルの代替拡張。 |
.a | ライブラリファイル。オブジェクトファイルのパッケージ。 |
.dll | Windows上のダイナミックリンクライブラリ |
.so | 多くのUnixライクなシステム上の共有オブジェクト(ライブラリ)。 |
.dylib | OSX上のダイナミックリンクライブラリ(Unix版) |
.exe 、 .com | Windows実行ファイル。オブジェクトファイルとライブラリファイルをリンクすることで形成されます。 Unixライクなシステムでは、実行可能ファイル用の特別なファイル名拡張子はありません。 |
POSIX c99コンパイラフラグ | 説明 |
---|---|
-o filename | 出力ファイル名。 ( bin/program.exe 、 program ) |
-I directory | direrctory ヘッダーを検索します。 |
-D name | マクロname 定義する |
-L directory | directory 内のライブラリを検索します。 |
-l name | リンクライブラリlibname 。 |
POSIXプラットフォーム(Linux、メインフレーム、Mac)上のコンパイラは、 c99
と呼ばれていなくても、通常これらのオプションを受け入れます。
GCC(GNUコンパイラコレクション)フラグ | 説明 |
---|---|
-Wall | 一般に受け入れられているすべての警告メッセージを有効にします。 |
-Wextra | より多くの警告メッセージを有効にし、あまりにも騒々しいことがあります。 |
-pedantic | コードが選択された標準に違反している場合に警告を出す。 |
-Wconversion | 暗黙の変換で警告を有効にしてください。注意して使用してください。 |
-c | リンクせずにソースファイルをコンパイルします。 |
-v | コンパイル情報を出力します。 |
-
gcc
はPOSIXフラグに加えて多くのものを受け入れます。 - POSIXプラットフォーム(
clang
、ベンダ固有のコンパイラ)上の他の多くのコンパイラでも、上記のフラグが使用されます。 - さらに多くのオプションについては、 GCCの呼び出しを参照してください。
TCC(Tiny Cコンパイラ)フラグ | 説明 |
---|---|
-Wimplicit-function-declaration | 暗黙の関数宣言について警告します。 |
-Wunsupported | TCCによって無視されるサポートされていないGCC機能について警告します。 |
-Wwrite-strings | 文字列定数をchar *の代わりにconst char *型にする。 |
-Werror | 警告が出された場合は、コンパイルを中止してください。 |
-Wall | -Werror 、 -Wunusupported 、および-Wwrite strings 以外のすべての警告を-Wwrite strings 。 |
リンカー
リンカの仕事は、複数のオブジェクトファイル( .o
ファイル)をバイナリ実行可能ファイルにリンクすることです。 リンクのプロセスは、主に記号アドレスを数値アドレスに解決することを含む。リンク処理の結果は通常、実行可能プログラムです。
(オブジェクトファイルをリンク処理中に、リンカは、コマンドラインで指定されたすべてのオブジェクトモジュールをピックアップする前に、いくつかのシステム固有のスタートアップコードを追加して、他のオブジェクトファイル内の外部定義を持つオブジェクトモジュール内のすべての外部参照を解決しようとしますコマンドラインで直接指定することも、ライブラリから暗黙的に追加することもできます)。次に、オブジェクトファイルのロードアドレスを割り当てます。つまり、コードとデータが完成したプログラムのアドレス空間にどこに格納されるのかを指定します。ロード・アドレスを取得すると、オブジェクト・コード内のすべてのシンボリック・アドレスを、ターゲットのアドレス空間内の「実際の」数値アドレスに置き換えることができます。プログラムはすぐに実行可能です。
これには、コンパイラがソースコードファイルから作成したオブジェクトファイルと、事前にコンパイルしてライブラリファイルに収集したオブジェクトファイルの両方が含まれます。これらのファイルの名前は.a
または.so
で終わり、リンカはほとんどの場所がわかっていて、必要に応じて自動的にリンクするため、通常はそれらについて知る必要はありません。
リンカーの暗黙的な呼び出し
プリプロセッサと同様に、リンカは別のプログラムで、しばしばld
と呼ばれます(ただし、Linuxはcollect2
使用します)。また、プリプロセッサのように、コンパイラを使用するときに自動的にリンカが呼び出されます。したがって、リンカーを使用する通常の方法は次のとおりです。
% gcc foo.o bar.o baz.o -o myprog
この行は、3つのオブジェクトファイル(一緒にリンクするためのコンパイラ指示しfoo.o
、 bar.o
、そしてbaz.o
バイナリ実行ファイルの名前に) myprog
。これでmyprog
と呼ばれるファイルを実行することができ、うまくいけばクールで便利なmyprog
ができます。
明示的なリンカの呼び出し
リンカーを直接呼び出すことは可能ですが、これはお勧めできませんが、通常はプラットフォーム固有のものです。つまり、Linuxで動作するオプションは、Solaris、AIX、macOS、Windowsなどのプラットフォームでは機能しません。 GCCを使って作業する場合は、 gcc -v
を使用して、あなたのために実行されているものを見ることができます。
リンカのオプション
リンカは、その動作を変更するためにいくつかの引数もとります。次のコマンドはgccにfoo.o
とbar.o
をリンクするよう指示しますが、 ncurses
ライブラリも含みます。
% gcc foo.o bar.o -o foo -lncurses
これは実際には(多かれ少なかれ)同等です
% gcc foo.o bar.o /usr/lib/libncurses.so -o foo
(が、 libncurses.so
可能性がありlibncurses.a
で作成したばかりのアーカイブで、 ar
)。オブジェクトファイルの-lname
、ライブラリを(パス名または-lname
オプションのどちらかで)リストする必要があることに注意してください。静的ライブラリでは、指定された順序が重要です。共有ライブラリでは、順序は関係ありません。
多くのシステムで、数学関数( <math.h>
から)を使用している場合、数学ライブラリを読み込むには-lm
を指定する必要がありますが、Mac OS XとmacOS Sierraではこれを必要としません。 Linuxや他のUnixシステムでは別のライブラリがありますが、macOSではないライブラリがあります - POSIXスレッドとPOSIXリアルタイムとネットワーキングライブラリは例です。その結果、リンクプロセスはプラットフォームによって異なります。
その他のコンパイルオプション
これはあなた自身のCプログラムのコンパイルを開始するために知る必要があることです。一般に、 -Wall
コマンドラインオプションを使用することをお勧めします。
% gcc -Wall -c foo.cc
-Wall
オプションを指定すると、コンパイラは法的で疑わしいコード構造について警告し、非常に早期に多くのバグを見つけられるようになります。
コンパイラが(宣言されているが使われていない変数や値を返すことを忘れるなどの)変数を含むコンパイラに警告を投げたい場合は、 -Wall
という名前にもかかわらず、このオプションのセットを使うことができます。 すべての可能性のある警告について:
% gcc -Wall -Wextra -Wfloat-equal -Wundef -Wcast-align -Wwrite-strings -Wlogical-op \
> -Wmissing-declarations -Wredundant-decls -Wshadow …
その注意clang
オプションであり-Weverything
本当に内のすべての警告をオンにしないclang
。
ファイルタイプ
Cプログラムをコンパイルするには、5種類のファイルを扱う必要があります。
ソースファイル :これらのファイルには関数定義が含まれており、慣例により
.c
で終わる名前が付けられています。注:.cc
と.cpp
はC ++ファイルです。 Cファイルではありません 。
例:foo.c
ヘッダーファイル :これらのファイルには、関数プロトタイプとさまざまなプリプロセッサステートメントが含まれています(下記参照)。これらは、ソースコードファイルが外部で定義された関数にアクセスできるようにするために使用されます。ヘッダーファイルは慣例により
.h
終わります。
例:foo.h
オブジェクトファイル :これらのファイルは、コンパイラの出力として生成されます。バイナリ形式の関数定義で構成されていますが、それらは単独では実行できません。オブジェクトファイルは、いくつかのオペレーティングシステム(Windows、MS-DOSなど)では
.obj
終わることがありますが、通常は.o
終わります。
例:foo.o
foo.obj
バイナリ実行ファイル :これらは "リンカ"と呼ばれるプログラムの出力として生成されます。リンカーは、複数のオブジェクトファイルをリンクして、直接実行可能なバイナリファイルを生成します。バイナリ実行ファイルには、UNIXオペレーティングシステムでは特別な接尾辞はありませんが、一般にWindowsでは
.exe
で終わります。
例:foo
foo.exe
ライブラリ :ライブラリはコンパイルされたバイナリですが、それ自体が実行可能ファイルではありません(つまり、ライブラリに
main()
関数がありません)。ライブラリには、複数のプログラムで使用できる関数が含まれています。ライブラリには、ライブラリ内のすべての関数のプロトタイプを含むヘッダファイルが付属していなければなりません。これらのヘッダファイルは、ライブラリを使用するソースファイル内で参照されるべきです(例えば、#include <library.h>
)。プログラムを正常にコンパイルできるようにリンカーをライブラリに参照する必要があります。ライブラリには、静的と動的の2種類があります。- 静的ライブラリ :スタティックライブラリ(
.lib
拡張子を使用するDLLインポートライブラリファイルと混同しないように、POSIXシステム用の.a
ファイルとWindows用の.lib
ファイル)は、プログラムに静的に組み込まれています。静的ライブラリーには、ライブラリーのどのバージョンが使用されているか正確にプログラムが知っているという利点があります。一方、使用されるすべてのライブラリ関数が含まれるため、実行可能ファイルのサイズは大きくなります。
例:libfoo.a
foo.lib
- 動的ライブラリ :動的ライブラリ(ほとんどのPOSIXシステムの
.so
ファイル、OSXの.dylib
、Windowsの.dll
ファイル)は、プログラムによって実行時に動的にリンクされます。 1つのライブラリイメージを多くのプログラムで共有できるため、これらは共有ライブラリと呼ばれることもあります。ダイナミックライブラリには、複数のアプリケーションがライブラリを使用している場合に必要なディスク領域が少なくて済みます。また、実行可能ファイルを再構築することなく、ライブラリの更新(バグ修正)が可能です。
例:foo.so
foo.dylib
foo.dll
- 静的ライブラリ :スタティックライブラリ(
プリプロセッサ
Cコンパイラがソースコードファイルのコンパイルを開始する前に、ファイルは前処理フェーズで処理されます。このフェーズは、別のプログラムで行うことも、1つの実行可能ファイルに完全に統合することもできます。いずれにしても、コンパイルが正しく開始される前に、コンパイラによって自動的に呼び出されます。前処理段階では、テキスト置換を適用して、ソースコードを別のソースコードまたは翻訳単位に変換します。あなたはそれを「変更された」または「拡張された」ソースコードと考えることができます。その拡張されたソースは、ファイルシステム内に実際のファイルとして存在することもありますし、メモリに短時間だけ保存してからさらに処理することもできます。
プリプロセッサのコマンドはシャープ記号( "#")で始まります。いくつかのプリプロセッサコマンドがあります。最も重要なのは2つです。
定義 :
#define
は主に定数の定義に使用されます。例えば、#define BIGNUM 1000000 int a = BIGNUM;
〜になる
int a = 1000000;
このように
#define
を使用すると、ソースコードファイルのさまざまな場所に一定の値を明示的に書き出す必要がなくなります。後で定数値を変更する必要がある場合には、これは重要です。#define
では、コード全体に散らばっている複数の場所で変更する必要があります。#define
は高度な検索と置換だけを行うため、マクロを宣言することもできます。例えば:#define ISTRUE(stm) do{stm = stm ? 1 : 0;}while(0) // in the function: a = x; ISTRUE(a);
次のようになる:
// in the function: a = x; do { a = a ? 1 : 0; } while(0);
最初の近似では、この効果はインライン関数とほぼ同じですが、プリプロセッサは
#define
マクロの型チェックを行いません。これはエラーが起こりやすいことがよく知られており、その使用には大きな注意が必要です。また、プリプロセッサは、後述のようにコメントを空白に置き換えることに注意してください。
含まれるもの :
#include
は、ソースコードファイルの外で定義された関数定義にアクセスするために使用されます。例えば:#include <stdio.h>
プリプロセッサは、
<stdio.h>
内容をコンパイルする前に、#include
文の場所にあるソースコードファイルに貼り付けます。#include
はほとんどの場合、ヘッダファイルをインクルードするために使用されます。ヘッダファイルは、主に関数宣言と#define
文を含むファイルです。この場合、#include
を使用してprintf
やscanf
などの関数を使用できるようにします。この関数の宣言はstdio.h
ファイルにあります。 Cコンパイラでは、以前にそのファイルで宣言または定義されていない限り、関数を使用することはできません。#include
文は、Cプログラムで以前に書かれたコードを再利用する方法です。論理演算 :
#if defined A || defined B variable = another_variable + 1; #else variable = another_variable * 2; #endif
変更されます:
variable = another_variable + 1;
以前にプロジェクトのどこかにAまたはBが定義されていた場合。これが当てはまらない場合、もちろんプリプロセッサはこれを行います:
variable = another_variable * 2;
これは、異なるシステム上で実行されるコードや異なるコンパイラでコンパイルされるコードでよく使用されます。コンパイラ/システム固有のグローバルな定義があるので、それらの定義をテストして、コンパイラが必ずコンパイルするコードを使用させることができます。
コメント
プリプロセッサは、ソースファイル内のすべてのコメントを単一のスペースで置き換えます。コメントは、
//
行の最後まで、またはopening/*
とclosing*/
コメント括弧の組み合わせによって示されます。
コンパイラ
Cプリプロセッサがすべてのヘッダファイルをインクルードし、すべてのマクロを展開した後、コンパイラはプログラムをコンパイルできます。これは、Cソースコードをオブジェクトコードファイルに変換することによって行います。これは、ソースコードのバイナリバージョンを含む.o
で終わるファイルです。ただし、オブジェクトコードは直接実行可能ではありません。実行可能ファイルを作成するには、ファイルに#include
を含むすべてのライブラリ関数のコードを追加する必要があります(これは#include
宣言を含むものと同じではありません)。これがリンカの仕事です。
一般に、Cコンパイラの呼び出し方法は、使用しているシステムによって大きく異なります。ここではGCCコンパイラを使用していますが、もっと多くのコンパイラが存在することに注意してください。
% gcc -Wall -c foo.c
%
はOSのコマンドプロンプトです。これは、 foo.c
ファイル上でプリプロセッサを実行し、それをオブジェクトコードファイルfoo.o
コンパイルするようにコンパイラに指示します。 -c
オプションは、ソースコードファイルをオブジェクトファイルにコンパイルし、リンカーを呼び出さないことを意味します。このオプション-c
は、LinuxやmacOSなどのPOSIXシステムで利用できます。他のシステムでは異なる構文を使用することがあります。
あなたのプログラム全体が1つのソースコードファイルに含まれている場合、代わりにこれを行うことができます:
% gcc -Wall foo.c -o foo
これはコンパイラにfoo.c
でプリプロセッサを実行させ、コンパイルしてリンクしてfoo
という実行可能ファイルを作成するように指示します。 -o
オプションは、行の次の単語がバイナリ実行可能ファイル(プログラム)の名前であることを示します。 -o
指定しなかった場合( gcc foo.c
と入力しただけの場合)、実行可能ファイルは歴史的な理由からa.out
という名前になります。
一般に、コンパイラは、 .c
ファイルを実行可能ファイルに変換するときに、次の4つのステップを実行します。
- 前処理 -
.c
ファイルの#include
ディレクティブと#define
マクロをテキスト形式で展開 - compilation - プログラムをアセンブリに変換します(このステップで
-S
オプションを追加することでコンパイラを停止できます) - アセンブリ - アセンブリを機械コードに変換する
- リンケージ - オブジェクトコードを外部ライブラリにリンクして実行可能ファイルを作成する
また、使用しているコンパイラの名前はGCCで、コンテキストに応じて "GNU C compiler"と "GNU compiler collection"の略です。他のCコンパイラが存在する。 Unixライクなオペレーティングシステムでは、多くの場合、他のコンパイラとのシンボリックリンクである「Cコンパイラ」の名前がcc
です。 Linuxシステムでは、 cc
はしばしばGCCのエイリアスです。 macOSまたはOS-Xではclangを指します。
POSIX標準では現在、C99の名前としてc99
必須です。デフォルトではC99標準をサポートしています。以前のバージョンのPOSIXではコンパイラとしてc89
でした。 POSIXでは、このコンパイラが上記で使用したオプション-c
と-o
を理解することも要求しています。
注意:両方のgcc
例にある-Wall
オプションは、疑わしい構造に関する警告を出力するようコンパイラーに指示します。これは強くお勧めします。他の警告オプション 、例えば-Wextra
を追加することも良い考え-Wextra
。
翻訳フェーズ
§5.1.1.2翻訳フェーズにリストされているC 2011標準の時点では、ソースコードからプログラムイメージへの翻訳(例えば、実行可能ファイル)は、8つの順序付けられたステップで発生するようにリストされています。
- ソースファイルの入力は、必要に応じてソース文字セットにマップされます。このステップでは、三角形が置き換えられます。
- 継続行(
\
で終わる行)は、次の行と接続されます。 - ソースコードは、空白と前処理トークンとして解析されます。
- プリプロセッサが適用され、ディレクティブを実行し、マクロを展開し、プラグマを適用します。
#include
によって引っ張られた各ソースファイルは、変換フェーズ1〜4(必要に応じて再帰的に)を実行します。プリプロセッサ関連の指令はすべて削除されます。 - 文字定数および文字列リテラルのソース文字セット値は、実行文字セットにマップされます。
- 互いに隣接する文字列リテラルが連結されます。
- ソースコードは、翻訳単位を構成するトークンに解析されます。
- 外部参照が解決され、プログラムイメージが形成されます。
Cコンパイラの実装では、いくつかのステップを組み合わせることができますが、上記の手順が上記の順序で別々に行われたかのように動作する必要があります。