LINUX下文件编译

本文详细介绍了在Linux环境下使用gcc/g++编译器进行C/C++文件的编译与调试流程,包括预处理、编译、汇编、链接等阶段,以及如何使用gdb进行程序调试,同时涵盖了静态库与动态库的创建和使用方法。
摘要由CSDN通过智能技术生成

Linux 下文件编译与调试

gcc/g++编译器

对于.c格式的 C文件,可以采用gcc或g++编译

对于 .cc、.cpp格式的C++文件,应该采用g++进行编译
常用的选项:
-c  表示编译源文件
-o  表示输出目标文件
-g  表示在目标文件中产生调试信息,用于gdb调试
-D  <宏定义> 编译时将宏定义传入进去
-Wall  打开所有类型的警告。
1. gcc编译过程:预编译->编译->汇编->链接
PixBK54YwwGA3Xr1qV79+7GRYKEqGkUCoXCJHCFEEL8PmQtBSGEEEII8fb5f9qh51JxgM7aAAAAAElFTkSuQmCC
(1)预处理:预处理器将对源文件中的宏进行展开。
(2)编译:gcc将c文件编译成 汇编文件。
(3)汇编:汇编器as将汇编文件编译成机器码。
(4)链接:将目标文件和外部符号进行连接,得到一个可执行二进制文件。

//test.c
#include <stdio.h>
#define NUMBER (1+2)
int main()
{
   int x = NUMBER;
   return 0;
}


(1)预处理: gcc –E test.c -o test.i  
我们用 cat 查看 test.i 的内容如下: int  main()  int x=(1+2);  return 0;我们可以看到,文件中宏定义 NUMBER出现的位置被(1+2)替换掉了,其它的内容保持不变。


h3UbeVgAAAABJRU5ErkJggg== //前面引入了很多东西


