Linux学习打卡第四天之makefile实验

参考:https://blog.csdn.net/u010655348/article/details/50815370

首先需要弄明白程序编译执行的大致过程:

  • 预处理阶段
  • 词法和语法分析阶段
  • 编译阶段 :对.c文件进行编译,首先编译成汇编语句,再编译成跟CPU相关的二进制码,最后生成的是目标文件(后缀名为.obj)
  • 链接阶段 :将各个目标文件中的各段代码进行绝对地址定位,生成跟特定平台相关的可执行文件(后缀名为.exe)

.h是头文件,内含函数声明、宏定义、结构体定义等等
.c文件是程序文件,内含函数实现、变量定义等等

其实合在一起写也可以,那么分开写的理由何在呢?原因如下:

  • 如果在头文件中实现一个函数体,如果在多个cpp文件中引用它,而且又同时编译多个cpp文件从而生成多个目标文件,那么,在每个引用此头文件的cpp文件所生成的目标文件中都有一份这个函数的代码,如果这段函数又没有定义成局部函数,那么在链接时,就会发现多个相同的函数,就会报错,函数重复定义
  • 如果在头文件中定义全局变量,势必会对次全局变量赋初值,那么在多个引用此头文件的cpp文件中同样存在相同变量名的拷贝,关键是此变量被赋了初值,所以编译器就会将次变量放入data域,最终在链接阶段,会在data域中存在多个相同的变量,它无法将这些变量统一成一个变量。但是对于声明一个变量,这个变量在头文件中没有初值,编译器就会将它放入BSS域,链接器会对BSS域的多个同名变量仅分配一个存储空间.
  • 如果在cpp文件中声明宏、结构体、函数等,那么我要在另一个cpp文件中引用相应的宏、结构体、函数,就必须再做一次声明,如果我改了一个cpp文件的一个声明却忘了改其他cpp文件中的,就会出现问题。所以把这些公用的东西统一放在一个文件里,以后想用它的cpp文件只需要一个#include就可以了
  • 如果在头文件中声明结构体、函数等,当你需要将你的代码封装成一个库,让别人来用你的代码,你又不想公布源码,那么人家如何来用你的库呢?这时候就可以利用头文件,从头文件中看函数原型,了解如何调用

比如,我在aaa.h里定义了一个函数的声明,然后我在同一个目录下建立aaa.c,里面定义了这个函数的实现,然后在main函数所在的.c文件中#include该头文件aaa.h,然后我就可以使用这个函数了。main在运行时就会找到这个定义了该函数的aaa.c文件,主要过程如下:

  • 1、main函数为标准C/C++程序入口,编译器会先找到main函数所在文件;
  • 2、假定编译程序编译myproject.c(含有主函数main)时,发现他include了mylib.h(其中声明了函数void tests()),那么此时编译器将按照事先设定的路径(include路径列表及代码文件所在的路径)查找与之同名的实现文件mylib.c,如果找到该文件并在其中发现已经该函数的实现代码,则继续编译;如果在指定目录找不到实现文件,或者在找到的实现文件及其后续的各include文件中未找到实现代码,则返回一个编译错误.其实include的过程完全可以看成是一个”文件拼接”的过程.

上面过程就是一个动态链接。

还有一种静态链接,在这种方式下,我们所要做的,就是写出包含函数,类等等声明的头文件(a.h,b.h,…),以及他们对应的实现文件(a.cpp,b.cpp,…),编译程序会将其编译为静态的库文件(a.lib,b.lib,…)。在随后的代码重用过程中,我们只需要提供相应的头文件(.h)和相应的库文件(.lib),就可以使用过去的代码了。

相对动态方式而言,静态方式的好处是实现代码的隐蔽性,即C 中提倡的"接口对外,实现代码不可见"。有利于库文件的转发.

下面写一个具体的实例。

1、普通编译链接

1、分别创建头文件.h、同名实现文件.c、主函数文件.c

vim add_minus.h

#ifndef __ADD_MINUS_H__
#define __ADD_MINUS_H__

int add(int a,int b);
int minus(int a,int b);

