Linux下用GCC

前言

离职前对做过的支付系统进行了一番#总结,继续完善我的C服务器。

本想着接下来大概实现一下 CGI 协议,但是实现过程中被一个问题卡住了:

C进程与php进程的交互数据类型问题:

在 C 进程中我准备将服务器处理后的请求数据存储在一个结构体内,然后将此结构体中的信息传给 PHP,而 PHP 进程内也会有一个全局数组与之对应,可是众所周之,结构体是 C 进程内的内存数据,是无法直接传给 PHP 使用的。

这时候我们也需要一种“协议”来解决进程数据类型的异构性。当然这个解决方案确定起来还是很简单的,无非是对C结构体进行序列化,使用xml,json,protobuf(没用过)之一,花费时间多的地方在实现过程。 原来想自己造个轮子,实现一下json类型的编解码,觉得有些偏离了主题了,于是考虑使用一个开源库cJSON;

可是自己没有过 C 大型项目的开发经验,写的都是小 demo,gcc -o name source.c 足以解决问题了,没有过编译多个文件、组织项目的经验,下载到源码后一脸懵逼,搜索到的编译资料都是一些较为零散的内容,不成体系,不过在自己的多次尝试下终于成功地将 cJSON 引入到项目中了,这里稍做一下总结。

绕了好久,终于来到了本篇文章的主题:项目编译,主要介绍一些用 GCC 在 linux 下项目编译链接的步骤。

另外,我只是测试了方案可行,还没动手改,对方案优劣情况的判断还不足,望有过类似经验的同学给点意见什么的。


编译步骤

先说一下一个C源文件的编译一般步骤:

  1. 预处理(preprocess):主要是在代码层面的处理,包括文件的引入,展开宏定义,删除注释,添加行号等,生成的文件以.i结尾。

    gcc -E test.c -o test.i

  2. 编译(compilation):编译是在代码语法层面的处理,生成对应的汇编语言代码,生成以.s为后缀的汇编语言文件;

    gcc -S test.i -o test.s

  3. 汇编(Assembly):将汇编语言代码生成可执行的机器码,生成以.o为后缀的目标文件。

    gcc -c test.s -o test.o

  4. 链接(Linking):将各个.o目标文件连接起来,并解决库依赖,生成无后缀的可直接执行文件。

    gcc -o test test.o

如果我们直接使用后面的命令,那么前面的步骤也会自动执行。如我们常使用的 gcc -o 实际上是一次性完成了所有的步骤的。

以上的中间文件,大家可以使用文本查看工具来查看其中内容来验证其功能。


静态库和动态库

库文件有动态和静态之分,他们的命名规范为 lib库名.后缀,在链接目标文件和库时,使用 -l 库名(空格可省略)选项,也可以添加-L /path来规定优先搜索库文件的目录。

例如:C中的数学函数库math.h的动态库文件名为libm.so,那么我们编译连接文件时就需要添加-lm的选项。如果要指定库文件路径为/usr/lib64/libm.so,那么可添加-L /usr/lib64来指定库文件优先查找目录。

另外静态和动态库文件搜索目录顺序不一样,下面分别详细介绍:

静态库

静态库文件一般是以.a为后缀的库文件,它在编译连接时会将库文件的内容全部添加到可执行文件中,在编译连接完成后,静态库文件便不再影响可执行文件。

它的优点是简单粗暴,但如果库文件内部有改动的话需要重新对所有引用此库文件的可执行文件重新编译。

一般编译步骤如下:

gcc -c static.c -o static.o // 编译静态库文件的源文件
ar -r static.a static.o // 生成静态库文件
gcc -o main -lstatic // 连接静态库文件生成可执行文件

编译连接时,静态库文件搜索目录顺序为:

  1. 编译连接时 -L 参数指定的目录;
  2. 环境变量目录 LIBRARY_PATH
  3. 固定目录 /lib、/usr/lib、/usr/local/lib等;

动态库

动态库文件一般以.so结尾,它在编译连接时只把动态库的文件添加到可执行文件,只在程序运行时才加载库文件。这种方式的优点是非常灵活,如果动态库文件内部有变动,那么只需重要重新编译库文件即可。

