int sub(int a, int b);

#endif

可以看出上面这些代码文件都是非常简单的,但包括了基本的依赖关系,便于我们整体把握项目,而不拘泥于细节。


#### 1. 在vscode中用命令行进行编译(gcc和Makefile)


如果不适用Makefile的话,在源文件不是很多的时候是可行的,在本例中,编译命令为:

gcc main.c add.c sub.c -o out.exe //编译生成可执行文件
./out.exe //运行生成的可执行文件
打印结果为:

do add
 i + j = 3
 do sub
 i - j = -1
如果源代码文件一多的话,我们一般就需要通过Makefile来管理了,Makefile的写法我就不细说了,大家可以网上找资料学习学习。


首先本例中的makefile如下:
a : main.o add.o sub.o
 gcc -o a.exe main.o add.o sub.o
 main.o : main.c add.h sub.h data.h
 gcc -c main.c
 add.o : add.c add.h data.h
 gcc -c add.c
 sub.o : sub.c sub.h data.h
 gcc -c sub.c
在命令行中输入

make
命令行中会显示:
gcc -c main.c
gcc -c add.c
gcc -c sub.c
gcc -o a main.o add.o sub.o
在键入./a.exe
命令行中会显示如下结果,和上面是完全一样的。
do add
i + j = 3
do sub
i - j = -1

#### 2. 在DEVC++中编译


直接新建项目


![image-20210630225458895]()


把文件全部添加进去


![image-20210630225546203]()


直接点击编译运行即可


![image-20210630225728027]()


看起来IDE编译运行只需要一个按钮,实际上IDE也是帮我们默默的维护一个Makefile,在该项目目录下就存在一个Makefile文件,打开其内容如下:

Project: 项目1

Makefile created by Dev-C++ 5.11

CPP = g++.exe
 CC = gcc.exe
 WINDRES = windres.exe
 OBJ = add.o main.o sub.o
 LINKOBJ = add.o main.o sub.o
 LIBS = -L"D:/Dev-Cpp/MinGW64/lib" -L"D:/Dev-Cpp/MinGW64/x86_64-w64-mingw32/lib" -static-libgcc
 INCS = -I"D:/Dev-Cpp/MinGW64/include" -I"D:/Dev-Cpp/MinGW64/x86_64-w64-mingw32/include" -I"D:/Dev-Cpp/MinGW64/lib/gcc/x86_64-w64-mingw32/4.9.2/include"
 CXXINCS = -I"D:/Dev-Cpp/MinGW64/include" -I"D:/Dev-Cpp/MinGW64/x86_64-w64-mingw32/include" -I"D:/Dev-Cpp/MinGW64/lib/gcc/x86_64-w64-mingw32/4.9.2/include" -I"D:/Dev-Cpp/MinGW64/lib/gcc/x86_64-w64-mingw32/4.9.2/include/c++"
 BIN = 项目1.exe
 CXXFLAGS = $(CXXINCS)
 CFLAGS = $(INCS)
 RM = rm.exe -f.PHONY: all all-before all-after clean clean-custom
all: all-before $(BIN) all-after
clean: clean-custom
 ${RM} $(OBJ) $(BIN)$(BIN): $(OBJ)
 $(CPP) $(LINKOBJ) -o $(BIN) $(LIBS)add.o: add.c
 $(CPP) -c add.c -o add.o $(CXXFLAGS)main.o: main.c
 $(CPP) -c main.c -o main.o $(CXXFLAGS)sub.o: sub.c
 $(CPP) -c sub.c -o sub.o $(CXXFLAGS)
可以看出,DEVC++也是用gcc和Makefile来帮我们管理项目的。


#### 3. 使用visual studio来编译项目


创建一个空项目


![image-20210630230919379]()


把文件都添加进去,如下图所示


![image-20210630230959895]()


点击调试->开始执行(不调试)


![image-20210630231122063]()


结果如下图所示


![image-20210630231230332]()


和前面的结果是一样的。


上面用了在Windows上常见的几种编译C语言项目的方法,可以发现用gcc和Makefile来编译项目是最灵活的,但是有一些使用门槛。而使用编译器可以更加方便快捷的来编译项目。两种方法各有优劣,大家则需选择,建议两种方法都要会用。




---


