Linux学习笔记——C程序的编译运行与调试

这篇文章比较详细地介绍了gcc工具的基本使用,下面主要对一些内容进行补充。



gcc基本使用

gcc 在这里代指 gcc编译工具链,一个 .c 文件需要经过如下四个步骤生成最终的可执行文件

我们可以一步步执行命令生成可执行文件,也可以直接使用 gcc hello.c -o a.out 生成可执行文件。

下图展现了gcc常用的一些参数

  • -I 指定头文件目录

hello.c

#include<stdio.h>
#include "hello.h"

int main()
{
        int a = NUM;
        printf("Hello %d!\n",a);
        return 0;
}

hello.h

#ifndef NUM
#define NUM 20
#endif

一般都将头文件放入 include 文件夹,那么编译生成可执行文件需要指定头文件目录:

gcc hello.c -Iinclude -o a.out
  • -D 编译时定义宏

我们可以在编译时指定宏来实现代码的注释与否以进行DEBUG等行为。

hello.c

#include<stdio.h>
#include "hello.h"

int main()
{
        int a = NUM;
        printf("Hello %d!\n",a);
        #ifdef DEBUG
        printf("DEBUG\n");
        #endif
        return 0;
}

  • -On n=0~3 进行编译优化

test.c

gcc -S test.c -o test.s
test.s


gcc -S test.c -O3 -o test.s

可见编译优化后将上面复杂代码优化为 int d = 10


静态库

正常开发一个软件需要将头文件放进 include 文件夹,源文件放入 src 文件夹,并可以将源程序编译生成的多个可执行文件放入函数库中,这样在给用户程序时就可以不需要提交源代码。

而函数库分为静态库与动态库(共享库),下面首先介绍静态库。

目录结构如下:

main.c

#include <stdio.h>
#include "main.h"

int main()
{
        int a = 10;
        int b = 5;
        printf("%d %d\n", add(a,b), sub(a,b));
        return 0;
}

main.h

#ifndef MAIN_H
#define MAIN_H
int add(int x, int y);
int sub(int x, int y);
#endif

acc.c

#include "main.h"
int add(int x, int y)
{
        return x + y;
}

sub.c

#include "main.h"
int sub(int x, int y)
{
        return x - y;
}

gcc *.c -c -I./include 生成对应.o文件
ar rcs libmytest.a *.o 生成静态库libmytest.a
gcc main.c -L./lib -lmytest -Iinclude -o app 生成可执行文件app


动态库(共享库)

除了静态库,还有动态库,其各有各自的优点和缺点。

目录结构如下:

  • gcc -fPIC *.c -c -Iinclude 生成与位置无关的目标文件,这是因为动态库的目标文件是运行时动态加载进目标程序中,所以需要目标文件与位置无关,即相对位置。
  • gcc -shared -o libmytest.so *.o 制作动态库
  • gcc main.c -L./lib -lmytest -I./include -o app 使用动态库
  • .app 执行生成的可执行文件
    这是就有问题出现了,app运行失败,我们也可以 ldd app 发现 libmytest 找不到

    这是因为没有给动态链接器(ld-linux.so.2)指定好动态库libmytest.so的路径。

这里有四个解决方案,第一个方案是临时方案,二至四个方法都是永久设置,其中永久设置最好使用第四种方法。

  1. 临时设置:export LD_LIBRARY_PATH=库路径,将当前目录加入环境变量,但是终端退出了就无效了。
  2. 永久设置:将上条写入家目录下.bashrc文件中
  3. 粗暴设置:直接将libmytest.so文件拷贝到/usr/lib/【/lib】目录下。(受libc库的启发)
  4. libmytest.so 所在目录的绝对路径追加入到 /etc/ld.so.conf 文件,
    使用 sudo ldconfig -v 更新

gdb调试

getmin.h

#ifndef GETMIN_H
#define GETMIN_H
int getmin(int a[], int l, int r);
#endif

getmin.c

#include "getmin.h"
int getmin(int a[], int l, int r)
{
        int i;
        int mi = a[l];
        int pos = l;
        for(i=l+1; i<r; ++i)
        {
                if(a[i] < mi)
                {
                        mi = a[i];
                        pos = i;
                }
        }
        return pos;
}

sort.h

#ifndef SORT_H
#define SORT_H
void sort(int a[], int n);
#endif

sort.c

#include "getmin.h"
void sort(int a[], int n)
{
        int i;
        for(i=0; i<n; ++i)
        {
                int pos = getmin(a, i, n);
                int temp = a[pos];
                a[pos] = a[i];
                a[i] = temp;
        }
        return ;
}

main.c

#include <stdio.h>
#include "sort.h"

int main()
{
        int a[10] = {5, 1, 3, 2, 4, 6, 9, 0, 8, 7};
        for(int i=0; i<10; ++i)
                printf("%d ", a[i]);
        printf("\n");
        sort(a, 10);
        for(int i=0; i<10; ++i)
                printf("%d ", a[i]);
        printf("\n");
}

我们可以使用 gcc *.c -g -o app 生成可调试的程序。

然后使用 gdb app 可以开始调试

  • l 列出代码(包含main函数的程序)的10行

  • l 文件名:行数 显示文件名行数开始的十行代码

  • l 文件名:函数名 显示文件名里的函数

  • Enter回车 可以执行前一条命令

  • start 单步执行,运行程序,n 执行下一行

  • b 行号 设置断点,r 运行程序在断点处停止,断点可以加判断比如 b 行号 if i==5

  • p 变量名 可以打印变量,display 变量名 可以追踪变量

  • s 执行下一行,且可进入函数体内部,finish 可以结束当前函数,返回到函数调用点,这时需要断点代码执行完毕或者删除断点,删除断点可以先使用 info b 查看断点编号,然后d 断点编号删除该断点

  • set var i=5 可以设置变量 i 为5

  • undisplay 编号 可以取消追踪变量,可以使用 info display 查找变量编号。

  • ``

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值