#endif /*__ADD_MINUS_H__*/
vim add_minus.c

#include "add_minus.h"

int add(int a,int b)
{
    return a+b;
}

int minus(int a,int b)
{
    return a-b;
}
vim main.c

#include "stdio.h"
#include "add_minus.h"
 int main(void)
 {
    int rst;
    printf("Hello Cacu!\n");

    rst=add(3,2);
    printf("3 + 2 = %d\n",rst);

    rst=minus(3,2);
    printf("3 - 2 = %d\n",rst);

    return 0;
 }

2、将同名实现文件编译成目标文件,结果得到add_minus.o

gcc -c add_minus.c

3、将主函数文件编译成目标文件,结果得到main.o

gcc -c main.c

4、将各个目标文件联合链接成可执行文件

gcc -o main main.o add_minus.o
./main
2、静态链接

1、同上
2、同上
3、将add_minus.o打包到静态库中,将会生成 libadd_minus.a静态库文件:

ar rc libadd_minus.a add_minus.0

4、将目标文件main.o和静态库文件进行链接并执行:

gcc -o main2 main.o -L./ -ladd_minus  //-L./表明库文件位置在当前文件夹-ladd_minus表示链接libadd_minus.a,使用“-l”参数时,前缀“lib”和后缀“.a”是需要省略的。
./main2
3、动态链接

1、分别创建头文件.h、同名实现文件.c、主函数文件.c

vim multi_div.h

#ifndef __MULTI_DIV_H__
#define __MULTI_DIV_H__

int multi(int a,int b);
int div(int a,int b);

#endif /*__MULTI_DIV_H__*/
vim multi_div.c

#include "multi_div.h"

int multi(int a,int b)
{
    return a×b;
}

int div(int a,int b)
{
    return a/b;
}
vim main.c

#include "stdio.h"
#include "multi_div.h"
 int main(void)
 {
    int rst;
    printf("Hello Cacu!\n");

    rst=multi(3,2);
    printf("3 × 2 = %d\n",rst);

    rst=div(6,2);
    printf("6 / 2 = %d\n",rst);

    return 0;
 }

2、将multi_div.c编译成动态链接库,生成libmulti_div.so文件:

gcc multi_div.c -fPIL -shared -o libmulti_div.so

3、将主函数文件编译成目标文件,结果得到main.o

gcc -c main.c

4、将目标文件main.o和动态链接库文件进行链接并执行:

gcc -o main3 main.o -L./ -lmulti_div
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/jqq/Makefile_example/chapter0/   //在LD_LIBRARY_PATH中指定库文件路径,否则会因生成的动态库不在库文件搜索路径中而报错
./main3
4、动态库和静态库混合链接

1、重写main.c文件,其他文件内容不变

vim main.c

#include "stdio.h"
#include "multi_div.h"
 int main(void)
 {
    int rst;
    printf("Hello Cacu!\n");

    rst=multi(3,2);
    printf("3 × 2 = %d\n",rst);

    rst=div(6,2);
    printf("6 / 2 = %d\n",rst);

    return 0;
 }

2、将主函数文件编译成目标文件,结果得到main.o

gcc -c main.c

3、将目标文件main.o同时与静态链接库和动态链接库进行链接并执行:

