linux成长之路(gcc编译器、静态库、动态库)

Jeremy Lin

GCC简介

GCC(GNU Complier Collection)是GNU推出的功能强大、性能优越的多平台编译器套件,它包括了C、C++、Objective-C、Fortran、Java、Ada和Go语言的前端,也包括了这些语言的库,当前最新的版本是GCC 5.1。GCC可以在多种硬件平台上编译出可执行程序,其执行效率与一般的编译器相比平均效率要高20%-30%。GCC编译器能将C、C++语言源程序、汇程式程序和目标程序编译、连接成可执行文件,如果没有给出可执行文件的名字,GCC将生成一个名为 a.out 的文件。在Linux系统中,可执行文件没有统一的后缀,系统从文件的属性来区分可执行文件和不可执行文件。而GCC通过后缀来区分输入文件的类别,下面我们来介绍GCC所遵循的部分约定规则:

.c的文件                         C语言源代码文件;

.a的文件                        由目标文件构成的档案库文件;

.C,.cc或.cxx 的文件     C++源代码文件且必须要经过预处理;

.h的文件                         程序所包含的头文件;

.i 的文件                         C源代码文件且不应该对其执行预处理;

.ii的文件                         C++源代码文件且不应该对其执行预处理;

.m的文件                        Objective-C源代码文件;

.mm的文件                     Objective-C++源代码文件;

.o的文件                         编译后的目标文件;

.s的文件                         汇编语言源代码文件;

.S的文件                         经过预编译的汇编语言源代码文件。


GCC执行过程

虽然我们称gcc是C语言的编译器,但适用gcc由C语言源代码生成可执行文件的过程不仅仅是编译的过程,而是要经历4个相互关联的步骤:

(1)预处理(Preprocessing)

(2)编译(Compilation)

(3)汇编(Assembly)

(4)连接(Linking)


(1)预处理阶段:输入的是C语言的源文件,通常为*.c。它们通常带有.h之类的头文件。这个阶段主要是处理源文件中的#ifdef、#include和#define命令。该阶段会生成一个中间文件*.i,但实际工作中通常不会专门生成这种文件,因为基本上用不到;若非要生成这种文件,可以利用下面的命令:

gcc -E hello.c -o hello.i


hello.c的源代码如下:

#include <stdio.h>
int main(int argc, char**argv)
{
	printf("Hello Linux\n");
	return 0;
}


生成的hello.i的结果如下(很长,所以只截取一部分看看)

技术分享
……

……

技术分享


(2)编译阶段:输入的是中间文件*.i,编译后生成汇编语言文件*.s。这个阶段对应的GCC命令如下:

gcc -S hello.i -o hello.s

如下所示:

技术分享


(3)汇编阶段:输入的是汇编文件*.s,输出的转换生成的机器语言*.o。这个阶段对应的命令如下:

gcc -c hello.s -o hello.o


(4)连接阶段:将输入的机器代码文件*.s,汇集成一个可执行的二进制文件。命令如下:

gcc hello.o -o helloX

技术分享

技术分享


GCC基本用法与选项

在使用GCC编译器的时候,我们必须给出一系列必要的调用参数和文件名称。GCC编译器的调用参数大约有100多个,其中多数参数我们可能根本就用不到,这里只介绍其中最集中、最常用的参数。

GCC的基本用法:


gcc [options] [filenames]


其中options就是编译器所需要的参数,filenames给出相关的文件名称。

-c,  只编译,不连接成为可执行文件。编译器只是由输入的*.c等源代码文件生成*.o目标文件,通常用于编译不包含主程序的子程序文件;

-o output_filename,确定输出文件的名称为output_filename,同时这个名称不能和源文件同名。如果不给出这个选项,gcc给出预设的可执行文件a.out;

-g,产生符号调试工具(GNU的gdb)所必要的符号资讯,要想对源代码进行调试,我们必须加入这个选项;

-O,对程序进行优化编译、连接,采用这个选项,整个源代码会在编译、连接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,但是,编译、连接的速度相应的要慢一些;

-O2,比-O更好的优化编译、连接,相应的速度会更慢一些;

-Wall,编译警告选项,在编译过程中如果gcc遇到一些它认为可能发生错误的地方就会提出一些相应的警告和提示消息;

-I dirname,将dirname所指出的目录加入到程序头文件搜索目录列表中,是在预编译过程中使用的参数;

-S,编译指定的源文件,但不进行汇编;

-E,预处理指定的源文件,不进行编译;


编译静态库

静态库是一些目标代码的集合。Linux环境下的静态库目标文件一般是以 .a 作为目标文件的文件扩展名。

Linux 环境下使用 ar 命令创建一个静态库。静态库的优点在于使用简单,编译快速。静态库在应用程序生成时,已经编译成为可重定位的目标文件,因此可以不必要再编译,节省编译时间,以最短的时间生成可执行程序。而且,静态库在编译的时候已经复制到可执行程序的代码中,不需要像使用动态库那样再程序加载时再次链接。在理论上,可执行程序使用静态库,要比使用动态库速度快1%~5%。

