c c++ 混合编译
单个源文件生成可执行程序
下面是一个保存在文件 helloworld.cpp 中一个简单的 C++ 程序的代码:
| 1 2 3 4 5 6 7 8 9 | /* helloworld.cpp */ #include <iostream>   intmain(intargc,char*argv[])  {          std::cout << "hello, world"<< std::endl;        return(0);  } | 
程序使用定义在头文件 iostream 中的 cout,向标准输出写入一个简单的字符串。该代码可用以下命令编译为可执行文件:
$ g++ helloworld.cpp
编译器 g++ 通过检查命令行中指定的文件的后缀名可识别其为 C++ 源代码文件。编译器默认的动作:编译源代码文件生成对象文件(object file),链接对象文件和 libstdc++ 库中的函数得到可执行程序。然后删除对象文件。由于命令行中未指定可执行程序的文件名,编译器采用默认的 a.out。程序可以这样来运行:
$ ./a.out hello, world
更普遍的做法是通过 -o 选项指定可执行程序的文件名。下面的命令将产生名为 helloworld 的可执行文件:
$ g++ helloworld.cpp -o helloworld
在命令行中输入程序名可使之运行:
$ ./helloworld hello, world
程序 g++ 是将 gcc 默认语言设为 C++ 的一个特殊的版本,链接时它自动使用 C++ 标准库而不用 C 标准库。通过遵循源码的命名规范并指定对应库的名字,用 gcc 来编译链接 C++ 程序是可行的,如下例所示:
$ gcc helloworld.cpp -lstdc++ -o helloworld
选项 -l (ell) 通过添加前缀 lib 和后缀 .a 将跟随它的名字变换为库的名字 libstdc++.a。而后它在标准库路径中查找该库。gcc 的编译过程和输出文件与 g++ 是完全相同的。
在大多数系统中,GCC 安装时会安装一名为 c++ 的程序。如果被安装,它和 g++ 是等同,如下例所示,用法也一致:
$ c++ helloworld.cpp -o helloworld []多个源文件生成可执行程序
如果多于一个的源码文件在 g++ 命令中指定,它们都将被编译并被链接成一个单一的可执行文件。下面是一个名为 speak.h 的头文件;它包含一个仅含有一个函数的类的定义:
| 1 2 3 4 5 6 7 8 9 | /* speak.h */ #include <iostream>   classSpeak  {  public:          voidsayHello(constchar*); }; | 
下面列出的是文件 speak.cpp 的内容:包含 sayHello() 函数的函数体:
| 1 2 3 4 5 6 7 8 | /* speak.cpp */ #include "speak.h"   voidSpeak::sayHello(constchar*str)  {          std::cout << "Hello "<< str << "\n"; } | 
文件 hellospeak.cpp 内是一个使用 Speak 类的程序:
| 1 2 3 4 5 6 7 8 9 10 11 | /* hellospeak.cpp */ #include "speak.h"  intmain(intargc,char*argv[]) {          Speak speak;          speak.sayHello("world");           return(0);  } | 
下面这条命令将上述两个源码文件编译链接成一个单一的可执行程序:
$ g++ hellospeak.cpp speak.cpp -o hellospeak
PS:这里说一下为什么在命令中没有提到“speak.h“该文件(原因是:在“speak.cpp“中包含有”#include"speak.h"“这句代码,它的意思是搜索系统头文件目录之前将先在当前目录中搜索文件“speak.h“。而”speak.h“正在该目录中,不用再在命令中指定了)。
[编辑]源文件生成对象文件
选项 -c 用来告诉编译器编译源代码但不要执行链接,输出结果为对象文件。文件默认名与源码文件名相同,只是将其后缀变为 .o。例如,下面的命令将编译源码文件 hellospeak.cpp 并生成对象文件 hellospeak.o:
$ g++ -c hellospeak.cpp
命令 g++ 也能识别 .o 文件并将其作为输入文件传递给链接器。下列命令将编译源码文件为对象文件并将其链接成单一的可执行程序:
$ g++ -c hellospeak.cpp
$ g++ -c speak.cpp
$ g++ hellospeak.o speak.o -o hellospeak
选项 -o 不仅仅能用来命名可执行文件。它也用来命名编译器输出的其他文件。例如:除了中间的对象文件有不同的名字外,下列命令生将生成和上面完全相同的可执行文件:
$ g++ -c hellospeak.cpp -o hspk1.o
$ g++ -c speak.cpp -o hspk2.o
$ g++ hspk1.o hspk2.o -o hellospeak [编辑]编译预处理
选项 -E 使 g++ 将源代码用编译预处理器处理后不再执行其他动作。下面的命令预处理源码文件 helloworld.cpp 并将结果显示在标准输出中:
$ g++ -E helloworld.cpp
本文前面所列出的 helloworld.cpp 的源代码,仅仅有六行,而且该程序除了显示一行文字外什么都不做,但是,预处理后的版本将超过 1200 行。这主要是因为头文件 iostream 被包含进来,而且它又包含了其他的头文件,除此之外,还有若干个处理输入和输出的类的定义。
预处理过的文件的 GCC 后缀为 .ii,它可以通过 -o 选项来生成,例如:
$ gcc -E helloworld.cpp -o helloworld.ii [编辑]生成汇编代码
选项 -S 指示编译器将程序编译成汇编语言,输出汇编语言代码而後结束。下面的命令将由 C++ 源码文件生成汇编语言文件 helloworld.s:
$ g++ -S helloworld.cpp
生成的汇编语言依赖于编译器的目标平台。
[编辑]创建静态库
静态库是编译器生成的一系列对象文件的集合。链接一个程序时用库中的对象文件还是目录中的对象文件都是一样的。库中的成员包括普通函数,类定义,类的对象实例等等。静态库的另一个名字叫归档文件(archive),管理这种归档文件的工具叫 ar 。
在下面的例子中,我们先创建两个对象模块,然后用其生成静态库。
头文件 say.h 包含函数 sayHello() 的原型和类 Say 的定义:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | /* say.h */ #include <iostream>   voidsayhello(void);   classSay{     private:    char*string;         public:     Say(char*str)          {                  string = str;         }             voidsayThis(constchar*str)         {                 std::cout << str << " from a static library\n";         }             voidsayString(void);  }; | 
下面是文件 say.cpp 是我们要加入到静态库中的两个对象文件之一的源码。它包含 Say 类中 sayString() 函数的定义体;类 Say 的一个实例 librarysay 的声明也包含在内:
| 1 2 3 4 5 6 7 8 9 10 | /* say.cpp */ #include "say.h"   voidSay::sayString() {         std::cout << string << "\n";  }    Say librarysay("Library instance of Say"); | 
源码文件 sayhello.cpp 是我们要加入到静态库中的第二个对象文件的源码。它包含函数 sayhello() 的定义:
| 1 2 3 4 5 6 7 8 | /* sayhello.cpp */ #include "say.h"   voidsayhello()  {          std::cout << "hello from a static library\n";  } | 
下面的命令序列将源码文件编译成对象文件,命令 ar 将其存进库中:$ g++ -c sayhello.cpp $ g++ -c say.cpp $ ar -r libsay.a sayhello.o say.o
程序 ar 配合参数 -r 创建一个新库 libsay.a 并将命令行中列出的对象文件插入。采用这种方法,如果库不存在的话,参数 -r 将创建一个新的库,而如果库存在的话,将用新的模块替换原来的模块。
下面是主程序 saymain.cpp,它调用库 libsay.a 中的代码:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /* saymain.cpp */ #include "say.h"  intmain(intargc,char*argv[])  {          externSay librarysay;             Say localsay = Say("Local instance of Say");            sayhello(); librarysay.sayThis("howdy");            librarysay.sayString();         localsay.sayString();            return(0);  } | 
该程序可以下面的命令来编译和链接:
$ g++ saymain.cpp libsay.a -o saymain
程序运行时,产生以下输出:
hello from a static library howdy from a static library Library instance of Say Local instance of Say
C与C++混合编程
C++ 是在 C 语言的基础上发展起来的。在某种程度上,我们可将 C++ 看做 C 的一种扩展。在本质上,二者的数据类型和函数调用惯例都是一致的,因此 C 与 C++ 混合编译也是很自然的事情。
二者的区别仅在于编译后函数的名字不同──C 简单地使用函数名而不考虑参数的个数或类型,而 C++ 编译后的函数名则总是将参数类型列表作为其一部分。尽管如此,C++ 提供了特殊的机制来声明 C 函数,这意味着一个 C++ 程序可以直接声明和调用 C 函数。
[编辑]C++调用C函数
下面是 C++ 程序调用 C 函数 csayhello() 的一个例子。由于该函数在 C++ 程序内声明时使用了 extern "C",故调用可以直接进行:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | /* cpp2c.cpp */ #include <iostream>   extern"C"voidcsayhello(char*str);  intmain(intargc,char*argv[])  {          csayhello("Hello from cpp to c");           return(0);  } | 
C 函数不需任何特殊处理,其代码如下:
| 1 2 3 4 5 6 7 8 | /* csayhello.c */ #include <stdio.h>   voidcsayhello(char*str)  {          printf("%s\n",str); } | 
下面三条命令编译以上两个文件并将二者链接为一个可执行文件。由于 gcc 和 g++ 的灵活性使得存在很多方法来完成该任务,但这三条命令或许是最常用的:
$ g++ -c cpp2c.cpp -o cpp2c.o
$ gcc -c csayhello.c -o csayhello.o
$ gcc cpp2c.o csayhello.o -lstdc++ -o cpp2c
注意到,在最后链接的时候指定 C++ 标准库是必须的,这是因为我们用的是 gcc 而不是 g++ 调用的链接器。如果使用的是 g++ 的话,C++ 标准库默认会被链接。
最普遍的做法是,将函数声明放到头文件中,然后将所有内容包含在 extern "C" 声明块内。文件内容像下面所示:
| 1 2 3 4 5 6 7 8 9 | extern"C"{        intmlimitav(intlowend, inthighend);           voidupdatedesc(char*newdesc);           doublegetpct(char*name);   }; | 
[编辑]C调用C++函数
要使 C 程序能够调用 C++ 中函数的话,C++ 提供一个符合 C 调用惯例的函数是必须的。下面的例子演示了在 C++ 内创建 C 函数的语法:
| 1 2 3 4 5 6 7 8 9 10 11 12 | /* cppsayhello.cpp */ #include <iostream>     extern"C" voidcppsayhello(char*str);    voidcppsayhello(char*str) {         std::cout << str << "\n"; } | 
尽管函数 cppsayhello() 通过 extern "C" 声明为 C 函数,事实上它是 C++ 源代码的一部分,这意味着函数体内是真正的 C++ 代码。在函数内你可以自由地创建和析构对象。如果你要在 cppsayhello() 内调用 C 函数的话,将其声明为 extern "C" 是必须的。否则,编译器会将作为一个 C++ 函数并相应地更改函数名。
下面是调用 C++ 函数 cppsayhello() 的 C 程序:
| 1 2 3 4 5 6 7 8 | /* c2cpp.c */ intmain(intargc,char*argv[])  {          cppsayhello("Hello from C to C++");             return(0); } | 
下面的命令编译并链接生成c2cpp:
$ g++ -c cppsayhello.cpp -o cppsayhello.o
$ gcc -c c2cpp.c -o c2cpp.o
$ gcc cppsayhello.o c2cpp.o -lstdc++ -o c2cpp
c c++ 混合编译的更多相关文章
- makefile多目录的.c 格式.cpp混合编译
		# # c.cpp混合编译的makefile模板 # # BIN = test.exe CC = gcc CPP = g++ #这里只加入库头文件路径及库路径 INCS = -I"c:/mi ... 
- VS2005混合编译ARM汇编代码-转
		原文地址:http://blog.csdn.net/annelcf/article/details/5468093 公司HW team有人希望可以给他们写一个在WinCE上,单独读写DDR的工具,以方 ... 
- 混合编译.c/.cpp与.cu文件
		混合编译.c/.cpp与.cu文件 项目中用到cuda编程,写了kernel函数,需要nvcc编译器来编译..c/.cpp的文件,假定用gcc编译. 如何混合编译它们,整体思路是:.cu文件编译出的东 ... 
- .Net Core Razor 预编译,动态编译,混合编译
		预编译 预编译是ASP .Net Core的默认方式.在发布时,默认会将系统中的所有Razor视图进行预编译.编译好的视图DLL统一命名为 xxx.PrecompiledViews.dll 或者 xx ... 
- AOT和JIT以及混合编译的区别、优劣
		AOT,JIT是什么? JIT,即Just-in-time,动态(即时)编译,边运行边编译: AOT,Ahead Of Time,指运行前编译,是两种程序的编译方式 区别 这两种编译方式的主要区别在于 ... 
- 16bit C & ASM 如何混合编译?
		起源: 今天在看以前没看完的一本书<图形程序开发人员指南>,在做里面的例子. 第一章就出问题了,一个例子“L1_2.c, L1_3.asm" ,这是C程序和ASM汇编程序的混合编 ... 
- makefile编写---.c .cpp 混合编译makefile 模板
		# c.cpp混合编译的makefile模板 # # BIN = client_system BASE_INSTALL_DIR := /opt/arm-2009q1 BUILD_TOOL_DIR := ... 
- 【问题】Java和Scala混合编译下无法正常使用lombok的问题
		工作中有java和scala和混合编译的工程,最近遇到一个问题,就是工程中有依赖java bean的scala文件,编译过程中发现编译器无法找到Java bean 中 lombok生成的getter, ... 
- xmake v2.6.1 发布,使用 Lua5.4 运行时,Rust 和 C++ 混合编译支持
		xmake 是一个基于 Lua 的轻量级跨平台构建工具,使用 xmake.lua 维护项目构建,相比 makefile/CMakeLists.txt,配置语法更加简洁直观,对新手非常友好,短时间内就能 ... 
随机推荐
- vue 过滤器基本使用
			Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化,例如时间戳格式化. 过滤器可以用在: 双花括号插值 v-bind 表达式 (2.1.0+ 开始支持). 过滤器应该被添加在 JavaSc ... 
- 懒人小工具1:winform自动生成Model,Insert,Select,Delete以及导出Excel的方法
			懒人小工具2:T4自动生成Model,Insert,Select,Delete以及导出Excel的方法 github地址:https://github.com/Jimmey-Jiang/J ... 
- webpack教程(一)——初体验
			首先全局安装webpack,再npm初始化一个项目,并局部安装webpack开发工具 $ npm install webpack -g npm init (项目名称) $ npm install we ... 
- mysql 多主
			原理:多个msyql/mariadb之间可以实时同步,任意节点的操作可以立即同步到其他节点,底层采用galera插件同步,类似rsync,上层mysql相对于galera是透明的,可以实现多节点同时读 ... 
- WinForm多线程+委托防止界面假死
			当有大量数据需要计算.显示在界面或者调用sleep函数时,容易导致界面卡死,可以采用多线程加委托的方法解决 using System; using System.Collections.Generic ... 
- fft的实现
			private static Complex[] FFT1(Complex[] f) { int N=f.length; int power= (int) (Math.log10(N)/Math.lo ... 
- Junit4测试用例
			一.题目简介 测试一元一次方程的求解 二.源码的github链接 https://github.com/liujing1994/test1 三.所设计的模块测试用例.测试结果截图 一元一次方程测试 ... 
- 七牛云域名DV SSL证书申请流程以及CDN融合加速配置
			从2017年起,苹果ios以及微信小程序都陆续要求请求连接request地址是使用HTTPS协议的.所以在项目开发阶段就要考虑解决https的问题,同时这也是为项目实际安全所考虑.最近我也是在折腾项目 ... 
- ES6.0简单了解
			Case 1:let.const var的缺陷 1.可以重复声明变量 2.无法限制修改(没有常量) 3.没有块级作用域 let和const可以弥补var的缺陷 let: 不能重复声明,用来声明变量,声 ... 
- realm vs. domain
			从wiki的角度:https://wikidiff.com/domain/realm domain是物理的,realm是抽象的,都是领域. domain是一个人或组织拥有或控制的地理区域,而realm ... 