此外上面我们演示的是编译生成可执行文件的例子,但很多时候,我们的项目只需要编译打包成库文件提供给别人使用,因此我们要生成的就不是可执行文件,而是库文件。


那么库文件又是什么呢?


上面使用Makefile编译的时候,编译生成可执行文件其实是分成了两步。


第一步将每一个.c文件及其包含的.h文件编译生成.o文件(也叫目标文件)。


然后将所有的.o文件链接起来生成可执行文件。


将一个或多个目标文件打包起来就是库文件(目标文件的仓库)。


库文件也分为静态库和动态库,具体区别可自己查询。


#### 4. 下面演示一下用gcc命令行来生成静态库和动态库。


在我们之前项目的基础上,假如我么要增加一个乘法功能,我们想使用一个乘法开源库。(实际上我们是可以直接用乘法的,这边知识为了演示的方便所以选择了一个简单的开源库,方便说清楚)


这个乘法开源库只有两个文件(一般开源库是不会这么简单的,这里我们是为了演示的方便)


-----mult.c


-----mult.h


文件内容为:


mult.c
#include <stdio.h>
 #include “data.h”
 #include “mult.h”int mult(int a, int b) {
 printf(“do mult\n”);
 return a*b;
 }
mult.h
#ifndef _MULT_H
 #define _MULT_Hint mult(int a, int b);
#endif
下面我们要尝试将这个演示用的开源库(mult.c mult.h)编译成静态库和动态库文件。


使用gcc编译开源库成库文件同样可以使用Makefile,但因为这边源文件较少,我们就直接用gcc命令行来编译该开源库。


生成静态库:


命令行中依次输入

gcc -c mult.c //会生成mult.o文件
ar rcs libmult.a mult.o //将mult.o打包成libmult.a静态库文件

生成动态库:


命令行中依次输入

gcc -fpic -shared mult.c -o dllmult.so //生成dllmult.so动态库文件

静态库和动态库文件的使用


在编译好静态库或者动态库之后,我们只需要将库文件加头文件给需要使用这个开源库的项目就可以,那么我们之前的项目要怎么使用这个演示用的开源库呢?


静态库文件的使用


当我们把libmult.a和mult.h文件拿到之后,我们通过mult.h文件可以知道这个库向外提供的接口(函数),我们发现正好有我们需要的乘法功能,接口为int mult(int a, int b);


我们修改main.c文件成下图所示
#include <stdio.h>
 #include “add.h”
 #include “sub.h”
 #include “data.h”
 #include “mult.h”extern int i;
 extern int j;int main(){
 printf(“i + j = %d\n”,add(i,j));
 printf(“i - j = %d\n”,sub(i,j));
 printf(“i * j = %d\n”,mult(i,j));//增加该行
 return 0;
 }
要使用静态库,直接把libmult.a加入链接即可。


命令行中输入
gcc -c main.c add.c sub.c //生成main.o add.o sub.o目标文件
 gcc main.o add.o sub.o libmult.a -o a.exe//将上面生成的目标文件和静态库链接起来,生成可执行文件
 ./a.exe //执行可执行文件,输出结果如下所示
 do add
 i + j = 3
 do sub
 i - j = -1
 do mult
 i * j = 2
动态库文件的使用


命令行中输入
gcc main.o add.o sub.o dllmult.so -o a.exe
 ./a.exe //输出结果如下所示
 do add
 i + j = 3
 do sub
 i - j = -1
 do mult
 i * j = 2
#### 5. 顺便我们来用DEVC++和VisualStudio来演示一下如何生成库文件。


DEVC++生成静态库


新建项目,可以看到存在static library和DLL选项,分别对应静态库和动态库。


![image-20210701002830797]()


将开源库文件mult.c和mult.h加入到项目中


![image-20210701003315051]()


点击编译一下,就可以在项目目录下发现.a静态库文件啦。


![image-20210701003236468]()


此外我们还可以看看编译静态库文件的Makefile文件是怎样的,如下图

Project: 项目3