预处理阶段:对包含的头文件(#include)和宏定义(#define、#ifdef等)进行处理
(2)编译:gcc -S test.i –o test.s 通过 cat test.s 查看 test.s 的内容为 代码。
OAAAAAElFTkSuQmCC
编译阶段:检查代码规范性、语法错误等,在检查无误后把代码翻译成汇编语言
(3)汇编:as test.s -o test.o  利用as将汇编文件编译成机器码。得到输 出文件为 test.o。test.o 中为目标机器上的二进制文件. 用 nm 看文件中的符号: nm test.o 输出如下:0000000000000000 T main。 既然已经是 二进制目标文件了,能不能执行呢?试一下 ./test.o,提示  cannotexecute binary file. T表示这个符号属于代码。
mcH2+oAAAAASUVORK5CYII=
//test.o 得先加一个执行权限
A8Nf6rWK9nyBAAAAABJRU5ErkJggg==
(4)链接: gcc test.o –o test  ,将所有的.o文件链接起来生产可执行程序。
rpAYlNRcroYAAAAASUVORK5CYII=

链接阶段:将.s的文件以及库文件整合起来链接为可执行程序


gcc所支持后缀名:
Aux77wcpC57ty1nYureW04OWKsn0SS5mqMFBmJLE3V5NFlMRo0KoR55ee9t0Gf7sYyMKaMpqwIQQnnZXaONa9P7yF32XlvOROPaJMFj0XTAWhXRJIe9IeVcGWwLSJvYVOvtqI1tkhLDJVFpYi2Y8t7bAgudsLvuS4rQsxRjnjWSfDH+LkQHjD3TOxB3zRw8mjrAo6kTPJo6wKOpEzyaOsCjqRM8mjrAo6kTPJo6wKOpEzyaOkDj6NtxnNeRaHIcZwOG4T960VQxQYtxtgAAAABJRU5ErkJggg==
gcc常用选项:
8fVp7gqFdPaVQAAAAASUVORK5CYII=
gcc库选项:
AAAAAASUVORK5CYII=
gcc警告选项:
nKl6cAAAAASUVORK5CYII=


file 文件名        看对应文件类型
AOR8mqVy2dTIAAAAAElFTkSuQmCC
gcc -v            显示编译器版本


//test1.c
#include<stdio.h>
int main()
{
        #ifdef meihao
        printf("define meihao\n");
        #else
        printf("not define meihao\n");
        #endif
        printf("main exit\n");
}
gcc -E test1.c -o test1.i
R4wQMZA90GeGgb8Q5guAgaNEAUGrQjR8cj5RFMAX4p7dAEAAAAAAHD0P98QdIPGXCHIAAAAAElFTkSuQmCC //没有定义meihao
gcc -E test1.c -o test1.i -D meihao
MzmDl7cAQAAAABJRU5ErkJggg==


函数库分为静态库和动态库

静态库是目标文件.a 的归档文件(格式为libname.a)。如果在编译某 个程序时链接静态库,则链接器将会搜索静态库并直接拷贝该程序 的可执行二进制文件到当前文件中;

静态库将会整合到程序中,在程序执行时不用加载静态库。 因此,静态库会使你的程序臃肿并且难以升级,但比较容易部署。而动态库会使你的程序轻便易于升级但难以部署。

动态库(格式为 libname.so[.主版本号.次版本号.发行号])。在程序编 译时并不会被链接到目标代码中,而是在程序运行时才被载入。
fUuX9f88FQ4nBtUihAAAAAElFTkSuQmCC
动态库只在执行时才被链接使用,不是直接编译为可执行文件,并且一个动态库可以被多个程序使用故可称为共享库。
创建静态库:
>gcc -c add.c    //编译add.c源文件生成add.o目标文件
>ar -crsv libadd.a add.o  //对目标文件*.o进行归档,生成lib*.a,此处lib要写;命令参数:
c 创建一个库。不管库是否存在,都将创建。
r 代替库中现有的文件或者插入新的文件
s 写入一个目标文件索引到库中,或者更新一个存在的目标文件索引
v 输出详细信息
 a - add.o
>su
#cp ./libadd.a /lib     //拷贝到标准程序设计库
>gcc main.c -w -o main -ladd
>./main 1 2
 sum=3

> gcc -c add.c
>ar -crsv libadd.a add.o
 a - add.o
>gcc main.c -w -o main -L./ -ladd   //不要忘记-L后面的那个./ ;即在库文件的搜索路径中添加当前路径   -ladd表示链接库文件libadd.a/.so 
>./main 1 2
 sum=3
创建动态库:
>gcc -fPIC -Wall -c add.c  //编译,允许发出警告,生成使用相对地址的位置无关的目标代码
>gcc -shared -o libadd.so add.o  //生成库文件libadd.so
>su  
#cp ./libadd.so /lib
#exit  //拷贝到 /lib 库中
>gcc main.c -w -o main -ladd
>./main 1 2
 sum=3

在运行main前,需要注册动态库的路径。
方法有3种:
▶修改/etc/ld.so.conf
▶修改LD_LIBRARY_PATH环境变量
库放在当前目录
>export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/meihao
>gcc main.c -o main -ladd -L/home/meihao

将库文件拷贝到/lib或者/usr/lib下(系统默认搜索库路径)。
B8tviPnDdAVlQAAAABJRU5ErkJggg==


//add.c
int add(int a,int b)
{
        return a+b;
}
//main.c
#include<stdio.h>
#include<stdlib.h>
void main(int argc,char* argv[])
{
        if(argc!=3)
        {
                printf("error args\n");
                return;
        }
        int i=atoi(argv[1]);
        int j=atoi(argv[2]);
        printf("sum=%d\n",add(i,j));
}
BxszZwSkpOLJAAAAAElFTkSuQmCC

SzHvyRiTSAAAAAASUVORK5CYII=



程序调试

gdb常用命令
Linux 包含了一个叫gdb的调试程序。gdb可以用来调试 C 和 C++ 程序。 在程序编译时用 -g 选项可打开调试选项,表示在目标文件中产生调试信息,用于gdb调试;
>gcc test1.c -Wall -g -o test  //(—g和—o要挨在一起)(—g和—c要挨在一起)
>gdb ./test
52vELWCGxPqPpDD6AQAAAMC8cEtX4y4BmwiMfgAAAAAwjuDcAvTEPsS5ttIAAAAA4PT8P+pbz+cPAoyAAAAAAElFTkSuQmCC    

l   //显示所要调试的代码;(l  函数名)显示文件中定义的函数代码
VefTswc++T0AAAAASUVORK5CYII=
b     //(b  main )表示在main函数那加一个断点(keep永久断点),也可以是数字(b n);
XHL6AAAAAElFTkSuQmCC
r     //表示运行;运行到断点;
fmjCo7LnLD9K2XRn+fkp63mbisb4DwAAADgbvfeP5ov4sXlOKdccDRiVSyqcidyw0cuSVvOLnZ4EAAAAnIjFPd75wpEsW1Q2WXD4sY6dkafv2rwKAAAAuGUs7GgXmDsAAACAiIj+Bxn3KQezeBqaAAAAAElFTkSuQmCC
p 变量名    //查看变量值也可以 (p  &变量名)查看变量地址;
OgAAAAAElFTkSuQmCC
n   //下一步不进入函数   相当于Windows的F10            
gVuotlboE0XwMsaD4f9CwAAAAAAAAAA8Jz+P7EVIAF2m1UIAAAAAElFTkSuQmCC
s   //表示单步进入函数, 相当于Windows的F11    


1、先打断点,运行起来
2、一步一步往下走,n
3、c程序一直往下走
4、s进入函数
c   //运行到最后                                                       
    (continue)
q    //退出                     
相当于Windows的Shift+F5        (quit)
set args 1 2   //调试中向main入口传递两个参数
fa2QAA4AH9f8o2jPbRsGq9AAAAAElFTkSuQmCC
show args   //显示刚才设置的参数
577zIAAAAA1P4DL597pX8eoPEAAAAASUVORK5CYII=
info b    //查看刚才所设断点
AT6+rOrNqwBcAAAAAElFTkSuQmCC
x   //是用来检查内存情况(x  /20xb  要查看变量地址) YiF2Ob4t9AAAAAElFTkSuQmCC
bt   //进入调用堆栈;
rvBgbTVY9VkAAAAASUVORK5CYII=
d  断点号   //删除断点
A7Ey8KICcEqwAAAAAElFTkSuQmCC
tb n   //设置临时断点,下次运行r会消失
kmAAAAABJRU5ErkJggg==

>gcc test2.c -Wall -g -o test2
t5jKOtIyKd4zU8Fm7n2eIFAAAAAAAAAAAAAAAAAAAAAAAAAAC4pv8DrXZzXngc21wAAAAASUVORK5CYII=
// int数据,一个数据占4个字节,32位
//test1.c
#include<stdio.h>
#include<stdlib.h>
int add(int a,int b)
{
        return a+b;
}
int main(int argc, char **argv)
{
        if(3!=argc)
        {       
                printf("error argcs\n");
                return -1;
        }
        int sum;
        sum=add((atoi(argv[1])),(atoi(argv[2])));
        printf("sum=%d\n",sum);
        return 1;
}

//test2.c

#include<stdio.h>
int main()
{
        int a[4]={1,2,3,4};
        printf("a[1]=%d\n",a[1]);
        return 1;
}


命令 x 是用来检查内存情况,英文是examine含义,使用方法 x /20xb 变量首地址,其中20x代表16进制的长度,b代表字节的含义



core dumped
>ulimit  -c  unlimited
FiAAAAAElFTkSuQmCC
>gcc test3.c -w -o test3
dvigyvUhnAPvaC6GcsSo7ED+AGEJz4jqjbP3+yYJZV+AvWjBa8LOjL9RAKxaAW9DL6MofqyTq9lDK1EAn4dXpLAWRvNGRK1sw5BJ6fKer2zdpDeOD2FdRB5LtheuXXycotL58FPsKgiAIgiAIgiAIgiAIgiAIgiAIgiAIgiAIgvDAZPFbBEEQbgV+vbhUeDH+D9UiPgRuEpI8AAAAAElFTkSuQmCC
>gdb ./test3 core
A9M8ZrZ9EMT9QAAAABJRU5ErkJggg==
//l 输出错误行代码,bt 打开调用栈查看自己错误行代码, q 退出;
Hw31MFcwTeAQAAAAAElFTkSuQmCC
//test3.c
#include<stdio.h>
int add(int a,int b)
{
        return a+b;
}
int main(int argc,char **argv)
{
        int i,j,sum;
        i=atoi(argv[1]);
        j=atoi(argv[2]);
        sum=add(i,j);
        printf("sum=%d\n",sum);
}
gcc对代码进行优化通过选项“-On”来控制优化级别(n是整数)。不同的优化级别对应不同的优化处理工作。如使用优化选项“-O1”主要进行线程跳转和延迟退栈两种优化。使用优化选项“-O2”除了完成所有“-O1”级别的优化之外,还要进行一些额外的调整工作,如处理其指令调度等。选项“-O3”则还包括循环展开或其他一些与处理器特性相关的优化工作。虽然优化选项可以加速代码的运行速度,但对于调试而言将是一个很大的挑战。因为代码在经过优化之后,原先在源程序中声明和使用的变量很可能不再使用,控制流也可能会突然跳转到意外的地方,循环语句也有可能因为循环展开而变得到处都有,所有这些对调试来讲都是不好的。所以在调试的时候最好不要使用任何的优化选项,只有当程序在最终发行的时候才考虑对其进行优化。
通常用的是-O2




转载于:https://www.cnblogs.com/meihao1203/p/8390821.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值