(1)创建静态库

假设有一源代码 static_lib.c:

int add( int a, int b )
{
	return a + b;
}
int sub( int a, int b )
{
	return a - b;
}
int mul( int a, int b )
{
	return a * b;
}
int div( int a, int b )
{
	return a / b;
}
现在要将其编译成为静态库文件——static_lib.a,则可使用如下的命令:

gcc -c static_lib.c
ar rcs static_lib.a static_lib.c
其中 ar 可以将目标文件加入到一个已经存在的静态库中(若不存在则创建),rcs 参数分别表示:把列表中的目标文件加入到静态库中(参数r),若指定静态库不存在,则创建该静态库文件(参数c),最后更新静态库文件的索引,使之包含新加入的目标文件的内容(参数s)。

技术分享


(2)使用静态库

静态库创建成功后,需要链接到应用程序中使用。应用程序可以引用该库中的函数或者全局变量。为了使应用程序可以正确引用该库中的全局符号,需要制作一个包含该静态库中全局符号声明的文件。这个头文件可以包含在应用程序的头文件中,这样就可以引用静态库中的全局符号了。

头文件static_lib.h

extern int add( int a, int b);
extern int sub( int a, int b );
extern int mul( int a, int b );
extern int div( int a, int b );
应用程序main.c:

#include <stdio.h>
#include "static_lib.h"

int main(void)
{
	int a, b;

	printf("please input a and b \n");
	scanf( "%d%d", &a, &b );
	printf("the add:  %d", add(a,b));
	printf("the sub:  %d", sub(a,b));
	printf("the mul:  %d", mul(a,b));
	printf("the div:  %d", div(a,b));
	return 0;
}
我们使用 gcc 的 -l 选项来指定静态库,或者使用 -L 参数来指定库文件的搜索路径。-l 和 -L 之后都直接带参数而不跟空格。因而,对于本例,编译命令如下:

gcc main.c -lstatic_lib.a -o app
或者使用 “ . ” 作为静态库的搜索路径,表示在当前目录下搜索需要的静态库文件。

gcc -L. main.c -o app
注意:-l 是链接器选项,一定要放在被编译的源文件的文件名之后。

gcc 也支持使用-static 选项对静态库进行链接。

gcc main.c -static ./static_lib.a -o app
技术分享


编译动态库

动态库又称为共享库或者动态链接库,现代程序中大量地使用动态链接库。例如,Windows 环境中的dll文件和Linux环境下的 so文件。动态库是在程序启动时被装载的。当一个程序装载了一个动态库后,其他应用程序仍然可以装载同一个动态库。

(1)创建动态库

假设有一份需要转化成动态库的代码share_lib.c

void insert_sort( int *array, int length)
{
	int i, j;
	for (i = 1; i < length; ++i)
	{
		int temp;
		j = i;
		temp = array[j];

		while(j > 0){
			if (temp < array[j - 1])
			{
				array[j] = array[j - 1];
				j--;
			}else
			break;
		}
		array[j] = temp;
	}
}


int binary_search( int *array, int item, int length)
{
	int high, low, mid;
	high = length - 1;
	low = 0;
	mid = (high + low) / 2;

	while(low <= high)
	{
		if (array[mid] > item)
		{
			high = mid;
			mid = (high + low) / 2;
		}else if ( array[mid] < item)
		{
			low = mid;
			mid = (high + low)/2;
		}
		else
			return mid;
	}
return -1;
}

Linux使用gcc创建一个动态库。由于动态库可以被多个进程共享加载,所以需要生成位置无关的目标文件。这时需要使用gcc编译器的-fPIC选项,该选项用于生成位置无关的代码。除了需要使用-fPIC选项外,还需要使用-shared 选项,该选项将位置无关的代码制作为动态库。

gcc -shared -fPIC share_lib.so share_lib.c

技术分享


(2)使用动态库

动态库创建完后,应用程序可以引用该库中的函数或全局变量。为了使应用程序可以正确引用该库中的全局符号,需要制作一个包含该动态库中全局符号声明的头文件。

如share_lib.h

extern void insert_sort( int *array, int length);
extern int binary_search( int *array, int item, int length);
使用动态库的main.c

#include <stdio.h>
#include "share_lib.h"

 int main(int argc, char const *argv[])
{
	int array[5] = {5,4,2,3,1};
	int item;
	int pos;

	insert_sort( array, 5);

	printf("please input a number\n");
	scanf("%d", &item);
	pos = binary_search( array, item, 5);

	if (pos == -1)
	{
	        printf("can't find\n");
	}else
	{
                      printf("the position is %d\n", pos+1);
	}

	return 0;
}

使用如下命令创建程序:

gcc main.c ./share_lib.so -o app

技术分享
技术分享



本文地址:http://blog.csdn.net/linj_m/article/details/45039347

更多资源请关注 博客:LinJM-机器视觉  微博:林建民-机器视觉



郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。