它的一般编译步骤如下:

gcc -c dynamic.c -fpic -o dynamic.o // 编译动态库文件的源文件 -fpic 表示编译为位置独立的代码,使之可以被放在可执行文件内存中的任何地方
gcc -shared dynamic.o -o dynamic.so // 生成动态库文件
gcc -o main -L . -ldynamic // 连接当前文件夹下的动态库文件

编译连接时,动态库文件搜索目录顺序为:

  1. 编译连接时 -L 参数指定目录;
  2. 环境变量目录 LD_LIBRARY_PATH
  3. 配置文件/etc/ld.so.conf中配置的目录
  4. 固定目录 /lib、/usr/lib等。

CMakeLists

写到这里还不是结尾,我们要考虑如果文件非常多怎么办,难道每一次都要输入n多个源文件名吗?如果软件完成后,用户使用时可不想记住这些复杂的命令和文件。

自动化才是目标,我们考虑使用自动化编译工具 cmake,那么接下来我们就要编写适合项目文件的编译配置文件 CMakeLists。

CMakeLists 是一个 txt 文件,它就像是项目的编译指南,是给用 cmake 工具用的。其语法类似于 shell,但内置了许多函数,这里我们介绍几个简单的语法,编写一个简单的 CMakeLists.txt

当前文件结构:

|__ CMakeLists.txt
|__ test.c
|__ cJSON.c
|__ include
| |__ cJSON.h
|__ lib

下面是一个动态库的编译CmakeList,将解释放在注释中。

PROJECT(test)  # 项目名称
cmake_minimum_required(VERSION 2.8) # 选择一个cmake版本 SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) # 设定产生库的目录
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) # 设定产生的可执行文件的目录 ADD_EXECUTABLE(test test.c) # 这里要先声明产生的可执行文件,以便后面连接 SET(cJSON cJSON.c) # 设置文件变量
ADD_LIBRARY(cJSON SHARED ${cJSON}) # 此语句用文件变量生成一个动态链接库
TARGET_LINK_LIBRARIES(test cJSON) # 连接可执行文件与动态链接库 FIND_LIBRARY(MATH_LIB libm.so /usr/lib64) # 在/usr/lib64文件夹下找libm.so(cJSON需要)
IF(MATH_LIB)
TARGET_LINK_LIBRARIES(test ${MATH_LIB}) # 找到之后连接上
ENDIF() MESSAGE("cmake complete, use make to compile!") # 在命令行输出提示语句

搞了一个多小时,终于写出来了一个能用的 CMakeLists 文件。运行 cmake . && make完成项目的构建。

此时的目录结构为(略过了 cmake 产生的临时文件):

|__ CMakeLists.txt
|__ test.c
|__ cJSON.c
|__ include
| |__ cJSON.h
|__ lib
| |__ libcJSON.so
|__ bin
|__ test

小结

本文严重地说明了光会写代码没什么卵用,环境的搭建/类库的使用也是必备技能,毕竟不能每个轮子都自己造。

如果你也是 C 新手的话,本文可以让你大概了解一下编译步骤等,不至于跟我一开始一样一头雾水。如果要深入学习的话,文章的关键词和下面的参考文件也能有些帮助。

如果您觉得本文对您有帮助,可以点击下面的 推荐 支持一下我。博客一直在更新,欢迎 关注 。

参考文件(精挑细选):

GCC工作过程以及动态库静态库链接

Linux动态库文件搜索路径

cmake使用示例与整理总结

 
分类: C/C++

