make、Makefile &静态库、动态库

场景说明:
文件如下,假设max.c和min.c是两个人分别设计实现的接口函数,当我们要使用其功能时,并不在意其实现细节。因此我们只要按照头文件max.h、min.h中的接口声明来调用函数功能即可。

max.c

int max(int a, int b)
{
        if(a>b){
                return a;
        }else{
                return b;
        }
}

min.c

int min(int a, int b)
{
        if(a

max.h

int max(int a, int b);

min.h

int min(int a, int b);

我们编写如下main.c并执行,实现相应功能:

#include 
#include "max.h"
#include "min.h"

int main()
{
	    int a,b;
        scanf("%d %d",&a,&b);
        int maxNum=max(a,b);
        int minNum=min(a,b);
        printf("maxNum=%d\nminNum=%d\n",maxNum,minNum);
}
  
# gcc max.c min.c main.c -o main.out
# ./main.out 
2 3
maxNum=3
minNum=2

整个过程包括如下几个步骤:
make、Makefile &静态库、动态库_第1张图片

但是当源代码很多很多时,每次执行时编译得到可执行文件可能耗时很长,故我们需要将不常修改的文件提前编译成 .o文件:

# gcc -c max.c -o max.o
# gcc -c min.c -o min.o
# gcc max.o min.o main.c -o main.out
# ./main.out 
2 3
maxNum=3
minNum=2

多数UNIX平台都通过cc调用它们的C编译程序。makefile 中我们经常能看到cc, cc 是一个全局变量,它指定你的makefile所用的编译器,一般默认是gcc 。基本的编译命令有以下几种:

1.  -c     编译产生对象文件(*.obj)而不链接成可执行文件,当编译几个独立的模块,而待以后由链接程序把它们链接在一起时,就可以使用这个选项,如:
$cc -c hello.c ===> hello.o
$cc hello.o

2.  -o     允许用户指定输出文件名,如
$cc hello.c -o hello.o
or
$cc hello.c -o hello

3.  -g    指明编译程序在编译的输出中应产生调试信息.这个调试信息使源代码和变量名引用在调试程序中或者当程序异常退出后在分析core文件时可被使用.

4.   -D   允许从编译程序命令行定义宏符号
一共有两种情况:一种是用-DMACRO,相当于在程序中使用#define MACRO,另一种是用-DMACRO=A,相当于程序中的#define MACRO A.如对下面这代码:
#ifdefine DEBUG
printf("debug message\n");
#endif
编译时可加上-DDEBUG参数,执行程序则打印出编译信息

5.   -I   可指定查找include文件的其他位置.例如,如果有些include文件位于比较特殊的地方,比如/usr/local/include,就可以增加此选项如下:
$cc -c -I/usr/local/include -I/opt/include hello.c 此时目录搜索会按给出的次序进行.

6. -E   这个选项是相对标准的,它允许修改命令行以使编译程序把预先处理的C文件发到标准输出,而不实际编译代码.在查看C预处理伪指令和C宏时,这是很有用的.可能的编译输出可重新定向到一个文件,然后用编辑程序来分析:
$cc -c -E hello.c >cpp.out       此命令使include文件和程序被预先处理并重定向到文件cpp.out.以后可以用编辑程序或者分页命令分析这个文件,并确定最终的C语言代码看起来如何.

7. -o   优化选项,     这个选项不是标准的
-O和 -O1指定1级优化
-O2 指定2级优化
-O3 指定3级优化
-O0指定不优化
$cc -c O3 -O0 hello.c  当出现多个优化时,以最后一个为准!!

8. -Wall  以最高级别使用GNU编译程序,专门用于显示警告用!!
$gcc -Wall hello.c

9.   -L指定连接库的搜索目录,-l(小写L)指定连接库的名字
$gcc main.o -L/usr/lib -lqt -o hello
上面的命令把目标文件main.o与库qt相连接,连接时会到/usr/lib查找这个库文件.也就是说-L与-l一般要成对出现.

如果每次都要该指令输入显然过于繁琐,我们可以通过make工具快捷地得到可执行文件。首先需要编写如下Makefile文件:

make、Makefile &静态库、动态库_第2张图片

左边是输出文件,右边是输入文件以及指令,然后只需要通过make命令执行便可:

# make
gcc -c max.c 
gcc -c min.c
gcc max.o min.o main.c -o main.out
# ./main.out 
2 3
maxNum=3
minNum=2

如果只修改了某个源文件,只需要再次make一下,并且其他未变的文件不会再编译,可以大大提高效率。

静态库与动态库

为了更好地对大量源文件进行整理,我们可以将所有编译过的.o文件生成一个库,他人同样只需要按照头文件中的接口声明来调用库功能,而不必关心接口怎么实现的,编译器会从库中提取相应的代码。

分类:

  • 静态库:程序编译阶段直接加入库
  • 动态库:程序运行时加载

命名规则:

  • lib静态库名.a
  • lib动态库名.so

静态库的创建及使用如下:

# ar cr libmyfunc.a  *.o
# gcc -o main.out main.c -L. -lmyfunc
# ./main.out 
2 3
maxNum=3
minNum=2

注意,库文件名必须为libxxx.a,这样 我们在链接这个库时就可以用 -lxxx

其中参数 -L. 表示在当前目录下找库文件,myfunc为静态库名。

rm libmyfunc.a 后,执行./main.out仍然可以执行,因为接口函数已经链接到目标文件main.out中了。

现在我们只需要把 libmyfunc.a 和相应的头文件发给其他开发人员,其他开发人员就可以按照头文件中的接口声明来调用库功能了。

动态库的创建及使用如下:

# gcc -shared -fPIC -o libmyfunc.so *.o
# gcc -o main.out main.c -L. -lmyfunc
# ./main.out 
2 3
maxNum=3
minNum=2

其中参数 -fPIC 用于产生与位置无关的共享代码,静态库与动态库重名时会优先寻找动态库。

注:当提示找不到动态库文件libmyfunc.so时:

方法1:将动态库文件放入lib目录下
# cp libmyfunc.so /usr/lib 或 /lib
# ldconfig 

方法2:将当前目录追加到/etc/ld.so.conf文件中
# pwd >> /etc/ld.so.conf
# ldconfig

方法3:设置环境变量
# export LD_LIBRARY_PATH=/usr/lib:$LD_LIBRARY_PATH

同样地,可以写个简单的Makefile,以静态库为例:

make、Makefile &静态库、动态库_第3张图片

运行 make build,将会构建libmyfunc.a
运行make test,将会生成链接libmyfunc.a的程序main.out

automake和autoconf

当然,当工程很大时,编译会涉及很多的依赖项,还要进行必要的检查,这时我们就需要借助一些工具来生成Makefile了。automake和autoconf是非常有用的用来发布C程序的东西。

通过automake和autoconf产生的程序安装步骤如下:

./configure    用来检测你的安装平台的目标特征,比如它会检测你是不是有CC或GCC
make           用来编译,它从Makefile中读取指令,然后编译
make install   用来安装,它也从Makefile中读取指令,安装到指定的位置

具体方式见文章:
http://blog.csdn.net/qq_15437629/article/details/77587588

Cmake

cmake的亮点在于编译复杂项目上的应用 —— cmake是一个跨平台的Makefile 生成工具!

测试目录如下:

.
├── bin
├── CMakeLists.txt
├── main.c
├── h
│   ├── max.h
│   └── min.h
└── so
    ├── CMakeLists.txt
    ├── max.c
    └── min.c

执行也可以达到目的:

# cmake .
# make
# ls ./bin/
libutils_so.so  main
# ./bin/main
1 6
maxNum=6
minNum=1

最外层CMakeLists.txt:

cmake_minimum_required(VERSION 3.13) # 指定cmake最低版本

SET(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}")
SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin) # 指定输出路径
SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)


set(CMAKE_C_FLAGS "-g -O2") # 增加编译选项,也可以用下列方式,不需要"":
add_compile_options(-g -O2)

add_subdirectory(so) #进入下一级CMakeLists解析,必须在工程定义前

PROJECT(main) # 指定项目名称(这个名称是任意的)

SET(SRC_LST main.c)

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/h) # 头文件目录

ADD_EXECUTABLE(main ${SRC_LST}) # 第一个参数表示生成可执行文件的文件名,第二个参数用于指定源文件
TARGET_LINK_LIBRARIES(main utils_so) # 定义依赖库
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/so) # 定义依赖库搜索路径

so/CMakeLists.txt:

PROJECT(utils_so) # 指定项目名称(这个名称是任意的)
SET(SRC_LST max.c min.c)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../h) # 头文件目录
#set(CMAKE_C_FLAGS "-I ${CMAKE_CURRENT_SOURCE_DIR}/../h") # 头文件目录

ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_LST}) # 生成动态库

https://blog.csdn.net/qwq1503/article/details/85866602

你可能感兴趣的:(C语言进阶,c语言,动态库,静态库,automake,Makefile)