GCC编译

GCC编译

本笔记为作者再学习嵌入式Linux的一些心得体会,如有不对的地方,请包含与谅解!我主要是采用香橙派5来作为我们学习嵌入式Linux的环境。

------------by wsoz

Hello程序代码

c 复制代码
#include <stdio.h>

int main(int argc,char **argv)
{
    if(argc > 1)
    {
        printf("Hello, %s!\n", argv[1]);
    }
    else
    {
        printf("Hello, World!\n");
    }
    return 0;
}

代码编译过程如下:

此处我们先对程序做一个简单的介绍:

c 复制代码
int main(int argc,char**argv)

这个是C程序入口标准函数声明

  • int 作为返回值
    • 0--表示程序正常结束
    • 非0--出错或者异常
  • int argc表示命令行参数个数
    • 包含了程序名/路径本身
  • char **argv表示的是命令行参数,每个参数都是字符串
    • 比如输入 ./hello world,会输出hello world!
    • 其中参数argv[0]:程序名/路径
    • argv[1]:world

GCC编译四阶段

阶段 命令示例 输入 输出 作用说明
预处理 (Preprocessing) gcc -E hello.c -o hello.i .c 源文件 .i 预处理文件 • 展开所有宏定义(#define) • 处理#include指令,插入头文件内容 • 删除所有注释 • 处理条件编译(#ifdef等) • 添加行号和文件标识
编译 (Compilation) gcc -S hello.i -o hello.s .i 预处理文件 .s 汇编文件 • 将C代码翻译成汇编代码 • 进行语法检查和语义分析 • 进行代码优化 • 生成目标平台的汇编指令
汇编 (Assembly) gcc -c hello.s -o hello.o .s 汇编文件 .o 目标文件 • 将汇编代码转换为机器码 • 生成二进制目标文件 • 包含符号表和重定位信息 • 此时还不能直接执行
链接 (Linking) gcc -o hello hello.o .o 目标文件 可执行文件 • 链接所需的库文件(如libc.so) • 解析符号引用 • 重定位代码和数据段 • 生成最终可执行程序

💡 一步完成所有阶段

bash 复制代码
gcc -o hello hello.c  # 自动完成预处理→编译→汇编→链接

GCC 常用编译选项

常用选项 描述
-E 预处理
-S 编译
-c 汇编
-o 指定输出文件
-I(大小i) 指定头文件目录
-L 指定链接时库文件目录
-l(小写L) 指定链接哪一个库文件
-v 详细查看编译过程

多文件代码示例

c 复制代码
#include <stdio.h>
#include "name.h"

int main(int argc,char **argv)
{
    if(argc > 1)
    {
        printf("Hello, %s!\n", argv[1]);
    }
    else
    {
        printf("Hello, World!\n");
    }
    fun();
    return 0;
}
c 复制代码
#include <stdio.h>

void fun(void)
{
    printf("I am WSOZ\r\n");
}

此时多文件进行编译的话我们通常可以分成两种方法进行

  • 方法1
bash 复制代码
gcc -o hello hello.c name.c

直接一步到位,直接包含所有的文件即可,简单快速

  • 方法2
bash 复制代码
#分开汇编
gcc -c hello.c -o hello.o
gcc -c name.c -o name.o
#统一链接
gcc -o hello hello.o name.o

我们对多程序的情况下一般采取分开汇编,然后统一进行链接,此种方法在有少数文件需要修改的情况更加适用

动态库和静态库

库(Library)的作用:把通用功能封装起来复用,避免每个项目都重复写代码。

静态库(.a)作用

在链接阶段把库代码"拷贝"进你的可执行文件里。

  • 优点:部署简单(一个程序就能跑)、不怕目标机缺库
  • 缺点:可执行文件变大;多个程序各带一份,浪费存储/内存;库升级要重新编译程序

动态库(.so)作用

程序里只保留"引用",运行时再加载库。

  • 优点:可执行文件更小;多个程序可共享同一个库;升级库更方便
  • 缺点:运行依赖库文件和版本(可能出现"找不到so"或版本不匹配)

制作与使用库

对于库的制作,本质上就是将编译后的 .o 打包成对应格式,然后在链接阶段复用。

  • 第一步生成目标文件 .o(动态库建议加 -fPIC

    bash 复制代码
    gcc -fPIC -c name.c -o name.o
  • 第二步打包成静态库/动态库

    bash 复制代码
    # 动态库
    gcc -shared -o libmy.so name.o
    # 静态库
    ar crs libmy.a name.o

下面就是对于库的使用了,大致有两种方法

方法1:把库放到系统目录(如 /usr/local/lib

动态库和静态库都可以放进去;只是链接/运行行为不同。

bash 复制代码
# 安装到系统库目录(推荐 /usr/local/lib)
sudo cp libmy.so /usr/local/lib/
sudo cp libmy.a  /usr/local/lib/
sudo ldconfig

1)使用动态库(.so)

bash 复制代码
gcc -o hello hello.c -lmy
./hello

2)使用静态库(.a)

bash 复制代码
# 最直接:显式写静态库文件路径
gcc -o hello hello.c /usr/local/lib/libmy.a
./hello

方法2:不放系统目录,通过 -L 指定路径

1)使用动态库(.so)

bash 复制代码
# 链接阶段找到库
gcc -o hello hello.c -L. -lmy

# 运行阶段还要能找到 .so(2选1)
LD_LIBRARY_PATH=. ./hello
# 或者
gcc -o hello hello.c -L. -lmy -Wl,-rpath,'$ORIGIN'
./hello

2)使用静态库(.a)

bash 复制代码
# 最直接:显式写静态库文件路径
gcc -o hello hello.c ./libmy.a
./hello

关键区别

  • -L 只影响链接阶段;
  • .so 运行时还要能被系统找到;
  • .a 在链接时已拷贝进可执行文件,运行时不再依赖该库文件。
bash 复制代码
# 检查可执行文件依赖
ldd ./hello
相关推荐
其实防守也摸鱼2 小时前
GDB安装与配置(保姆级教程)【Linux、Windows系统】
linux·运维·windows·命令模式·工具·虚拟机·调试
励志的小陈7 小时前
贪吃蛇(C语言实现,API)
c语言·开发语言
FreakStudio8 小时前
做了个Claude Code CLI 电子宠物:程序员的实体监工代码搭子
python·单片机·嵌入式·面向对象·并行计算·电子diy·电子计算机
AC赳赳老秦9 小时前
OpenClaw二次开发实战:编写专属办公自动化技能,适配个性化需求
linux·javascript·人工智能·python·django·测试用例·openclaw
mounter6259 小时前
【内核新动向】告别物理槽位束缚:深度解析 Linux Virtual Swap Space 机制
linux·内存管理·kernel·swap·virtual swap
handler019 小时前
从零实现自动化构建:Linux Makefile 完全指南
linux·c++·笔记·学习·自动化
Hello_Embed10 小时前
嵌入式上位机开发入门(二十六):将 MQTT 测试程序加入 APP 任务
网络·笔记·网络协议·tcp/ip·嵌入式
2023自学中11 小时前
i.MX6ULL 板子的完整启动流程图(从上电 → 用户空间)
linux·嵌入式
闫利朋11 小时前
Ubuntu 24.04 桌面安装向日葵完整指南
linux·运维·ubuntu
爱编码的小八嘎11 小时前
C语言完美演绎8-15
c语言