利用makefile文件编译c++源文件

简介: makefile文件编译c++ 生成一个so库文件

一、基础知识介绍
在Linux下,要想编译c++项目,并生成可执行文件,需要使用到makefile文件。
c++从代码到可执行文件,经历了编译和链接两阶段。

编译阶段:
编译时,编译器检查语法、函数的申明等是否正确。对于函数申明,一般是你需要告诉编译器头文件的所在位置。如果所有的检查都正确,编译器就可以编译出相应的中间文件(即.o文件)。一般来说,每个源文件都应该对应于一个.o文件。
链接阶段:
根据编译结果(即.o文件)生成可执行文件。

如下图所示:
20170707233901025

这里写图片描述

注意:在Linux下,可以通过“ldd 可执行文件名” 查看编译链接此可执行文件时,用到了那些系统库。
二、编写makefile文件

#定义变量,使用变量:$(变量名)
CC=g++
#定义变量srcs,表示需要编译的源文件,需要表明路径,如果直接写表示这些cpp文件和makefile在同一个目录下,如果有多个源文件,每行以\结尾
SRCS=main.cpp\
        udp.cpp
#定义变量OBJS,表示将原文件中所有以.cpp结尾的文件替换成以.o结尾,即将.cpp源文件编译成.o文件
OBJS=$(SRCS:.cpp=.o)
#定义变量,表示最终生成的可执行文件名
EXEC=maincpp
#start,表示开始执行,冒号后面的$(OBJS)表示要生成最终可执行文件,需要依赖那些.o文件的
start:$(OBJS)
        #相当于执行:g++ -o maincpp .o文件列表,-o表示生成的目标文件
        $(CC) -o $(EXEC) $(OBJS)
#表示我的.o文件来自于.cpp文件
.cpp.o:
        #如果在依赖关系中,有多个需要编译的.cpp文件,那么这个语句就需要执行多次。-c $<指的是需要编译的.cpp文件,-o $@指这个cpp文件编译后的中间文件名。比如在依赖关系中,有a.cpp和b.cpp,即$(OBJS)的值为a.cpp b.cpp,那么这条语句需要执行2次,第一次的$@为a.o,$<为a.cpp,第二次的$@为b.o,$<为b.cpp。-c表示只编译不链接,-o表示生成的目标文件
        #-DMYLINUX:-D为参数,MYLINUX为在cpp源文件中定义的宏名称,如#ifdef MYLINUX。注意-D和宏名称中间没有空格
        $(CC) -o $@ -c $< -DMYLINUX

#执行make clean指令
clean:
        #执行make clean指令时,需要执行的操作,比如下面的指令时指删除所有.o文件
        rm -rf \$(OBJS)

注意:上述文件中的cpp文件表示是c++源文件,如果是c源文件,一般文件名为”文件名.c“,如果要利用上述makefile文件编译链接c源文件,只需要将上述makefile文件中的cpp修改成c,g++修改成gcc即可。

上述makefile文件配置中,如果某个cpp文件被修改过,那么在执行make时,将会只编译修改过的cpp文件,但链接时,还是需要用到所有的.o中间文件。

三、生成一个so库文件
比如,我们在实际开发过程中,写了一个公共的功能,那么这个公共的功能如何在所有需要用到的项目中使用,一般有两种方式,一种是源码共享,即将源码提供给使用方;另外一种是提供一个类似于Linux提供的库文件的so文件。这里我们就来用C语言编写并生成一个so库文件。
假如我们需要打包成库文件的源码为mylib.c,里面提供了一个比较大小的函数,如下所示:

mylib.c:
int max(int x,int y){
    if(x>y){
        return x;
    }
    return y;
}

修改makefile文件:

#c编译器,g++是c++的编译器
CC=gcc
#假设我们的源文件名为mylib.c的c语音编写的
SRCS=mylib.c

OBJS=$(SRCS:.c=.o)
#这里的命名有一个规则,以lib开头,以.so结尾,必须这样
EXEC=libmylib.so

start:$(OBJS)
        #-shared表示最终链接成so共享文件
        $(CC) -o $(EXEC) $(OBJS) -shared
.c.o:
        #-fPIC表在编译时生成和位置无关的代码
        $(CC) -o $@ -c $< -fPIC
clean:
        rm -rf $(OBJS)

当执行完make操作后,会在目录下生成一个叫libmylib.so的文件,如果要让其他项目使用这个so文件,还必须为这个so文件编写头文件,比如mylib.h

mylib.h
#ifndef MYLIB_H
#define MYLIB_H

#ifdef __cplusplus //表示是一个c++程序
extern "C"{
#endif

int max(int x,int y);

#ifdef __cplusplus
}
#endif
#endif