Linux下用GCC的更多相关文章

  1. [转]Linux下用gcc/g++生成静态库和动态库(Z)

    Linux下用gcc/g++生成静态库和动态库(Z) 2012-07-24 16:45:10|  分类: linux |  标签:链接库  linux  g++  gcc  |举报|字号 订阅     ...

  2. 用C写一个web服务器(三) Linux下用GCC进行项目编译

    .container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .conta ...

  3. 在Linux下使用gcc编译mesa文件报undefined reference to symbol 'sin@@GLIBC_2.2.5和DSO missing from command line两个错误的解决方案

    一.概述 在Linux系统下使用gcc编译用C语言写的mesa的示例程序. 环境:Ubuntu Server 18.04.1 二.问题的出现 在Ubuntu下安装好mesa所需的库文件,将目标文件从g ...

  4. linux下安装gcc详解

    1.了解一下gcc 目前,GCC可以用来编译C/C++.FORTRAN.JAVA.OBJC.ADA等语言的程序,可根据需要选择安装支持的语言.我自己linux上是4.1.2版本,是不支持openMP的 ...

  5. 在Linux下使用gcc运行C语言程序

    Linux下使用最广泛的C/C++编译器是GCC,大多数的Linux发行版本都默认安装,不管是开发人员还是初学者,一般都将GCC作为Linux下首选的编译工具.本教程毫不犹豫地使用GCC来编译C程序. ...

  6. linux下使用gcc/g++编译代码时gets函数有错误

    今天在linux中使用个g++编译一个名为myfirst.cpp的代码的时候,出现如下错误 myfirst.cpp: In function ‘int main()’:myfirst.cpp:11:2 ...

  7. linux下使用gcc编译运行C程序

    gcc(GNU Compiler Collection)是Linux下最常用的C语言编译器,是GNU项目中符合ANSI C标准的编译系统,能够编译用C.C++和Object C等语言编写的程序.  在 ...

  8. Linux下使用gcc编程初体验

    近期刚刚放弃了Windows,投入了Ubuntu 的怀抱.今天就拿一个小小的案例来做一下C语言的编译和运行流程.额,顺便说一句.本文适合那些Linux新手,不适合老鸟哈. 看完本文可以学到什么? 程序 ...

  9. 在Linux下用gcc编译hello world

    1. 确保Linux系统里已经装好了gcc 测试:输入gcc后是如下的结果就说明已经安装成功 2. 创建HelloWorld.c 使用 touch 创建一个空文件; 用vim编辑 按下A或者I 插入 ...

随机推荐

  1. Altium Designer如何设置pcb尺寸

  2. jQuery的原理

    JQ的原理 jquery-1.xxx :专门为PC端诞生的类库,兼容所有的浏览器 jquery-2.xxx:当初是为了移动端而准备的,所以IE低版本浏览器一般不兼容,但是这个版本针对移动端的事件等操作 ...

  3. SpringMVC学习记录(五)--表单标签

    在使用SpringMVC的时候我们能够使用Spring封装的一系列表单标签,这些标签都能够訪问到ModelMap中的内容. 以下将对这些标签一一介绍. 1.引入标签头文件 在正式介绍SpringMVC ...

  4. 多类 SVM 的损失函数及其梯度计算

    CS231n Convolutional Neural Networks for Visual Recognition -- optimization 1. 多类 SVM 的损失函数(Multicla ...

  5. LA-3708 - Graveyard 简单的模拟一下即可

    一开始不知道在想啥,竟然写了个双重for循环的.T T一直WA,又没效率. T T然后在纸上模拟演算,改了,就AC了 以后做题果断要先模拟一下例子...能加深对题目的理解. 当教训吧..太懒导致写了好 ...

  6. 【习题 5-11 UVA 12504 】Updating a Dictionary

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 不确定某个map里面是否有某个关键字的时候. 要用find来确定. 如果直接用访问下标的形式去做的话. 会强行给他加一个那个关键字( ...

  7. SQL日期时间函数

    一.Sql Server中的日期与时间函数 1. 当前系统日期.时间  select getdate() 2. dateadd 在向指定日期加上一段时间的基础上,返回新的 datetime 值  例如 ...

  8. goland 2018.2 激活

    感谢 http://blog.sina.com.cn/s/blog_1885d23df0102ydjc.html http://www.3322.cc/soft/38102.html 下载   htt ...

  9. shiro实现登录安全认证(转)

    shiro实现登录安全认证 shiro的优势,不需要再代码里面判断是否登录,是否有执行的权限,实现了从前端页面到后台代码的权限的控制非常的灵活方便 传统的登录认证方式是,从前端页面获取到用户输入的账号 ...

  10. [Angular2Fire] Firebase auth (Google, Github)

    To do auth, first you need to go firebase.console.com to enable the auth methods, for example, enabl ...