第4章 linux中的编译器GCC/G++和调试器GDB

1.1 简介

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

对于 .cc、.cpp 格式的C++文件,应该采用 g++进行编译。

常用的选项:

选项

作用

-c <源文件>

表示编译源文件(.c,.cpp,.cc等)

-o <输出文件>

表示输出目标文件(.o)

-g

表示在目标文件中产生调试信息,用于 gdb 调试

-D <宏定义>

编译时将宏定义传入进去

-Wall

打开所有类型的警告。

注意:这个编译器编译出来的文件是为了在X86的linux系统环境下运行的。

1.2 gcc的编译过程

过程预编译à编译à汇编à链接

当我们进行编译的时候,要使用一系列的工具,我们称之为工具链。其中包括:预处理器、编译器、汇编器as、连接器。

一个编译过程可以用图2.1 来表示, 包括下面几个阶段:

(1)预处理:预处理器将对源文件中的宏进行展开。

(2)编译:gcc 将 c 文件编译成汇编文件(.s)。

(3)汇编:as 将汇编文件编译成机器码(.o)。

(4)链接:将目标文件和外部符号进行连接,得到一个可执行二进制文件。

1.3 gcc所支持的后缀

后缀名

所对应的语言

后缀名

所对应的语言

.c

C原始程序

.s/.S

汇编语言原始程序

.C/.cc/.cxx

C++原始程序

.h

预处理文件(头文件)

.m

Objective.c原始程序

.o

目标文件

.i

已经预处理的C原始程序

.a/.so

编译后的库文件

.ii

已经预处理的C++原始程序

1.4 gcc常用选项

选项

含义

-E

预编译,不做任何处理

-c

只编译不链接,生成目标文件”.o”

-S

只编译不会汇编,生成汇编代码”.s”

-g

在执行过程中包含标准调试信息

-o file

指定将file文件作为输出文件

-v

打印出编译器内部编译各过程的命令行信息和编译器的版本

-I dir

在头文件的搜索路径列表中添加dir目录

例子

下面以一个很简单的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 的内容如下:

    ......        //大多都是stdio.h文件内容
    # 943 "/usr/include/stdio.h" 3 4
     
    # 3 "test.c" 2
    int main(void)
    {
     int x = (1+2);
     return 0;
    }

我们可以看到,文件中宏定义NUMBER 出现的位置被(1+2)替换掉了,还有把stdio.h的内容都添加进来了。

  1. 编译 gcc -S test.i -o test.s

通过cat test.s 查看test.s 的内容为汇编代码如下:

junjia@junjia:~/works/3rd_day/1st_test$ cat test.s
        .file    "test.c"
        .text
        .globl    main
        .type    main, @function
    main:
    .LFB0:
        .cfi_startproc
        pushl    %ebp
        .cfi_def_cfa_offset 8
        .cfi_offset 5, -8
        movl    %esp, %ebp
        .cfi_def_cfa_register 5
        subl    $16, %esp
        movl    $3, -4(%ebp)
        movl    $0, %eax
        leave
        .cfi_restore 5
        .cfi_def_cfa 4, 4
        ret
        .cfi_endproc
    .LFE0:
        .size    main, .-main
        .ident    "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4"
        .section    .note.GNU-stack,"",@progbits
    junjia@junjia:~/works/3rd_day/1st_test$ 
  1. 汇编 as test.s -o test.o

利用as 将汇编文件编译成机器码。得到输出文件为 test.o,test.o 中为目标机器上

的二进制文件。

用 nm 查看文件中的符号: nm test.o ,输出如下:

00000000 T main

有的编译器上会显示:

00000000 b .bss 00000000 d .data 00000000 t .text U ___main U __alloca 00000000 T _main 

既然已经是二进制目标文件了,能不能执行呢?

试一下./test.o,提示 cannot execute binary file

因为是有U这样的符号的地址没有定下来。

  1. 链接gcc -o test test.o

将所有的.o 文件链接起来生成可执行程序,这个时候的test就可以执行了。

一般我们编译文件没有必要那么多步骤,只要如下就够了

gcc -o teat test.c

步骤总结:

预处理阶段:对包含的头文件(#include)和宏定义( #define、 #ifdef 等)进行处理。

gcc –E hello.c –o hello.i 
//-o 表示输出为指定文件类型 -E 将源文件( *.c) 转换为( *.i)

编译阶段:检查代码规范性、语法错误等,在检查无误后把代码翻译成汇编语言。

gcc –S hello.i –o hello.s 
//-S 将已预处理的 C 原始程序( *.i)转换为( *.s) 

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

gcc –o hello.exe hello.s 
//最后将汇编语言原始程序(*.s)和一些库函数整合成( *.exe) 

示例1:几种编译过程。

#include <stdio.h>
#define MAX 100
#define max(a, b) ((a) > (b) ? (a) : (b)) //宏定义,执行-E 之后被替换
main()
{
     printf("MAX=%d\n", MAX);
     printf("max(3,4)=%d\n", max(3, 4));
}

//法一:

gcc –E project1.c –o project1.i //预编译,生成已预编译过的 C 原始程序*.i

gcc –S project1.i –o project1.s //编译,生成汇编语言原始程序*.s

gcc –o project1.exe project1.s //链接,生成可执行程序

//法二:

gcc –c project1.c –o project1.o //编译

gcc –o project1.exe project1.o //链接

//法三:

gcc –o project1.exe project1.c //编译并链接

示例2:-D选项的使用。

#include <stdio.h>
main()
{
     #ifdef lry //表示如果定义了 lry,即命令行参数传了 lry,就执行下面的输出
     printf("lry is defined!\n");
     #else
     printf("lry is not defined!\n");
     #endif
     printf("main exit\n");
}

编译过程:

gcc –E project2.c –o project2.i –D lry

//条件编译,用-D 传递,如果没有传 lry 则执行#else

gcc –S project2.i –o project2.s

gcc –o project2.exe project2.s

或:gcc –o project2 project2.c –D lry

1.5 gdb调试器

作用:对c/c++程序进行调试的。

1.让程序停止在指定的某处

2.当程序停住时,可以查看程序的运行状态(比如:变量值)

gdb命令&调试步骤

第1步:编译程序

gcc -g a.c -o a.out

第2步:启动GDB

gdb a.out //运行调试器并打开调试文件

或者:

gdb //运行gdb

file a.out //打开调试文件

第3步:打断点

break 函数名 //在函数入口处打断点

或者

break 行号 //在某行打断点

第4步:运行程序

run

其他命令:括号内是命令的简写。

序号

命令

作用

1

next,continue

2

list(l)

3

break(b)

函数名/行号/文件名:行号

4

break行号 if 条件

break 5 if k=8

5

info break

查看断点信息

6

delete <断点编号>

删除断点

7

run(r)

全速运行到断点处

8

next(n)

单独运行,但不进入子函数

9

step(s)

单独运行,但进入子函数

10

continue(c)

11

print(p)

12

finish

13

watch 变量名

查看变量的值

下一小节:Makefile的使用

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI学长

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值