コンパイラ・システムの構成要素 (2.1 節)
Tru64 UNIX 環境におけるデータ型 (2.2 節)
C 言語プリプロセッサ (2.3 節)
ソース・プログラムのコンパイル (2.4 節)
オブジェクト・ファイルのリンク (2.5 節)
プログラムの実行 (2.6 節)
オブジェクト・ファイルのツール (2.7 節)
標準 C ライブラリにおける ANSI 名前空間汚染のクリーンアップ (2.8 節)
インライン・アセンブリ・コード ASM (2.9 節)
コンパイラ・システムはソース・コードを実行可能プログラムに変換します。 これは,次のステップから構成されます。
前処理 -- コンパイラ・システムは,マクロ定義の展開やソース・コードへのヘッダ・ファイルの取り込みなどの処理を行います。
コンパイル -- コンパイラ・システムは,ソース・ファイルまたは前処理済みのファイルを,.o
ファイル接尾語を持つオブジェクト・ファイルに変換します。
リンク -- コンパイラ・システムはバイナリ・イメージを作成します。
これらのステップは,前処理,コンパイル,リンクの別々のコマンドによって実行することも,単一のオペレーションで,コンパイル中にコンパイラ・システムが適切なタイミングで各ツールを呼び出すことによって実行することもできます。
コンパイラ・システムには,この他にも,コンパイルしてリンクされたプログラムのデバッグや,作成されたオブジェクト・ファイルのチェック,ルーチンのライブラリの作成,プログラムの実行時性能の分析などを行うツールがあります。
表 2-1
に,コンパイラ・システムのツールと,本書および他のマニュアルの参照箇所を示します。
表 2-1: コンパイラ・システムの機能
目的 | 使用ツール | 参照箇所およびマニュアル |
プログラムのコンパイル,リンク,ロードおよびシェアード・ライブラリの作成 | コンパイラ・ドライバ,リンク・エディタ,動的ローダ | この章,第 4 章
,
cc (1)c89 (1)as (1)ld (1)loader (5) |
プログラムのデバッグ | シンボリック・デバッガ (dbx
および
ladebug ) および Third Degree |
第 5 章,第 6 章,第 7 章
,
dbx (1)third (1)ladebug (1) |
プログラムのプロファイル | プロファイラ,コール・グラフ・プロファイラ | 第 8 章
,
hiprof (1)pixie (1)gprof (1)prof (1)atom (1) |
プログラムの最適化 | 最適化プログラム,ポストリンク最適化プログラム | この章,第 10 章
,
cc (1)third (1) |
オブジェクト・ファイルの検査 | nm ,
file ,
size ,
dis ,
odump ,
stdump
のツール |
この章,
nm (1)file (1)size (1)dis (1)odump (1)stdump (1) |
必要なライブラリの作成 | アーカイバ (ar ),リンカ(ld ) のコマンド |
この章,第 4 章
,
ar (1)ld (1) |
図 2-1
に,コンパイラ・システムの主な構成要素とそれらの入力と出力との関係を示します。
図 2-1: プログラムのコンパイル
コンパイラ・システムのコマンドは,ドライバ・プログラムとも呼ばれ,コンパイラ・システムの構成要素を起動します。 各言語にはそれぞれのコンパイラ・コマンドおよびオプションが用意されています。
C コンパイラは
cc
コマンドで起動します。
Tru64 UNIX のプログラミング環境では,1 つの
cc
コンパイラ・コマンドで,次のような複数のアクションを実行することができます。
各ファイル名の接尾語に基づいて,適切なプリプロセッサ,コンパイラ (またはアセンブラ),リンカのいずれを呼び出すかどうかを判断する。
コンパイラ・コマンドは,ファイル名に付けられた接尾語によって入力ファイルの内容を識別します。 表 2-2 に,サポートされるファイルの接尾語を示します。
ソース・ファイルをコンパイルおよびリンクして,実行可能プログラムを作成する。
複数のソース・ファイルが指定されている場合には,これらのファイルは,リンクされる前に他のコンパイラに渡されます。
as
アセンブラを呼び出すことにより,アセンブラ・コードを含むと考えられる 1 つ以上の
.s
ファイルをアセンブルし,その結果として出力されたオブジェクト・ファイルをリンクする。
as
コマンドは,アセンブルされたオブジェクト・ファイルを自動的にリンクしないため,アセンブラを直接起動する場合には,別のステップでオブジェクト・ファイルをリンクする必要があります。
リンクを行わない (すなわち実行可能プログラムを作成しない) よう指定する。
そのため,後のリンク・オペレーションのために
.o
オブジェクト・ファイルを保管する。
リンク・コマンド (ld
) に関連する主要なオプションをリンカに渡す。
たとえば,cc
コマンドに
-L
オプションを指定して実行すると,ライブラリを探索するためのディレクトリ・パスを指定することができます。
各言語には,リンク時に別々のライブラリが必要になります。
言語のドライバ・プログラムは,対応するライブラリをリンカに渡します。
ライブラリとのリンクについての詳細は,第 4 章および2.5.3 項を参照してください。
接尾語 | ファイル |
.a |
アーカイブ・ライブラリ |
.c |
C ソース・コード |
.i |
この接尾語の付くファイルは C プリプロセッサによって処理された,ドライバ用のソース・コードであるとみなされる。
たとえば
% cc -c source.i
のように実行する。
ファイル
source.i
は,C ソース・コードを含んでいるとみなされる。 |
.o |
オブジェクト・ファイル |
.s |
アセンブリ・ソース・コード |
.so |
共用オブジェクト (シェアード・ライブラリ) |
以降の各項で,Tru64 UNIX システムでデータ項目を表現する方法について説明します。
注意
Tru64 UNIX システムでの省略時のメモリ・アクセス・サイズは 8 バイト (クォドワード) です。 したがって,2 つ以上の実行スレッドが同時に隣接するメモリを変更する場合,そのメモリ・アドレスをクォドワードに整列させて,個々の変更で誤って上書きされないようにしなければなりません。 たとえば,コンポジット・データ構造の同じクォドワードに含まれる別々のデータ項目が同時に変更されるような場合にエラーが生じることがあります。
クォドワード以外での整列や,その他の問題が生じるさまざまな状況については,『Guide to the POSIX Threads Library』 の「Granularity Considerations」を参照してください。
Tru64 UNIX システムは下位バイト処理型であり,右から左の順にバイトを使用します。 C コンパイラでは,下位バイト処理型のバイト順のみをサポートしています。 サポートするデータ型のサイズは次のとおりです。
データ型 | ビットの大きさ |
char | 8 |
short | 16 |
int | 32 |
long | 64 |
long long | 64 |
float | 32 (IEEE 単精度) |
double | 64 (IEEE 倍精度) |
pointer | 64 [脚注 1] |
long double | 128 |
C コンパイラは,『IEEE Standard for Binary Floating-Point Arithmetic』(ANSI/IEEE Std 754-1985) に定義されているように,IEEE 単精度 (32 ビット
float
),倍精度 (64 ビット
double
),拡張倍精度 (128 ビット
long double
) 浮動小数点データをサポートしています。
浮動少数点数は次の範囲になります。
float
: 1.17549435e-38f から 3.40282347e+38f まで
double
: 2.2250738585072014e-308 から 1.79769313486231570e+308 まで
long double
: 3.3621031431120935062626778173217526026e-4932 から 1.1897314953572317650857593266280070162e+4932
Tru64 UNIX では,規格に定義されている基本的な浮動小数点数フォーマット,演算 (加算,減算,乗算,除算,平方根,剰余,比較),および変換を提供しています。
コンパイラ・オプションを指定することにより,完全に IEEE に準拠したトラップ動作 (NaN [番号でない] を含む) を使用することができます。
また,IEEE 形式のトラップが必要ない場合には,速いモードを指定することができます。
コンパイル時に丸めモードを選択して,IEEE 演算の結果に適用することもできます。
IEEE
浮動少数点処理をサポートするオプションについての詳細は,
cc
(1)
ユーザ・プログラムでは,ieee_set_fp_control()
を呼び出して,浮動小数点トラップのスレッドへの引き渡しを制御したり,write_rnd()
を呼び出して,IEEE 丸めモードを動的に設定することができます。
IEEE
浮動少数点の例外を処理する方法についての詳細は,
ieee
(3)2.2.3 構造体の位置合わせ
C コンパイラは,省略時の設定では,構造体のメンバを自然境界に位置合わせします。 つまり,構造体の構成要素は,宣言された順にメモリに配置されます。 最初の構成要素は,構造体全体と同じアドレスを持ちます。 それに続く各構成要素は,前の構成要素に続いて,その構成要素の型の次の自然境界に合わせられます。
たとえば,次の構造体は図 2-2 に示すように位置合わせが行なわれます。
struct {char c1; short s1; float f; char c2; }
構造体の最初の構成要素
c1
は,オフセット 0 から始まって,その最初のバイトを占めています。
2 番目の構成要素の
s1
は,short
であるため,ワード境界から始まる必要があります。
したがって,c1
と
s1
の間に埋め込みを行います。
f
と
c2
を自然境界に合わせるには,埋め込みは必要ありません。
ただし,サイズが
f
の位置合わせの倍数に切り上げられるため,c2
の後ろに 3 バイトの埋め込みが行われます。
構造体メンバの省略時の位置合わせの変更には,次のような方法を使用することができます。
#pragma member_alignment
および
#pragma nomember_alignment
指示文
#pragma pack
指示文
-Zpn
オプション
これらの指示文については,3.8 節および3.11 節を参照してください。
2.2.4 ビット・フィールドの位置合わせ
一般に,ビット・フィールドの位置合わせは,その前のフィールドのビット・サイズとビット・オフセットによって決定されます。 たとえば,次の構造体は,図 2-3に示すような位置合わせが行われます。
struct a { char f0: 1; short f1: 12; char f2: 3; } struct_a;
最初のビット・フィールド
f0
は,ビット・オフセット 0 から始まり 1 ビットを占めています。
2 番目の
f1
は,オフセット 1 から始まり 12 ビットを占めています。
3 番目の
f2
は,オフセット 13 から始まり 3 ビットを占めています。
構造体のサイズは 2 バイトです。
次のような場合には,ビット・フィールドの位置合わせの前に埋め込みが行われます。
サイズ 0 のビット・フィールドでは,次のパック境界まで埋め込みが行われます。
パック境界は,#pragma pack
指示文,または
-Zpn
コンパイラ・オプションによって決定されます。
サイズが 0 のビット・フィールドの場合,ビット・フィールドの基本タイプは無視されます。
たとえば,次の構造体について考えてみてください。
struct b { char f0: 1; int : 0; char f1: 2; } struct_b;
-Zp1
オプションを指定してソース・ファイルをコンパイルした場合,またはコンパイル時に
#pragma pack 1
指示文を検出した場合には,f0
はオフセット 0 から始まって 1 ビットを占有し,名前のないビット・フィールドはオフセット 8 から始まって 0 ビットを占有し,f1
はオフセット 8 から始まって 2 ビットを占有します。
同様に,-Zp2
オプションまたは
#pragma pack 2
指示文が使用されている場合は,名前のないビット・フィールドはオフセット 16 から始まります。
-Zp4
または
#pragma pack 4
が使用されていれば,オフセット 32 から始まります。
ビット・フィールドが現在のユニットに入らない場合は,次のパック境界,または次のユニット境界のうち,どちらか近い方まで,埋め込みが行われます。
(ユニット境界はビット・フィールドの基本型によって決定されます。
たとえば,宣言 "char foo: 1
" に関連するユニット境界はバイトです。
)
現在のユニットは,次の例に示すように,現在のオフセット,ビット・フィールドの基本サイズ,指定されているパックの種類によって決定されます。
struct c { char f0: 7; short f1: 11; } struct_c;
-Zp1
オプションまたは
#pragma pack 1
指示文を指定したと仮定すると,この構造体では
f0
はビット・オフセット 0 から始まって 7 ビットを占有します。
f1
の基本サイズが 8 ビットであり,現在のオフセットが 7 であるため,f1
は現在のユニットには入りません。
次のユニット境界または次のパック境界のうち,どちらか近い方まで埋め込みが行われ,この場合はビット 8 になります。
この構造体のレイアウトを図 2-4
に示します。
データ位置合わせはデータ型によって暗黙に定義されます。
たとえば,C コンパイラは
int
(32 ビット) を 4 バイト境界に位置合わせし,long
(64 ビット) を 8 バイト境界に位置合わせします。
_ _align
記憶クラス修飾子は,任意の C データ型のオブジェクトを指定された記憶境界に位置合わせします。
これは,データ宣言または定義で使用できます。
_ _align
修飾子は次の形式で使用します。
_ _align (keyword)
_ _align (n)
keyword には定義済み位置合わせ定数を,n には 2 の累乗の整数を指定します。 定義済み定数あるいは 2 の累乗は,コンパイラに対してデータの位置合わせのためのバイト数を指定します。
たとえは,整数を次のクォドワード境界に合わせる場合は,次のいずれかの宣言を使用します。
int _ _align( QUADWORD ) data; int _ _align( quadword ) data; int _ _align( 3 ) data;
この例では,int _ _align ( 3 )
は 2x2x2 バイト (8 バイト),つまりメモリの 1 クォドワードの位置合わせを指定します。
定義済み境界合わせ定数とそれに対応する2 の累乗およびバイト数は次のとおりです。
定数 | 2 の累乗 | バイト数 |
BYTE あるいは byte | 0 | 1 |
WORD あるいは word | 1 | 2 |
LONGWORD あるいは longword | 2 | 4 |
QUADWORD あるいは quadword | 3 | 8 |
C プリプロセッサは,ソース・ファイルをコンパイルする前に,マクロの展開,ヘッダ・ファイルの取り込み,プリプロセッサ指示文の実行を行います。
以降の各項で,C プリプロセッサが実行する Tru64 UNIX に固有の処理について説明します。
C プリプロセッサについての詳細は,リファレンス・ページの
cc
(1)cpp
(1)2.3.1 定義済みマクロ
コンパイラが起動されると,入力ファイルの言語とそのコードの実行環境を識別する C プリプロセッサ・マクロが定義されます。
プリプロセッサ・マクロの一覧については,
cc
(1)#ifdef
文でこれらのマクロを参照すると,特定の言語または環境に適用するコードを分離することができます。
Tru64 UNIX を一意に識別するには,次の文を使用します。
#if defined (_ _digital_ _) && defined (_ _unix_ _)
ソース・ファイルのタイプと適用する規格のタイプによって,定義されるマクロが決定されます。 C コンパイラはいくつかのレベルの規格をサポートしています。
-std
オプションを指定すると,ANSI C 規格を適用しますが,規格で許可されていない共通プログラミング手法のいくつかを許可し,マクロ
_ _STDC_ _
を 0 (ゼロ) に設定します。
これは省略時の設定です。
-std0
オプションを指定すると,Kernighan と Ritchie (K & R) のプログラミング・スタイルを適用しますが,K & R の動作が定義されていなかったり不明瞭であるものについては,特定の ANSI 拡張機能が使用できます。
一般に,-std0
では,ほとんどの ANSI 以前の C プログラムをコンパイルして,期待どおりの結果を得ることができます。
この場合,_ _STDC_ _
マクロが定義されません。
-std1
オプションを指定すると,ANSI C 規格およびその禁止事項のすべてを厳密に適用します。
禁止事項には,void
の処理に適用されるものや,式における
lvalue
の定義,整数とポインタの混合,rvalue
の変更などがあります。
これは
_ _STDC_ _
マクロを 1 に定義します。
cpp
コマンドは,標準の C マクロ,すなわち
_ _LINE_ _
,_ _FILE_ _
,_ _DATE_ _
,_ _TIME_ _
,(該当する場合は
_ _STDC_ _
) 以外は事前に定義しないので注意してください。
2.3.2 ヘッダ・ファイル
システム・ライブラリへのインタフェースを定義する
大規模なアプリケーション内で別々にコンパイルされたモジュールに共通な定数,型,関数プロトタイプを定義する
C ヘッダ・ファイル (インクルード・ファイルとも呼ばれる) には,.h
接尾語が付きます。
必要なヘッダ・ファイルは,通常,ライブラリ・ルーチンまたはシステム・コールのリファレンス・ページに示されています。
ヘッダ・ファイルは,さまざまな言語で記述されたプログラムで使用することができます。
注意
dbx
またはladebug
を使用してプログラムをデバッグする場合は,ヘッダ・ファイルに実行可能コードを入れてはなりません。 デバッガがヘッダ・ファイルをソース・コードの 1 行として解釈し,デバッグ・セッション時にファイルのソース行が,まったく表示されなくなるためです。dbx
デバッガについての詳細は,第 5 章を参照してください。ladebug
についての詳細は,『Ladebug Debugger Manual』を参照してください。
ヘッダ・ファイルは,次の 2 つの方法のうちのいずれかでプログラム・ソース・ファイルに含めることができます。
#include "filename"
これは,C マクロ・プリプロセッサが,その指示文を含むファイルを見つけたディレクトリ,-I
オプションで指定された探索パス,/usr/include
の順でインクルード・ファイル
filename
を探索することを示します。
#include <filename>
これは,C マクロ・プリプロセッサが,その指示文を含むファイルを見つけたディレクトリではなく,-I
オプションで指定された探索パスと
/usr/include
でインクルード・ファイル
filename
の探索を行うことを示します。
また,-Idir
および
-nocurrent_include
オプションを使用して,C プリプロセッサに
#include
ファイルを探索させる追加のパス名 (ディレクトリ) を指定することもできます。
-I
dir
については,C プリプロセッサは,最初にその指示文を含むファイルを見つけたディレクトリの中を探索し,続いて,指定されたパス名 (dir),次に省略時のディレクトリ (/usr/include
) を探索します。
dir
が省略されると,省略時のディレクトリは探索されません。
引数のない
-I
については,C プリプロセッサは
/usr/include
を探索しません。
-nocurrent_include
については,C プリプロセッサは,#include
指示文を含むファイルのあるディレクトリを探索しません。
つまり,#include "filename"
は
#include <filename>
と同様に処理されます。
C,Fortran,およびアセンブリ・コードは,同じインクルード・ファイルに常駐させることができ,必要に応じてプログラムの中に条件付きで挿入することができます。
共用可能なインクルード・ファイルを設定するには,.h
ファイルを作成し,次の例のように,それぞれのコードを入力しなければなりません。
#ifdef _ _LANGUAGE_C_ _ . . (C code) . #endif #ifdef _ _LANGUAGE_ASSEMBLY_ _ . . (assembly code) . #endif
コンパイラがこのファイルを C ソース・ファイルに取り込むと,_ _LANGUAGE_C_ _
マクロが定義されて,C コードがコンパイルされます。
コンパイラがこのファイルをアセンブリ言語ソース・ファイルに取り込むと,_ _LANGUAGE_ASSEMBLY_ _
マクロが定義されて,アセンブリ言語コードがコンパイルされます。
2.3.4 処理系固有のプリプロセッサ指示文 (#pragma)
#pragma
指示文は,コンパイラごとに異なる機能をインプリメントする標準的な方法です。
C コンパイラは,次の処理系固有のプラグマをサポートします。
#pragma assert
#pragma environment
#pragma extern_model
#pragma extern_prefix
#pragma function
#pragma inline
#pragma intrinsic
#pragma linkage
#pragma member_alignment
#pragma message
#pragma optimize
#pragma pack
#pragma pointer_size
#pragma use_linkage
#pragma weak
これらのプラグマについての詳細は,第 3 章で説明します。
2.4 ソース・プログラムのコンパイル
cc
コマンドで設定されたコンパイル環境は,共通オブジェクト・ファイル・フォーマット (COFF) に準拠しているオブジェクト・ファイルを作成します。
cc
コマンドでサポートされているオプションは,デバッグ,最適化,プロファイル機能など,さまざまなプログラム開発関数と出力ファイルへ割り当てる名前を選択します。
cc
コマンド行オプションについての詳細は,
cc
(1)
以降の各項で,省略時のコンパイラの動作および各国語対応プログラムのコンパイル方法について説明します。
2.4.1 省略時のコンパイル動作
ほとんどのコンパイラ・オプションには,そのオプションがコマンド行で指定されない場合に使用される省略時の値があります。
たとえば,出力ファイルの省略時の名前は,オブジェクト・ファイルに対しては
filename.o
になります。
このとき,filename
は,ソース・ファイルのベース名です。
実行可能プログラムのオブジェクトの省略時の名前は,a.out
になります。
次の例では,prog1.c
および
prog2.c
のソース・ファイルをコンパイルする際に,省略時の設定が使用されます。
% cc prog1.c prog2.c
このコマンドによって C コンパイラが実行され,prog1.o
と
prog2.o
というオブジェクト・ファイルと,a.out
という実行可能プログラムが作成されます。
cc
コンパイラ・コマンドに他のオプションを指定しないで入力した場合には,次のオプションが有効になります。
ANSI C 別名化規則をオフにします。 これは,最適化プログラムが最適化においてアグレッシブにならないようにします。
すべての Alpha プロセッサに適切な命令を生成します。
コンパイラがそのような仮定をして,位置合わせされたポインタ型のポインタ逆参照のためにより効率的なコードを生成できるようにします。
コンパイラは,errno
を設定できる算術ライブラリ・ルーチンを呼び出した後,プログラムが
errno
を問い合わせる可能性があることを想定できるようにします。
実行時に共用可能オブジェクトを使用する動的な実行可能ファイルを作成します。
配列境界の実行時検査を無効にします。
コンパイル前に C およびアセンブリ・ソース・ファイルで,C マクロ・プリプロセッサが呼び出されるようにします。
指定のコンパイルに対してコンパイラが出力するエラー・レベルの診断数を 30 までに制限します。
型表現を
float
から
double
に昇格する必要がないことをコンパイラに通知します。
精度に影響するような浮動小数点演算の順序変更をしないようにコンパイラに指示します。
浮動少数点数の通常の丸め (不偏の丸めから近似値へ) を行います。
浮動小数点アンダフローまたは不正確なトラッピング・モードを生成しない命令を生成します。
シンボリック・デバッグのためのシンボル情報を生成しません。
ディレクトリ
/usr/include
において,ファイル名がスラッシュ (/) で始まらない
#include
ファイルが最初に探索されることを指定します。
#pragma inline
指示文によって明示的にインライン化が要求されている関数呼び出しのみをインライン化します。
特定の関数を intrinsics として認識して,適切な最適化を行うようにコンパイラに指示します。
コンパイラに指示して,データ構造体のメンバを (ビット・フィールドのメンバを除き) 自然境界に位置合わせするようにします。
任意に位置合わせされたアドレスの位置合わせフォールトを生成します。
インクルード・ファイルのネスト・レベル制限を 50 に設定します。
省略時のオプション設定をすべてリストしてコンパイラを起動します。 このオプションは,-migrate をオフにするためだけに用意されています。
グローバルな最適化を可能にします。
プロファイリングを無効に設定します。
gprof
プロファイリングをオフにします。
モジュールごとにシンボルの優先使用を可能にします。
パス名が
/usr/include
で始まるヘッダ・ファイル内の移植性のない構造に対するメッセージを抑制します。
char
型の表現を
signed char
型と同じにします。
ANSI C 規格を拡張しますが,規格によって禁止されている一般的なプログラミング手法を許可します。
Alpha アーキテクチャのすべての処理系に適切な命令のチューニングを選択します。
文字列リテラルを書き込み可能にします。
表 2-3
に,
cc
(1)表 2-3: cc コマンドのオプション・カテゴリごとの省略時オプション
オプション・カテゴリ | 省略時オプション |
コンパイラ選択 | -newc |
言語モード | -std |
コンパイラ全体の動作 | -arch generic, -error_limit 30, -nestlevel=50 |
コンパイラの診断制御 | -SD/usr/include |
C プリプロセッサ | -cpp, -I/usr/include |
リンカまたはローダ | -call_shared |
最適化 | -noansi_alias, -assume math_errno, -float, -nofp_reorder, -inline manual, -intrinsics, -O1, -preempt_symbol, -tune generic |
フィードバック主導の最適化 | なし |
ソース・コードのデバッグ | -g0 |
プログラムのプロファイリング | -p0, -nopg |
データの整列 | -assume aligned_objects, -member_alignment, -nomisalign |
データの揮発性 | -weak_volatile |
C 言語 | -signed, -writable_strings |
スタックの処理とポインタの処理 | なし |
IEEE の浮動小数点サポート | -fprm n, -fptm n |
コンパイラの開発 | なし |
次に示すのは,cc
コンパイラのその他の省略時の動作です。
省略時の動作では,コンパイル (あるいはアセンブル) が成功した場合はソース・ファイルは自動的にリンクされます。
-o
オプションで出力ファイル名を指定していない場合は,a.out
が使用されます。
浮動小数点の計算は,IEEE 浮動小数点でなくファスト浮動小数点で行われます。
ポインタは 64 ビットです。 32 ビット・ポインタの使用方法については,付録 A を参照してください。
一時ファイルは
/tmp
ディレクトリか,または
$TMPDIR
環境変数で指定したディレクトリに作成されます。
メイン・プログラムのソース言語がサブプログラムのソース言語と異なっている場合は,適切なドライバで各プログラムを個別にコンパイルし,別のステップでリンクしてください。
-c
オプションを指定すると,リンクに適したオブジェクトを作成することができます。
-c
オプションは,オブジェクト・ファイルが作成された直後にドライバを停止します。
たとえば,次のように入力してください。
% cc -c main.c
このコマンドは,実行可能ファイル
a.out
ではなく,オブジェクト・ファイル
main.o
を作成します。
C 以外の言語で作成したソース・ファイルのオブジェクト・モジュールを作成したのち,cc
コマンドを使用して C ソース・ファイルをコンパイルし,すべてのオブジェクト・モジュールをリンクして 1 つの実行可能ファイルにすることができます。
たとえば,次の
cc
コマンドは
c-prog.c
をコンパイルし,c-prog.o
および
nonc-prog.o
をリンクして
a.out
という実行可能ファイルを作成します。
% cc nonc-prog.o c-prog.c
cc
コマンドで
-check_bounds
オプションを指定すると,実行時コードを生成して,配列境界検査が行われます。
-nocheck_bounds
オプション (省略時の設定) は,配列境界の実行時検査を無効にします。
コンパイラに対して実行時検査を指示するコード,および指定された検査で使用される正確な境界値は,コンパイラのインプリメンテーション上の特性に影響されるため,ユーザには明白ではありません。 これらを左右する厳密な条件は,次のとおりです。 以下の説明は,配列を含めて C の言語規則を十分に理解していることを前提としています。
検査は,宣言された配列オブジェクト名の使用時に行われます。 ポインタ値の使用時には,たとえポインタが添字演算子を使って逆参照されるとしても,検査は行われません。 これは,たとえば,1 次元の配列として宣言された仮パラメータは,C 言語ではポインタと見なされるため,検査が行われないことを意味します。 ただし,仮パラメータが多次元配列の場合,最初の添字はアクセス対象の配列オブジェクトを決定するポインタ操作を表し,そこでは境界は検査されません。 2 番目およびそれに続く添字で,境界検査が生成されます。
添字演算子 (左または右のオペランド) を使って配列へのアクセスが行われ,かつ添字演算子が演算子のアドレスのオペランドではない場合,インデックスがゼロと配列内の要素数から 1 を引いた数との間の範囲内にあるかどうかが検査されます。
添字演算子 (左または右のオペランド) を使って配列へのアクセスが行われ,かつ添字演算子が演算子のアドレスのオペランドである場合,インデックスがゼロから配列内の要素数までの範囲内にあるかどうかが検査されます。 C 言語では,特に,配列の終端を 1 要素超えたアドレスを計算に使用できなければなりません。 それは,次のループ終了テストのような一般的なプログラミング手法を可能にするためです。
int a[10]; int *b; for (b = a ; b < &a[10] ; b++) { .... }
この場合,a[10]
が配列の境界外にあっても,&a[10]
の使用が可能になります。
配列へのアクセスがポインタ加算を使って行われる場合,追加された値がゼロから配列内の要素数までの範囲内にあるかどうかが検査されます。 配列名に整数を追加することには,配列名を最初の要素へのポインタに変換すること,および要素のサイズに応じて決定された整数値を追加することが含まれます。 コンパイラ内での境界検査の実装は,配列名からポインタへの変換をトリガとして実行されますが,境界検査の導入時には,生成されるポインタ値が逆参照されるかどうかは不明です。 このため,この場合も前述の場合と同様に処理されます。 つまり,アドレスの計算のみが検査され,配列の終端を超えた 1 要素のアドレスも計算できます。
配列へのアクセスがポインタ減算を使って行われる (つまり,あるポインタから別のポインタを減算することではなく,ポインタから整数値を減算する) 場合,減算される値が配列の要素数を負にした値からゼロまでの範囲内にあるかどうかが検査されます。
後者の 3 つの場合には,オプションのコンパイル時のメッセージ (ident SUBSCRBOUNDS2
) から,配列へのアクセスが定数の添字または定数ポインタ算術のいずれかを使って行われたこと,アクセスされた要素が配列の終端を 1 要素だけ超えた範囲外にあることを正確に検出できます。
1 つの要素を使って宣言された配列の検査は行われません。
ANSI C では,struct
の宣言内に複数の次元のない配列は許可されないため,動的にサイズ変更が行われる配列を,複数メンバに割り当てられた要素数を保持する
struct
としてインプリメントし,最後のメンバを 1 つの要素で宣言された配列とすることが一般的な手法であるためです。
最後の配列メンバへのアクセスは,実行時に割り当てられたサイズにより制限されるため,1 と宣言された境界に対する検査は役に立ちません。
この場合,オプションのコンパイル時メッセージ (ident SUBSCRBOUNDS1
) を有効に設定することにより,単一要素で宣言された配列へのアクセスが定数の添字または定数ポインタ算術のいずれかを使って行われたことを検出できること,およびアクセスされた要素は配列の一部ではないことに注意してください。
コンパイラは,定数で索引付けされた配列の実行時検査を発行します (コンパイラがコンパイル時にここで論じている事例を検出でき,実際に検出した場合でも)。 コンパイラがアクセスが有効であると判断すると,例外として,実行時検査が行われません。
多次元配列へのアクセスが行われると,コンパイラは各添字式を検査して,それぞれが対応する境界内にあることを確認します。
次のコードの場合,コンパイラは
x
と
y
の両方が 0 から 9 までの範囲内にあるかどうかを検査します (10 * x + y
が 0 から 99 までの範囲内にあるかどうかは検査しません)。
int a[10][10]; int x,y,z; x = a[x][y];
次の例は,これまで示した規則を例証するものです。
int a[10]; int *b; int c; int *d; int one[1]; int vla[c]; // C9X variable-length array a[c] = 1; // check c is 0-9, array subscript c[a] = 1; // check c is 0-9, array subscript b[c] = 1; // no check, b is a pointer d = a + c; // check c is 0-10, computing address d = b + c; // no check, b is a pointer b = &a[c] // check c is 0-10, computing address *(a + c) = 1; // check c is 0-10, computing address *(a - c) = 1; // check c is -10 to 0, computing address a[1] = 1; // no run-time check - know access is valid vla[1] = 1; // run-time check, vla has run-time bounds a[10] = 1; // run-time check (and compiler diagnostic) d = a + 10; // no run-time check, computing address // SUBSCRBOUNDS2 message can be enabled c = one[5]; // no run-time check, array of one element // SUBSCRBOUNDS1 message can be enabled
境界外のアクセスに遭遇すると,出力は次のようになります。
Trace/BPT trap (core dumped)
プログラムは,次のコードを使ってこのエラーをトラップできます。
signal(SIGTRAP, handler);
実行時検査が有効な場合は,ポインタ算術を使って配列への正当なアクセスが行われた場合に,間違った検査が行われることがあります。
コンパイラは,配列名からポインタへの変換により得られたポインタへの最初の算術操作に対する検査コードだけを出力できます。
このため,得られたポインタ値に対して再びポインタ算術による操作が行われると,検査が不正確になる可能性があります。
a = b + c - d
という式があり,a
がポインタ,b
が配列,c
と
d
が整数の場合を考えてみます。
境界検査が有効な場合,c
が配列の境界内にあるかどうかが検査されます。
このとき,c
が配列の境界外にあるが,c - d
は境界内にある場合,間違った実行時トラップとなります。
この場合には,ポインタ式をコーディングし直して,整数部分をカッコ内に含めることができます。 これで,式には 1 つのポインタ算術演算子だけが含まれることになり,正確な検査が行われます。 前の例では,式は次のように変更されます。
a = b + (c - d);
cc
ドライバ・コマンドは,オブジェクト・ファイルをリンクして,実行プログラムを作成します。
場合によっては,ld
リンカを直接使用することもあります。
アプリケーションの特性に応じて,コンパイルしてから別にリンクを行うか,またはコンパイラ・コマンドを使用して,コンパイルとリンクを一度に実行するかを決定してください。
考慮すべき点は数多くありますが,特に次のことを考慮にいれてください。
すべてのソース・ファイルが同一の言語であるかどうか
ソース形式のファイルがあるかどうか
リンカ・コマンドの代わりにコンパイラ・コマンドを使用して,異なるオブジェクトを 1 つの実行可能プログラムにリンクすることができます。
アセンブラを除くコンパイラは,.o
接尾語を認識するとすぐにリンカを実行します。
この接尾語は,そのファイルにリンクが可能なオブジェクト・コードを含まれていることを示します。
コンパイラのドライバ・プログラムは言語に対応するライブラリを自動的にリンカに渡すため,通常はコンパイラ・コマンドを使用してください。
たとえば,cc
ドライバでは,省略時の設定として C ライブラリ (libc.so
) を使用します。
各コンパイラ・コマンドが使用する省略時のライブラリについての詳細は,
cc
(1)
また,cc
コマンドの
-l
オプションを使用して追加のライブラリを指定し,未解決の参照を探索することもできます。
次の例は,cc
ドライバを使用して,-l
オプションで 2 つのライブラリ名をリンカに渡す方法を示しています。
% cc -o all main.o more.o rest.o -lm -lexc
-lm
オプションは算術ライブラリを指定し,-lexc
オプションは例外ライブラリを指定します。
プログラムを最適化する場合には,1 つのコマンドを使用してモジュールのコンパイルとリンクを行ってください。 ほとんどのコンパイラには,特定のオプションを使用して,最適化のレベルを上げる機能があります。 たとえば,次のようなオプションを使用できます。
-O0
オプションは,最適化の要求は行いません。
これは通常,デバッグのために使用されます。
-O1
オプションは,ローカルなモジュール固有の最適化を要求します。
モジュール間にまたがる最適化は,-ifo
オプションを用いて要求しなければなりません。
さらに
-O3
オプションを指定すると,モジュール間にまたがる最適化が改善されます。
この場合には,1 つの操作で複数のファイルのコンパイルを行うことによって,コンパイラは最大限の最適化を実行することができます。
-ifo
オプションを指定すると,複数のソース・ファイルに対して 1 つの
.o
ファイルが生成されます。
通常,ユーザがリンカを直接実行するのではなく,cc
コマンドを使用して間接的に実行します。
アセンブラ・オブジェクトから単独で作成する必要のある実行可能プログラムは
ld
コマンドで作成しなければなりません。
リンカ (ld
) は,コマンド行に指定された順番で 1 つまたは複数のオブジェクト・ファイルを結合して,1 つのプログラム・オブジェクト・ファイルを作成します。
リンカによって,再配置,外部シンボルの解決,および実行可能ファイルの作成に必要な,他のすべての処理が実行されます。
-o
オプションで指定しない限り,実行可能プログラム・ファイルは,a.out
という名前になります。
このプログラム・ファイルを実行することも,別のリンカ・オペレーションへの入力として使用することもできます。
as
アセンブラはリンカを自動的に実行しないため,アセンブリ言語で記述されたプログラムをリンクするには,次のいずれかの処理を行います。
他のコンパイラ・コマンドのいずれかを使用して,アセンブルおよびリンクを行う。
アセンブリ言語ソース・ファイルの接尾語
.s
によって,コンパイラ・コマンドは自動的にアセンブラを起動します。
as
を使用してアセンブルを実行し,次に
ld
コマンドを使用して,アセンブルが実行されたオブジェクト・ファイルのリンクを行う。
リンクのプロセスに影響を与えるオプションやライブラリについての詳細は,リファレンス・ページの
ld
(1)2.5.3 ライブラリの指定
Tru64 UNIX システム上でコンパイルされたプログラムは,自動的に
libc.so
という C ライブラリとリンクされます。
libc.so
にないルーチンまたはコンパイラ・コマンドに対応するアーカイブ・ライブラリのうちのいずれかを使用する場合は,プログラムをライブラリと明示的にリンクしなければなりません。
ライブラリと明示的にリンクしない場合には,プログラムは正常にリンクされません。
次のような状況では,ライブラリを明示的に指定する必要があります。
多言語プログラムのコンパイル
多言語プログラムをコンパイルする場合は,必要な実行時ライブラリがあれば,必ず明示的に要求して未解決の参照を処理してください。
-lstring
を指定することによって,ライブラリがリンクされます。
string
はライブラリ名の省略形です。
たとえば,C のメイン・プログラムおよび別の言語のプロシージャを記述する場合は,その言語と算術ライブラリに対して,ライブラリを明示的に指定しなければなりません。
これらのオプションを使用すると,リンカは
-l
を
lib
に置き換えて,言語ライブラリおよび算術ライブラリのための指定された文字と,スタティック・ライブラリ (非共用アーカイブ・ライブラリ) かダイナミック・ライブラリ (呼び出し共用オブジェクトまたはシェアード・ライブラリ) により
.a
接尾語あるいは
.so
接尾語を追加します。
続いて,結果として生成されたライブラリ名を次のディレクトリの中で探索します。
/usr/shlib
/usr/ccs/lib
/usr/lib/cmplrs/cc
/usr/lib
/usr/local/lib
/var/shlib
各言語で使用されるライブラリの一覧については,さまざまな言語のコンパイラ・ドライバのリファレンス・ページを参照してください。
アーカイブ・ライブラリへのオブジェクト・ファイルの保存
コンパイラまたはリンカのコマンド行にライブラリのパス名を指定しなければなりません。
たとえば,次のコマンドでは,/usr/jones
ディレクトリ内の
libfft.a
というアーカイブ・ライブラリが算術ライブラリとリンクされるように指定されます。
% cc main.o more.o rest.o /usr/jones/libfft.a -lm
リンカは,指定された順序でライブラリを探索します。 したがって,アーカイブ・ライブラリのいずれかのファイルで,算術ライブラリからのデータまたはプロシージャが使用される場合は,算術ライブラリを指定する前に,アーカイブ・ライブラリを指定しなければなりません。
さまざまな種類のリンク・エラーに対して,リンカはエラーを無視して出力ファイルを作成し,同名の同じ出力ファイルを上書きします。 ほとんどのアプリケーションでは,この動作による問題は生じません。 問題が生じる場合は,既存の出力ファイルが上書きされないようにすることができます。 この場合は,リンカに対して一時ファイルの出力を指定し,次にリンクがエラーなしで完了した時点で一時ファイルの名前を目的の出力ファイルに変更します。 call_shared 実行可能ファイルでの実行例を次に示します。
cc -o main.exe.X main.o mv main.exe.X main.exe
次の例に示すように,シェアード・ライブラリを構築する際にも同じ手法を使用できます。 この場合は,-soname オプションを使用して,ライブラリの内部名を正しく設定する必要があります。
ld -shared -soname libfoo.so -o libfoo.so.X foo.o -lc mv libfoo.so.X libfoo.so
現在の作業ディレクトリで実行可能プログラムを実行するには,ほとんどの場合,そのファイル名を入力します。
たとえば,現在のディレクトリにあるプログラム
a.out
を実行するには,次のように入力します。
% a.out
実行可能プログラムがパスのディレクトリ内に存在しない場合は,次のように,ファイル名の前にディレクトリ・パスを入力します。
% ./a.out
プログラムが起動されるとき,C プログラムの
main
関数が,次のオプションのパラメータのいずれかを指定して定義されている場合には,main
関数はコマンド行から引数を受け取ることができます。
int
main ( int
argc, char *argv[ ], char *envp[ ] )[...]
argc
パラメータは,プログラムを起動したコマンド行に指定されている引数の数です。
argv
パラメータは,引数を含む文字列の配列です。
envp
パラメータは,ユーザ名や制御ターミナルなどのプロセス情報を含む環境配列です。
(envp
パラメータはコマンド行引数の引き渡しには何の関係もありません。
主に,exec
および
getenv
関数の呼び出し中に使用されます。
)
定義したパラメータにのみアクセスすることができます。 たとえば,次のプログラムでは,argc および argv パラメータを定義して,プログラムに渡されたパラメータの値をエコーします。
/* * Filename: echo-args.c * This program echoes command-line arguments. */ #include <stdio.h> int main( int argc, char *argv[] ) { int i; printf( "program: %s\n", argv[0] ); /* argv[0] is program name */ for ( i=1; i < argc; i++ ) printf( "argument %d: %s\n", i, argv[i] ); return(0); }
このプログラムを次のコマンドでコンパイルすると,a.out
というプログラム・ファイルが作成されます。
$ cc echo-args.c
ユーザが
a.out
を起動して,コマンド行引数を引き渡すと,プログラムによりそれらの引数がターミナル上にエコーされます。
たとえば,次のように入力します。
$ a.out Long Day\'s "Journey into Night" program: a.out argument 1: Long argument 2: Day's argument 3: Journey into Night
引数を
a.out
に引き渡す前に,シェルによってすべての引数が解析されます。
このため,単一引用符の前にはバックスラッシュを指定し,アルファベットの引数は空白またはタブで区切り,引数に空白やタブを含める場合は引用符で囲む必要があります。
2.7 オブジェクト・ファイルのツール
ソース・ファイルをコンパイルした後,次に示すツールを使用すると,オブジェクト・ファイルや実行可能ファイルをチェックすることができます。
odump
-- シンボル・テーブルとヘッダ情報を含む,オブジェクト・ファイルの内容を表示します。
stdump
--
オブジェクト・ファイルのシンボル・テーブル情報を表示します。
nm
--
シンボル・テーブル情報のみを表示します。
file
--
使用されているプログラミング言語など,指定されたファイルの一般的なプロパティに関する記述的な情報を表示します。
size
--
テキスト,データおよび bss セグメントのサイズを表示します。
dis
--
オブジェクト・ファイルを機械語命令に逆アセンブルを行います。
次の項では,上記のツールについて詳しく説明します。
また,オブジェクト・ファイルやバイナリ・ファイル中でプリント可能な文字列を探索する
string
コマンドの使用についての情報は,
strings
(1)2.7.1 ファイル内の選択した部分のダンプ (odump)
odump
ツールは,ヘッダ・テーブルおよび他のオブジェクト・ファイルやアーカイブ・ファイル内の選択した部分を表示します。
たとえば,ファイル
echo-args.o
に対して
odump
を使用すると,次のような情報が表示されます。
% odump -at echo-args.o ***ARCHIVE SYMBOL TABLE*** ***ARCHIVE HEADER*** Member Name Date Uid Gid Mode Size ***SYMBOL TABLE INFORMATION*** [Index] Name Value Sclass Symtype Ref echo-args.o: [0] main 0x0000000000000000 0x01 0x06 0xfffff [1] printf 0x0000000000000000 0x06 0x06 0xfffff [2] _fpdata 0x0000000000000000 0x06 0x01 0xfffff
詳細は,
odump
(1)2.7.2 シンボル・テーブル情報の表示 (nm)
nm
ツールは,オブジェクト・ファイルのシンボル・テーブル情報を表示します。
たとえば,nm
では,実行可能ファイル
a.out
を作成するためのオブジェクト・ファイルについて,次のような情報が表示されます。
% nm nm: Warning: - using a.out Name Value Type Size .bss | 0000005368709568 | B | 0000000000000000 .data | 0000005368709120 | D | 0000000000000000 .lit4 | 0000005368709296 | G | 0000000000000000 .lit8 | 0000005368709296 | G | 0000000000000000 .rconst | 0000004831842144 | Q | 0000000000000000 .rdata | 0000005368709184 | R | 0000000000000000
.
.
.
Name 欄にはシンボルまたは外部名,Value 欄にはシンボルのアドレスまたはデバッグ情報,Type 欄にはシンボルのタイプを示す英文字,Size 欄にはシンボルのサイズ (ソース・ファイルがデバッグ・オプション
-g
などでコンパイルされている場合のみ正確) がそれぞれ表示されています。
シンボル・タイプの英文字は,次のことを示しています。
B -- External zeroed data
D -- External initialized data
G -- External small initialized data
Q -- Read-only constants
R -- External read-only data
詳細は,
nm
(1)2.7.3 ファイル・タイプの決定 (file)
file
コマンドは,入力ファイルを読み取って各ファイルを検査し,タイプによって分類します。
ファイル・タイプは標準出力に書き込まれます。
file
コマンドは,/etc/magic
ファイルを使用して,マジック・ナンバを含むファイルを識別します。
マジック・ナンバは,ファイル・タイプを示す数字または文字列定数です。
次の例は,C ソース・ファイル,オブジェクト・ファイル,および実行可能ファイルを含むディレクトリに対して
file
を使用した場合の出力を示しています。
% file *.* .: directory ..: directory a.out: COFF format alpha dynamically linked, demand paged executable or object module not stripped - version 3.11-8 echo-args.c: c program text echo-args.o: COFF format alpha executable or object module not stripped - version 3.12-6
詳細は,
file
(1)2.7.4 ファイルのセグメント・サイズの決定 (size)
size
ツールは,指定されたオブジェクト・ファイルまたはアーカイブ・ファイルの text,data,および bss セグメントに関する情報を,8 進数,16 進数,または 10 進数で表示します。
たとえば,引数を指定しないで呼び出された場合,size
コマンドは
a.out
に関する情報を返します。
次のように,コマンド行に,オブジェクト・ファイルまたは実行可能ファイルの名前を指定することもできます。
% size text data bss dec hex 8192 8192 0 16384 4000 % size echo-args.o text data bss dec hex 176 96 0 272 110
詳細は,
size
(1)2.7.5 オブジェクト・ファイルの逆アセンブル (dis)
dis
ツールは,オブジェクト・ファイルのモジュールを機械語に逆アセンブルします。
たとえば,a.out
プログラムの逆アセンブルを行った場合,dis
コマンドは,次のような出力を作成します。
% dis a.out
.
.
.
_ _start: 0x120001080: 23defff0 lda sp, -16(sp) 0x120001084: b7fe0008 stq zero, 8(sp) 0x120001088: c0200000 br t0, 0x12000108c 0x12000108c: a21e0010 ldl a0, 16(sp) 0x120001090: 223e0018 lda a1, 24(sp)
.
.
.
詳細については,
dis
(1)2.8 標準 C ライブラリにおける ANSI 名前空間汚染のクリーンアップ
ANSI C 規格では,libc
にリンクされるユーザのプログラムは,プログラム内における一定範囲のグローバル識別子を,libc
内のグローバル識別子と競合したり,強制排除の問題を起こさないで使用できることが保証されています。
ANSI C 規格はグローバル識別子のある範囲を予約して,libc
が内部インプリメンテーションで使用できるようにしています。
これらは予約識別子と呼ばれ,ANSI の X3.159-1989 に定義されているように,次のものがあります。
下線で始まる外部識別子
下線で始まり,その後に大文字または下線が続く外部識別子
ANSI 準拠のプログラムでは,ANSI ルーチン名と同じか,または前述の予約されている名前空間にあてはまるグローバル識別子は定義できません。 その他のグローバル識別子名はすべて,ユーザ・プログラムで使用できます。
歴史的な
libc
のインプリメンテーションには,多数の ANSI でなく,予約されていない識別子が含まれています。
これらはどちらも文書化されサポートも行われています。
これらのルーチンは,ANSI であっても ANSI でなくても,libc
内から他の
libc
ルーチンによって呼び出されることがよくあります。
ユーザのプログラムで,このような ANSI でなく,予約されていない項目を独自に定義している場合には,libc
にある同名のルーチンを強制排除します。
このために,ユーザのプログラムが ANSI 準拠であっても,ANSI のものも ANSI でないものも,サポートされている
libc
ルーチンの動作が変わることがあります。
このような潜在的な競合は,ANSI 名前空間汚染と呼ばれます。
Tru64 UNIX の
libc
のインプリメンテーションには,文書化されサポートされている ANSI でなく,予約されていない多数のグローバル識別子が含まれています。
libc
内のこれらグローバル識別子を強制排除から保護し,ユーザの名前空間汚染を避けるため,これらの識別子の大多数の名前を,識別子名の前に 2 つの下線 (_ _) の付く予約されている名前空間に変更しています。
これらの項目への外部アクセスを維持するために,弱い識別子も,変更前の元の識別子名を使用して追加されています。
弱い識別子は,ファイル間のシンボリック・リンクのような働きをします。
弱い識別子が参照されると,代わりに強い識別子が使用されます。
libc
と静的にリンクするユーザ・プログラムは,弱い識別子用の余分のシンボル・テーブル・エントリを持つことがあります。
これらの識別子は,それぞれ対応する予約識別子と同じアドレスを持ち,このアドレスもテーブルに含まれています。
たとえば,静的にリンクされたプログラムが
libc
から
tzset()
関数を単に呼び出した場合,シンボル・テーブルにはこの呼び出しに対して,次の 2 つのエントリが含まれます。
# stdump -b a.out | grep tzset 18. (file 9) (4831850384) tzset Proc Text symref 23 (weakext) 39. (file 9) (4831850384) _ _tzset Proc Text symref 23
この例では,tzset
が弱い識別子であり,_ _tzset
が強い識別子です。
識別子
_ _tzset
が,実際に作業を行うルーチンです。
共用としてリンクされたユーザ・プログラムでは,シンボル・テーブルへのこのような追加は見られません。 これは,弱い識別子と強い識別子のペアがシェアード・ライブラリに残っているためです。
libc
から ANSI でなく,予約されていない識別子を参照している既存のユーザ・プログラムは,1 つの例外を除いて,この変更のためにリコンパイルする必要はありません。
つまり,libc
におけるこれらの識別子の強制排除に依存していたプログラムは,予約されていない名前を使用することによってそれらを強制排除することはできなくなります。
この種の強制排除は ANSI 準拠ではないため,使用しないでください。
ただし,これらの識別子を強制排除する機能は,新しく予約された "_ _" 名を使用すると,まだ利用できます。
この変更は,動的および静的に
libc
を使用する場合にあてはまります。
/usr/shlib/libc.so
/usr/lib/libc.a
デバッグ・プログラムを
libc
にリンクすると,弱いシンボルへの参照は,次のように強いシンボルへの参照として解決されます。
% dbx a.out dbx version 3.11.4 Type 'help' for help. main: 4 tzset (dbx) stop in tzset [2] stop in _ _tzset (dbx)
弱いシンボル
tzset
が参照されている場合,デバッガは代わりに強いシンボル
_ _tzset
で応答します。
これは,強いシンボルが実際に作業を行っているためです。
dbx
デバッガは,_ _tzset
が直接参照された場合と同様に動作します。
2.9 インライン・アセンブリ・コード -- ASM
コンパイラは,一般に ASM と呼ばれるインライン・アセンブリ・コードをサポートしています。
組み込み関数と同じように,ASM は関数呼び出しの構文で実装されています。
ただし組み込み関数と異なり,ASM を使用するためには,3 種類の ASM のプロトタイプとプリプロセッサ指示文
#pragma intrinsic
を含むヘッダ・ファイル
<c_asm.h>
をインクルードしなければなりません。
これらの関数のフォーマットは,次のとおりです。
_ _int64 asm(const char *, ...); /* for integer operations, like MULQ */ float fasm(const char *, ...); /* for single precision float instructions, like MULS */ double dasm(const char *, ...); /* for double precision float instructions, like MULT */ #pragma intrinsic (asm, fasm, dasm)
const char*
asm
,fasm
,dasm
関数への最初の引数には,インラインで生成される命令と,引数の解釈を記述するメタ言語が含まれています。
...
生成される命令に対するソースおよびデスティネーションの引数 (あれば) と,生成された命令で使用されるその他の値。
これらの値は,生成される命令からは,呼び出し基準の引数渡しについての通常の規約に従って利用可能になります (最初の整数引数はレジスタ
R16
で使用できます)。
ASM を使用する際は,ヘッダ・ファイル
<c_asm.h>
内の
#pragma intrinsic
指示文は必須です。
これは,次のようなことをコンパイラに知らせます。
これらの関数はユーザ定義関数ではない。
コンパイル時に,最初の引数を分析して文字列の内容で指定されたマシンコード命令を生成する,特別な ASM 処理を適用しなければならない。
引数の参照に関するメタ言語の形式は,次のとおりです。
<metalanguage_sequence> : <register_alias> | <register_number> | <register_macro> ; <register_number> : "$" number ; <register_macro> : "%" <macro_sequence> ; <macro_sequence> : number | <register_name> | "f" number | "F" number | "r" number | "R" number ; <register_name> : /* argument registers: R16-R21 */ "a0" | "a1" | "a2" | "a3" | "a4" | "a5" /* return value: R0 or F0, depending on type */ | "v0" /* scratch registers: R1, R22-R24, R28 */ | "t0" | "t1" | "t2" | "t3" | "t4" /* save registers: R2-R15 */ | "s0" | "s1" | "s2" | "s3" | "s4" | "s5" | "s6" | "s7" | "s8" | "s7" | "s8" | "s9" | "s10" | "s11" | "s12" | "s13" /* stack pointer: R30 */ | "sp" | "SP" | "$sp" | "$SP" | "RA" | "ra" /* return addr: R26 */ | "PV" | "pv" /* procedure value: R27 */ | "AI" | "ai" /* arg info: R25 */ | "FP" | "fp" /* frame pointer: R29 */ | "RZ" | "rz" | "zero" /* sink/source: R31 == zero */
構文上,メタ言語は命令シーケンス内のどの位置にも配置できます。
命令,オペランド,メタ言語を含むリテラル文字列は,次の一般形式に従っていなければなりません。
<string_contents> : <instruction_seq> | <string_contents> ";" <instruction_seq> | error | <string_contents> error ; <instruction_seq> : instruction_operand | directive ;
通常,instruction_operand は,アセンブリ言語命令と見なされます。 これは,コンマで区切られたオペランドのシーケンスとはホワイト・スペースで区切られます。
C 言語では,隣接した文字列リテラルを連結して単一の文字列にするため,連続した命令は,個々の命令がセミコロンで終わっていれば (例に示すように),1 行に 1 つづつ文字列として記述できます (通常のアセンブリ言語の記述方法と同じ)。
ASM には意味および構文上の規則があります。
ASM 呼び出しへの最初の引数は,メタ言語の形式で指定し,アセンブルされる命令であると解釈されます。 これは,コンパイル時にコンパイラが完全に理解できる内容でなければなりません。 したがって,必ずリテラル文字列 (またはリテラル文字列に展開されるマクロ) でなければならず,実行時に値が決まる文字列値を指定することはできません。 したがって,間接参照,テーブル参照,構造体の参照などは使用できません。
その他の引数は,通常の関数引数と同じように引数レジスタにロードされますが,ASM 呼び出しへの 2 番目の引数が,呼び出し標準での最初の引数として処理される点が異なります。
たとえば,次のテストでは 6 個の引数が引数レジスタの
a0
〜
a5
にロードされ,各部分式の結果はリターン値レジスタの
v0
に格納されます。
v0
は呼び出し標準のリターン値レジスタ (整数関数では
R0
) なので,最後の
MULQ
の結果は「呼び出し」から返される値になります。
if (asm("mulq %a0, %a1, %v0;" "mulq %a2, %v0, %v0;" "mulq %a3, %v0, %v0;" "mulq %a4, %v0, %v0;" "mulq %a5, %v0, %v0;", 1, 2, 3, 4, 5, 6) != 720){ error_cnt++; printf ("Test failed\n"); }
次の例は正しく動作しません。 浮動小数点のリターン値レジスタには値がロードされません。 また,引数は r2 ではなく引数レジスタにロードされるため,コンパイル時に,r2 が設定される前に使用されているという警告が表示されます。
z = fasm("mulq %r2, %a1 %r5", x=10, y=5);
正しく実行させるには,r2 の代わりに引数レジスタの番号を指定します。 上記の例を正しく記述すると,次のようになります。
z = fasm("mulq %a0, %a1, %a1;" "stq %a1, 0(%a2);" "ldt %f0, 0(%a2);" "cvtqf %f0, %f0;", x=10, y=5, &temp);
整数から浮動小数点レジスタへの変換に使用されるメモリ位置が,asm
のコードで使用できる点に注意してください。
これは,この目的のために C コード内で割り当てられた変数のアドレスを引数として渡すことで行います。
結果を期待どおりの場所に置くには,リターン値レジスタをメタ言語で指定しなければなりません。
引数とリターン値のない命令では,引数を省略します。 たとえば次のようになります。
asm("MB");