Makefile created by Dev-C++ 5.11
CPP = g++.exe
 CC = gcc.exe
 WINDRES = windres.exe
 OBJ = mult.o
 LINKOBJ = mult.o
 LIBS = -L"D:/Dev-Cpp/MinGW64/lib" -L"D:/Dev-Cpp/MinGW64/x86_64-w64-mingw32/lib" -static-libgcc
 INCS = -I"D:/Dev-Cpp/MinGW64/include" -I"D:/Dev-Cpp/MinGW64/x86_64-w64-mingw32/include" -I"D:/Dev-Cpp/MinGW64/lib/gcc/x86_64-w64-mingw32/4.9.2/include"
 CXXINCS = -I"D:/Dev-Cpp/MinGW64/include" -I"D:/Dev-Cpp/MinGW64/x86_64-w64-mingw32/include" -I"D:/Dev-Cpp/MinGW64/lib/gcc/x86_64-w64-mingw32/4.9.2/include" -I"D:/Dev-Cpp/MinGW64/lib/gcc/x86_64-w64-mingw32/4.9.2/include/c++"
 BIN = 项目3.a
 CXXFLAGS = $(CXXINCS)
 CFLAGS = $(INCS)
 RM = rm.exe -f.PHONY: all all-before all-after clean clean-custom
all: all-before $(BIN) all-after
clean: clean-custom
 ${RM} $(OBJ) $(BIN)$(BIN): $(LINKOBJ)
 ar r $(BIN) $(LINKOBJ)
 ranlib $(BIN)mult.o: mult.c
 $(CPP) -c mult.c -o mult.o $(CXXFLAGS)
DEVC++使用静态库


![image-20210701004617542]()


![image-20210701005339598]()


#### 6. VisualStudio创建静态库和VisualStudio使用静态库


这方面的内容我就不直接演示了,直接分享写的比较好的博客吧。


[VS2010/2013下生成并使用静态库]( )


[以OpenCV库为例讲解如何在VS中配置第三方动态库]( )


[在自己的项目中调用别人的库的方法(static lib库,dynamic lib库以及dll动态库)]( )


[C语言函数库:动态链接库与静态链接库]( )





---


### 3. 找几个开源库来实战演练一下


优秀的C语言开源库有不少,因此这篇文章我也会一直更新,每次遇到有意思的C语言开源库的编译使用都会放在这里。




---


#### 1. zlog的编译使用


项目GitHub地址:[zlog]( )


中文手册:[手册]( )


zlog在Windows上的移植:[WinZlog]( )


将zlog的源码下载下来,打开,可以看到目录结果如下图所示


![image-20210701202301523]()


一般来说,对于一个C语言项目,最重要的就是其源码和编译规则。


对应到上面目录中的文件就是src目录和makefiel,src目录中就是源代码文件,makefile就是编译规则,我们只需要根据makefile就可以编译出我们想要的库文件。


下面是该开源库带的makefile:

zlog makefile

Copyright © 2010-2012 Hardy Simpson HardySimpson1984@gmail.com

This file is released under the LGPL 2.1 license, see the COPYING file

OBJ= 
 buf.o 
 category.o 
 category_table.o 
 conf.o 
 event.o 
 format.o 
 level.o 
 level_list.o 
 mdc.o 
 record.o 
 record_table.o 
 rotater.o 
 rule.o 
 spec.o 
 thread.o 
 zc_arraylist.o 
 zc_hashtable.o 
 zc_profile.o 
 zc_util.o 
 zlog.o
 BINS=zlog-chk-conf
 LIBNAME=libzlogZLOG_MAJOR=1
 ZLOG_MINOR=2

Fallback to gcc when $CC is not in $PATH.

CC:=$(shell sh -c 'type $(CC) >/dev/null 2>/dev/null && echo vscode如何配置仓库地址_vscode如何配置仓库地址(OPTIMIZATION) -fPIC -pthread $(CFLAGS) $(WARNINGS) vscode如何配置仓库地址_vscode如何配置仓库地址_02(LDFLAGS) -pthread

DYLIBSUFFIX=so
STLIBSUFFIX=a
DYLIB_MINOR_NAME=vscode如何配置仓库地址_c语言_03(DYLIBSUFFIX).vscode如何配置仓库地址_vscode如何配置仓库地址_04(ZLOG_MINOR)
DYLIB_MAJOR_NAME=vscode如何配置仓库地址_c语言_03(DYLIBSUFFIX).vscode如何配置仓库地址_vscode如何配置仓库地址_06(LIBNAME).vscode如何配置仓库地址_静态库_07(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) vscode如何配置仓库地址_c语言_08(LIBNAME).$(STLIBSUFFIX)
STLIB_MAKE_CMD=ar rcs $(STLIBNAME)

Installation related variables

PREFIX?=/usr/local
INCLUDE_PATH=include
LIBRARY_PATH=lib
BINARY_PATH=bin
INSTALL_INCLUDE_PATH= vscode如何配置仓库地址_c语言_09(INCLUDE_PATH)
INSTALL_LIBRARY_PATH= vscode如何配置仓库地址_c语言_09(LIBRARY_PATH)
INSTALL_BINARY_PATH= vscode如何配置仓库地址_c语言_09(BINARY_PATH)

Platform-specific overrides

uname_S := $(shell sh -c ‘uname -s 2>/dev/null || echo not’)
compiler_platform := vscode如何配置仓库地址_静态库_12(CC) --version|grep -i apple’)