使用库函数,比如代码为main.cpp

main.cpp

#include <iostream>
#include "mylib.h" //注意,这个引号里的文件名应该包括其路径,直接这样写表示其和当前main.cpp文件在同一个路径下

using namespace std;


int main(){
        int x=max(23,54);
        cout<<x<<endl;
}

为main.cpp编写makefile文件

makefile:
CC=g++

SRCS=main.cpp

OBJS=$(SRCS:.cpp=.o)

EXEC=maincpp

start:$(OBJS)
        #-L../表示需要链接库的地址,可以是相对路径或者绝对路径,-lmylib为需要使用的库,注意这里的mylib是指libmylib.so文件,去掉前面的lib和.so后缀名后的名字
        $(CC) -o $(EXEC) $(OBJS) -L../ -lmylib
.cpp.o:
        $(CC) -o $@ -c $<
clean:
        rm -rf $(OBJS)

经过上述步骤后,执行make指令最后生成maincpp可执行文件。
但在执行./maincpp时提示找不到libmylib.so库文件。用 “ldd 可执行文件” 时,也显示这个自定义的so库文件找不到。

其实在上述makefile的$(CC) -o $(EXEC) $(OBJS) -L../ -lmylib部分时,只是指明了链接时的库文件路径,而没有指明运行时库文件路径。
在使用库文件(so文件)时,需要指明链接时的so文件路径和运行时的so文件路径。-L表示的是链接时库文件的路径。

转自:http://blog.csdn.net/zhaocuit/article/details/74782789

相关文章
|
2月前
|
C++ Windows
.NET Framework安装不成功,下载`NET Framework 3.5`文件,Microsoft Visual C++
.NET Framework常见问题及解决方案汇总,涵盖缺失组件、安装失败、错误代码等,提供多种修复方法,包括全能王DLL修复工具、微软官方运行库及命令行安装等,适用于Windows系统,解决应用程序无法运行问题。
195 3
|
3月前
|
存储 算法 安全
c++模板进阶操作——非类型模板参数、模板的特化以及模板的分离编译
在 C++ 中,仿函数(Functor)是指重载了函数调用运算符()的对象。仿函数可以像普通函数一样被调用,但它们实际上是对象,可以携带状态并具有更多功能。与普通函数相比,仿函数具有更强的灵活性和可扩展性。仿函数通常通过定义一个包含operator()的类来实现。public:// 重载函数调用运算符Add add;// 创建 Add 类的对象// 使用仿函数return 0;
121 0
|
8月前
|
自然语言处理 编译器 C语言
为什么C/C++编译腰要先完成汇编
C/C++ 编译过程中先生成汇编语言是历史、技术和实践的共同选择。历史上,汇编语言作为成熟的中间表示方式,简化了工具链;技术上,分阶段编译更高效,汇编便于调试和移植;实践中,保留汇编阶段降低了复杂度,增强了可移植性和优化能力。即使在现代编译器中,汇编仍作为重要桥梁,帮助开发者更好地理解和优化代码。
124 25
为什么C/C++编译腰要先完成汇编
|
10月前
|
自然语言处理 编译器 Linux
告别头文件,编译效率提升 42%!C++ Modules 实战解析 | 干货推荐
本文中,阿里云智能集团开发工程师李泽政以 Alinux 为操作环境,讲解模块相比传统头文件有哪些优势,并通过若干个例子,学习如何组织一个 C++ 模块工程并使用模块封装第三方库或是改造现有的项目。
751 56
|
8月前
|
存储 算法 安全
基于哈希表的文件共享平台 C++ 算法实现与分析
在数字化时代,文件共享平台不可或缺。本文探讨哈希表在文件共享中的应用,包括原理、优势及C++实现。哈希表通过键值对快速访问文件元数据(如文件名、大小、位置等),查找时间复杂度为O(1),显著提升查找速度和用户体验。代码示例展示了文件上传和搜索功能,实际应用中需解决哈希冲突、动态扩容和线程安全等问题,以优化性能。
|
10月前
|
自然语言处理 编译器 Linux
|
11月前
|
Linux C++
Linux c/c++文件虚拟内存映射
这篇文章介绍了在Linux环境下,如何使用虚拟内存映射技术来提高文件读写的速度,并通过C/C++代码示例展示了文件映射的整个流程。
298 0
|
11月前
|
Linux C++
Linux c/c++文件移动
这篇文章介绍了在Linux环境下,使用C/C++语言通过命令方式和文件操作方式实现文件移动的方法。
283 0
|
7月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
3月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
89 0