gcc -o main4 main.o -L./ -ladd_minus -lmulti_div
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/jqq/Makefile_example/chapter0/   //如果此前修改过这里就不用修改了
./main4
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux命令和Makefile是计算机科学中非常重要的两个主题。学习这两个主题可以帮助我们更好地理解操作系统和软件开发过程。 在学习Linux命令方面,我们需要掌握一些基本的命令,如ls、cd、mkdir、rm等,以及一些高级命令,如grep、awk、sed等。这些命令可以帮助我们在Linux系统中进行文件管理、文本处理、进程管理等操作。 在学习Makefile方面,我们需要了解Makefile的基本语法和结构,以及如何使用Makefile来编译和构建软件项目。Makefile可以帮助我们自动化构建和管理软件项目,提高开发效率和代码质量。 学习Linux命令和Makefile可以通过实验来进行。我们可以在Linux系统中使用命令行界面进行实验,或者使用虚拟机来模拟Linux环境。在实验中,我们可以通过编写脚本和Makefile来自动化执行一些任务,如编译和运行程序,处理文本文件等。 总之,学习Linux命令和Makefile是非常有用的,可以帮助我们更好地理解计算机系统和软件开发过程。 ### 回答2: Linux命令是指在Linux系统下使用的命令行工具,它是Linux系统管理的重要组成部分,也是程序员工作的必备工具之一。学习Linux命令,需要掌握Linux系统的基本架构和操作逻辑,并且要了解常用的命令工具的使用方法,例如cd、ls、grep、chmod等等。在学习过程中,可以先从命令的基本语法入手,逐步深入到各种实用技巧和高级用法,例如管道操作、正则表达式和shell脚本编程等。可以通过参考相关书籍、在线教程和实践操作来提高自己的Linux命令水平。 Makefile是一种用于自动化构建和编译程序的工具,它可以定义程序的编译规则和依赖关系,并且可以根据这些规则和关系自动化执行编译和构建的过程。在学习Makefile的过程中,需要先了解基本语法和规则,例如目标、依赖和命令等等。接着需要学习如何定义和使用变量、宏和函数等高级特性,以及如何引入外部文件和库的支持。学习Makefile还需要掌握一些调试和优化技巧,例如使用Makefile的自带参数和规则等功能。通过不断实践和实验,可以提高自己的Makefile技能,掌握快速开发和构建程序的能力。 在进行Linux命令和Makefile学习实验中,可以选取一些具有实际意义的任务,例如编译和运行一个C程序、构建和打包一个JavaScript库、或者使用Makefile对一个网站进行自动化发布等等。在实验中需要注意掌握一些基本的工具和技巧,例如文本编辑器的使用、版本控制工具的使用、Debug工具的使用等等。同时需要注重实验的记录和归纳,例如记录遇到的问题和解决方法、维护Makefile的文档和注释、整理并分享实验的心得和体验等等。通过实验的逐步深入和内容丰富,可以提高学习的效率和成果。 ### 回答3: Linux是一种自由配置和自由使用的操作系统,这种自由是指用户可以自由地获取、使用、复制、学习、修改和分发Linux及其相关软件的权利。对于学习Linux命令,我们可以从以下几方面入手: 1.基础命令:例如cd、ls、mkdir、cp、mv、rm、cat、tee、grep、awk等等。这些命令是 Linux 常用的命令,掌握了这些基础命令的使用方法,可以让我们更好地理解操作系统。 2.管道和重定向:管道和重定向是Linux命令中非常重要的一部分,例如用“|”将命令串联起来,或者使用“>”将输出重定向到文件中等。这些操作能够非常方便地对命令的输出进行处理,从而提高我们的工作效率。 3.shell脚本编程:shell脚本是一种可以自动化完成一系列操作的脚本语言,通过学习shell脚本编程,我们可以更加高效地完成重复性的工作。例如,同时部署多台服务器、修改配置文件等。 随着开发环境的复杂,makefile是一个非常重要的工具。Makefile是一个文件,其中包含有关如何编译和链接程序的信息,它可以基于文件之间的依赖关系进行操作。通过学习makefile,我们可以了解文件之间的依赖关系,从而实现更快速、更高效的编译和构建项目。 学习makefile的步骤如下: 1.了解makefile的语法:makefile主要有三个元素:变量、目标和规则。变量用于定义需要使用到的路径、编译器等信息,目标表示需要构建的主要目标文件,规则定义了如何生成目标文件。 2.编写makefile:在掌握makefile的语法后,我们需要开始动手编写makefile。我们可以从自己的项目中入手,判断项目中不同文件之间的依赖关系,并编写makefile来自动化构建这些文件。 3.调试makefile:在编写makefile时,难免会出现语法错误或者逻辑错误,因此我们需学会调试makefile。我们可以通过命令行参数和makefile内置函数进行调试。 总的来说,Linux命令和makefile学习都需要付出较多的时间和精力,但可以帮助我们更好地理解和使用Linux系统,提高我们的工作效率,值得一生投入。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值