共用体とは、同じメモリ領域を複数の型で共有するためのデータ構造である。たとえば、
#include <stdio.h>
int main(void){
union {float f; int i;} x;
x.i = 10;
printf("%d\n", x.i);
x.f = 0.01;
printf("%d\n", x.i);
return 0;
}
のように用いる。この場合、2回目の実行結果は10とはならない(確認してみよ)。これは、x.fとx.iは同じメモリ領域を共有して用いているために、x.fに0.01を代入した際にx.iの結果が上書きされてしまったためである。
gccを用いてコンパイルを行う場合、gccは1.プリプロセス処理、2.アセンブリで書かれたプログラムの作成、3.オブジェクトファイルの作成、4.実行ファイルの作成(リンク)という4つの手順を踏んでいる。gccに適切なオプションを付ける事により、これらの段階を途中で止める事が可能である。(RISC-V環境向けにクロスコンパイルする場合は、gccの代わりにriscv64-unknown-elf-gccを用いるが、下記の説明内容に変化はない。)
gcc -E test.c
マクロの展開や#で始まる行の処理、コメントの削除といったプリプロセスを行う。結果は標準出力から出力される。(なお、リダイレクトを用いることで標準出力先を変更する事が出来る。例えば、gcc -E test.c > test1.cなどとすると、プリプロセスの結果がtest1.cにて得られる)
gcc -S test.c
C言語からアセンブリプログラムへのコンパイルが行われる。アセンブリプログラムはtest.sとして作成される。
gcc -c test.s
アセンブリプログラムからオブジェクトファイル(機械語で書かれたコード+α)の作成が行われる。オブジェクトファイルはtest.oとして作成される。
gcc -o test test.o
オブジェクトファイルから実行ファイルの作成(リンク)が行われる。(複数のオブジェクトファイルを結合(リンク)する事で実行ファイルが得られる。手順3で作成されたオブジェクトファイルは1つではないのか?という疑問を抱くと思うが、例えばprintfなどの関数は既存のライブラリに依存しており別のオブジェクトファイルとして存在するため、それらをリンクする必要がある。)
単にgcc -o test test.cとすると、1-4までの全てのステップが同時に行われ、中間ファイル(.sや.oのファイル)は残らない。