ifeq ($(uname_S),SunOS)

REAL_LDFLAGS+= -ldl -lnsl -lsocket

DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS)
INSTALL= cp -r
endif

For Darwin builds, check the compiler platform above is not empty. The covers cross compilation on Linux

ifneq (vscode如何配置仓库地址_静态库_13(LIBNAME).vscode如何配置仓库地址_vscode如何配置仓库地址_04(ZLOG_MINOR).vscode如何配置仓库地址_c++_15(LIBNAME).vscode如何配置仓库地址_vscode如何配置仓库地址_04(DYLIBSUFFIX)
DYLIB_MAKE_CMD=$(CC) -dynamiclib -install_name vscode如何配置仓库地址_vscode如何配置仓库地址_17(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
REAL_CFLAGS+= -D_DARWIN_C_SOURCE
endif

ifeq ($(uname_S),AIX)
this logic of minor major is not relevant on AIX or at least not widely used
not to mention dynamic linker .a preference…
DYLIB_MAKE_CMD=$(CC) -shared -Wl,-G,-b64 -maix64 -pthread -o $(DYLIBNAME) $(LDFLAGS)
 REAL_CFLAGS+= -maix64
 STLIB_MAKE_CMD=OBJECT_MODE=64 ar rcs $(STLIBNAME) $(DYLIB_MAJOR_NAME)
 endifall: $(DYLIBNAME) $(BINS)
Deps (use make dep to generate this)
buf.o: buf.c zc_defs.h zc_profile.h zc_arraylist.h zc_hashtable.h 
 zc_xplatform.h zc_util.h buf.h
 category.o: category.c fmacros.h category.h zc_defs.h zc_profile.h 
 zc_arraylist.h zc_hashtable.h zc_xplatform.h zc_util.h thread.h event.h 
 buf.h mdc.h rule.h format.h rotater.h record.h
 category_table.o: category_table.c zc_defs.h zc_profile.h zc_arraylist.h 
 zc_hashtable.h zc_xplatform.h zc_util.h category_table.h category.h 
 thread.h event.h buf.h mdc.h
 conf.o: conf.c fmacros.h conf.h zc_defs.h zc_profile.h zc_arraylist.h 
 zc_hashtable.h zc_xplatform.h zc_util.h format.h thread.h event.h buf.h 
 mdc.h rotater.h rule.h record.h level_list.h level.h
 event.o: event.c fmacros.h zc_defs.h zc_profile.h zc_arraylist.h 
 zc_hashtable.h zc_xplatform.h zc_util.h event.h
 format.o: format.c zc_defs.h zc_profile.h zc_arraylist.h zc_hashtable.h 
 zc_xplatform.h zc_util.h thread.h event.h buf.h mdc.h spec.h format.h
 level.o: level.c zc_defs.h zc_profile.h zc_arraylist.h zc_hashtable.h 
 zc_xplatform.h zc_util.h level.h
 level_list.o: level_list.c zc_defs.h zc_profile.h zc_arraylist.h 
 zc_hashtable.h zc_xplatform.h zc_util.h level.h level_list.h
 mdc.o: mdc.c mdc.h zc_defs.h zc_profile.h zc_arraylist.h zc_hashtable.h 
 zc_xplatform.h zc_util.h
 record.o: record.c zc_defs.h zc_profile.h zc_arraylist.h zc_hashtable.h 
 zc_xplatform.h zc_util.h record.h
 record_table.o: record_table.c zc_defs.h zc_profile.h zc_arraylist.h 
 zc_hashtable.h zc_xplatform.h zc_util.h record_table.h record.h
 rotater.o: rotater.c zc_defs.h zc_profile.h zc_arraylist.h zc_hashtable.h 
 zc_xplatform.h zc_util.h rotater.h
 rule.o: rule.c fmacros.h rule.h zc_defs.h zc_profile.h zc_arraylist.h 
 zc_hashtable.h zc_xplatform.h zc_util.h format.h thread.h event.h buf.h 
 mdc.h rotater.h record.h level_list.h level.h spec.h
 spec.o: spec.c fmacros.h spec.h event.h zc_defs.h zc_profile.h 
 zc_arraylist.h zc_hashtable.h zc_xplatform.h zc_util.h buf.h thread.h 
 mdc.h level_list.h level.h
 thread.o: thread.c zc_defs.h zc_profile.h zc_arraylist.h zc_hashtable.h 
 zc_xplatform.h zc_util.h event.h buf.h thread.h mdc.h
 zc_arraylist.o: zc_arraylist.c zc_defs.h zc_profile.h zc_arraylist.h 
 zc_hashtable.h zc_xplatform.h zc_util.h
 zc_hashtable.o: zc_hashtable.c zc_defs.h zc_profile.h zc_arraylist.h 
 zc_hashtable.h zc_xplatform.h zc_util.h
 zc_profile.o: zc_profile.c fmacros.h zc_profile.h zc_xplatform.h
 zc_util.o: zc_util.c zc_defs.h zc_profile.h zc_arraylist.h zc_hashtable.h 
 zc_xplatform.h zc_util.h
 zlog-chk-conf.o: zlog-chk-conf.c fmacros.h zlog.h
 zlog.o: zlog.c fmacros.h conf.h zc_defs.h zc_profile.h zc_arraylist.h 
 zc_hashtable.h zc_xplatform.h zc_util.h format.h thread.h event.h buf.h 
 mdc.h rotater.h category_table.h category.h record_table.h 
 record.h rule.h$(DYLIBNAME): $(OBJ)
 $(DYLIB_MAKE_CMD) $(OBJ) $(REAL_LDFLAGS)
 # for use in test folder - linux and requirement for aix runtime
 # resolving
 cp -f $(DYLIBNAME) $(DYLIB_MAJOR_NAME)
 cp -f $(DYLIBNAME) $(DYLIB_MINOR_NAME)$(STLIBNAME): $(OBJ)
 $(STLIB_MAKE_CMD) $(OBJ)dynamic: $(DYLIBNAME)
 static: $(STLIBNAME)Binaries:
zlog-chk-conf: zlog-chk-conf.o $(STLIBNAME) $(DYLIBNAME)
 $(CC) -o $@ zlog-chk-conf.o -L. -lzlog $(REAL_LDFLAGS).c.o:
 $(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $<clean:
 rm -rf $(DYLIBNAME) $(STLIBNAME) $(BINS) *.o *.gcda *.gcno *.gcov $(DYLIB_MINOR_NAME) $(DYLIB_MAJOR_NAME)dep:
 $(CC) -MM *.cInstallation target
ifeq ($(uname_S),SunOS)
 INSTALL?= cp -r
 endififeq ($(uname_S),AIX)
 INSTALL?= cp -r
 endifINSTALL?= cp -a

install: $(DYLIBNAME) $(STLIBNAME)
mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH) $(INSTALL_BINARY_PATH)
$(INSTALL) zlog.h $(INSTALL_INCLUDE_PATH)
$(INSTALL) zlog-chk-conf $(INSTALL_BINARY_PATH)
$(INSTALL) $(DYLIBNAME) vscode如何配置仓库地址_vscode如何配置仓库地址_17(DYLIB_MINOR_NAME)
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIB_MAJOR_NAME)
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MAJOR_NAME) $(DYLIBNAME)
$(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH)

32bit:
 @echo “”
 @echo “WARNING: if this fails under Linux you probably need to install libc6-dev-i386”
 @echo “”
 $(MAKE) CFLAGS=“-m32” LDFLAGS=“-m32”gprof:
 $(MAKE) CFLAGS=“-pg” LDFLAGS=“-pg”gcov:
 $(MAKE) CFLAGS=“-fprofile-arcs -ftest-coverage” LDFLAGS=“-fprofile-arcs”