/* main.c */
int sum(int *a, int n);
int array[2] = {1, 2};
int main()
{
int val = sum(array, 2);
return val;
}
/* sum.c */
int sum(int *a, int n)
{
int i, s = 0;
for (i = 0; i < n; i++) {
s += a[i];
}
return s;
}
示例程序由main.c和sum.c两个源文件组成,main函数初始化一个整数数组,然后调用sun函数对数组元素求和。
gcc -c main.c sum.c
/* 编译、汇编到目标代码,生成可重定位目标文件 */
ld -o myprog main.o sum.o
/* 链接文件,生成可执行目标文件myprog */
./myprog
/* 运行可执行目标文件myprog */
目标文件有三种形式:
现代x86-64 Linux和Unix系统 使用可执行可链接格式(ELF)
ELF头 描述了生成该文件的系统的字的大小和字节顺序。其中包括ELF头的大小、目标文件的类型(如可重定位、可执行、共享的)、机器类型(如x86-64)、节头部表的文件偏移,以及节头部表中条目的大小和数量。
部分节的具体内容:
.text:已编译程序的机器代码。
.rodata:只读数据。
.data:已初始化的全局和静态C变量。
.bss:未初始化的全局和静态C变量,在目标文件中这个节不占据实际的空间,它仅仅是一个占位符。
.symtab:符号表
注意:局部C变量在运行时被保存在栈中,既不出现在.data节中,也不出现在.bss节中。
全局符号的强、弱
强符号:函数名和已初始化的全局变量名
弱符号:未初始化的全局变量名
所以编写程序时不到万不得已不要定义全局符号。
/* fun1.c */
int addcnt = 0;
void addvec(int *x, int *y,
int *z, int n)
{
int i;
addcnt++;
for (i = 0; i < n; i++)
z[i] = x[i] + y[i];
}
/* fun2.c */
int multcnt = 0;
void multvec(int *x, int *y,
int *z, int n)
{
int i;
multcnt++;
for (i = 0; i < n; i++)
z[i] = x[i] * y[i];
}
使用AR工具创建函数的静态库
gcc -c fun1.c fun2.c
ar rcs libvector.a fun1.o fun2.o
编写应用main2.c来使用这个库。
/* main2.c */
#include
#include "vector.h"
int x[2] = {1, 2};
int y[2] = {3, 4};
int z[2];
int main()
{
addvec(x, y, z, 2);
printf("z = [%d %d]\n", z[0], z[1]);
return 0;
}
接下来需要编译和链接文件main2.o和libvector.a
gcc -c main2.c
gcc -static -o myprog2 main2.o ./libvector.a
/* 静态库放在最后 */
或者等价地使用
gcc -c main2.c
gcc -static -o myprog2 main2.o -L. -lvector
静态库很有用,但也容易引起迷惑,因为链接时会扔掉库里没有使用的内容。
假设调用关系如下:
func.o → libx.a 和liby.a 中的函数
libx.a →libz.a 中的函数
libx.a 和liby.a 之间、liby.a 和libz.a 相互独立
则以下几个命令行都是可行的:
gcc -static -o myfunc func.o libx.a liby.a libz.a
gcc -static -o myfunc func.o liby.a libx.a libz.a
gcc -static -o myfunc func.o libx.a libz.a liby.a
假设调用关系如下:
func.o → libx.a 和liby.a 中的函数
libx.a →liby.a 同时liby.a →libx.a
则以下命令行可行:
gcc -static -o myfunc func.o libx.a liby.a libx.a