最近搞开发, 遇到C++与C相互调用的编译问题, 以前就对此类问题非常头痛, 借此机会梳理一下
1.预处理部分(来源:<c和指针>chapter14)
编译一个C程序涉及很多步骤, 其中第一个步骤被称为预处理(preprocessing)
预处理主要任务包括:删除注释, 插入被#include指令包含的头文件内容, 宏替换, 条件编译
【条件编译】
#if constant-expression
statements
#endif
其中constant-expression为常量表达式(或者是字面值常量, 或者是#define定义的符号)
如果constant-expression值为真(非零), 那么statements部分被正常编译
否则预处理器就删除它们
#ifdef symbol
测试一个符号是否被定义
【注意】
为了帮助记住复杂的嵌套指令, 为每个#endif加上一个注释标签是很有帮助的
标签的内容就是#ifdef后面那个表达式, 如:
#ifdef OPTION1
code for OPTION1
#else
code for OTHER
#endif /*OPTION1*/
【文件包含】
#include原理:
预处理器找到所要包含的头文件, 删除该条#include指令, 并用包含文件的内容取代之
【注意】:
#include头文件的开销在程序被编译时才存在, 对运行效率并无影响
当头文件被包含时, 位于头文件的所有内容都要被编译, 一个头文件若被包含到10个源文件中, 它实际被编译了10次
参与编译的实际上只有源文件(将头文件的内容复制到了源文件中)
函数库文件包含
形如:#include <stdio.h>
一般在UNIX系统上的C编译器在/user/include目录下查找函数库头文件
本地文件包含
形如:#include "head.h"
处理本地头文件的一种常见策略就是在源文件所在的当前目录进行查找, 如果该头文件并未找到, 编译器就像查找函数库头文件一样在标准位置查找本地头文件
可以通过makefile等方式指定头文件
文件嵌套包含
标准要求编译器必须支持至少8层的头文件嵌套,但并没有限定嵌套深度的最大值
文件嵌套包含的不利因素有两点:依赖关系复杂, 容易被重复包含
文件重复包含解决方法
利用条件编译
#ifndef _HEADERNAME_H
#define _HEADERNAME_H 1
/*content*/
#endif /*_HEADERNAME_H*/
【注意】:
#define _HEADERNAME_H 1与#define _HEADERNAME_H效果相同, 虽然后者被定义为一个空字符串, 但仍然被定义了
预处理器将读入整个头文件, 即使这个头文件的所有内容将被忽略(我的理解是预处理器首先完成头文件的替换工作, 然后根据条件编译, 编译该编的, 删除该删除的)
------------------------------------------------------------------------------------------------------
2.函数声明的作用(来源:<谭浩强C语言程序设计第四版>)
被调用函数必须满足如下条件:
首先被调用函数必须是已经定义的函数
如果使用库函数, 应该在文件开头用#include指令包含库函数头文件
如果是自己定义的函数, 且该函数定义的位置在调用它函数的后面(同一文件), 应该在主调函数中对被调函数做声明
声明的作用在于将函数信息告知编译系统, 以便在调用时能正确识别函数且检查调用是否合法
检查规则:
在检查函数调用时, 要求函数名, 函数类型, 参数个数, 参数顺序, 参数类型必须与函数声明一致
在函数声明时, 不检查参数名, 因此在函数声明时, 形参名可写可不写, 形参名是什么都无所谓
------------------------------------------------------------------------------------------------------
3.编译简单原理(来源:CSDN博客)
未处理符号表,导出符号表,地址重定向表
【转】:
https://blog.csdn.net/jiange_zh/article/details/52187611
------------------------------------------------------------------------------------------------------
4.extern "C"与c++调用c函数(来源:网络博客)
【转】:
https://www.jb51.net/article/37386.htm
https://www.jianshu.com/p/5d2eeeb93590#
【小总结】:
结合3, 4知识点, 在c++中编译的函数放到未解决符号表里的是以_foo_int_int形式存在, 而在c中编译的函数放到导出符号表中却是以_foo形式存在, 那么链接器在所有导出符号表中找不到_foo_int_int的实现会导致链接错误
------------------------------------------------------------------------------------------------------
5.c++中调用c实现函数的小例
root@ubuntu:/shiyan/shiyan278# g++ main.cpp func.c -o c.out
root@ubuntu:/shiyan/shiyan278#
root@ubuntu:/shiyan/shiyan278#
root@ubuntu:/shiyan/shiyan278# ./c.out
in c++ main...
value = 8
root@ubuntu:/shiyan/shiyan278#
func.h
1 #ifndef __FUNC_H__
2 #define __FUNC_H__ 1
3
4 #ifdef __cplusplus
5 extern "C"{
6 #endif
7
8 int func(int, int);
9
10 #ifdef __cplusplus
11 }
12 #endif //End __cplusplus
13 #endif //End __FUNC_H__
func.c
1 #include <stdio.h>
2 #include "func.h"
3
4 int func(int x, int y)
5 {
6 return (x + y);
7 }
main.cpp
1 #include <iostream>
2 #include <stdio.h>
3 #include "func.h"
4
5 using namespace std;
6
7 int main(void)
8 {
9 cout << "in c++ main..." << endl;
10 int value = func(3, 5);
11 cout << "value = " << value << endl;
12 return 0;
13 }