一、概述
在C语言的头文件中,经常可以看到如下的代码,那这个是什么作用呢?
#ifdef __cplusplus extern "C" { #endif /*...*/ #ifdef __cplusplus } #endif
extern "C"起作用的时候是在:C++调用C中的函数。由于C++和C是两种不同的编译和连接方法,所以在交叉调用的时候,就需要增加一些机制起到让两者无缝兼容的目的。而extern "C"正是这一种机制,规定在编译C++源文件时,那些调用自C文件的部分(需要用extern "C"修饰其在头文件中的声明),按照与C文件编译兼容的方式进行。
那么显然在不涉及C++调用C中函数的情况,extern "C"是不起作用的。在编译C文件的时候,编译器是不会自动添加“__cplusplus”的宏定义;而在编译C++文件的时候,编译器会自动的对“__cplusplus”进行宏定义。
二、C++的编译
1、测试源码
- config.c
void config(void) { return ; }
- config.h
#ifndef __CONFIG_H #define __CONFIG_H extern void config(void); #endif
- main.cpp
#include "config.h" int main(void) { ... config(); ... }
2、测试
使用arm-none-eabi-gcc交叉编译链对以上源码进行编译,arm-none-eabi-gcc工具编译config.c,arm-none-eabi-g++编译main.cpp,结果如下:
config.o.lst
94 .section .text.config,"ax",%progbits 95 .align 2 96 .global config 97 .thumb 98 .thumb_func 100 config: 101 .LFB30: 100 config: 101 .LFB30: 61:../User/config.c **** 62:../User/config.c **** void config(void) 63:../User/config.c **** { 102 .loc 1 63 0 103 .cfi_startproc 104 @ args = 0, pretend = 0, frame = 0 105 @ frame_needed = 1, uses_anonymous_args = 0 106 @ link register save eliminated. 107 0000 80B4 push {r7} 108 .cfi_def_cfa_offset 4 109 .cfi_offset 7, -4 110 0002 00AF add r7, sp, #0 111 .cfi_def_cfa_register 7 64:../User/bsp_led.c **** return ; 112 .loc 1 64 0 113 0004 00BF nop 65:../User/bsp_led.c **** } 114 .loc 1 65 0 115 0006 BD46 mov sp, r7 116 @ sp needed 117 0008 5DF8047B ldr r7, [sp], #4 118 000c 7047 bx lr
main.o.lst
35:../User/main.cpp **** config(); 42 .loc 1 35 0 43 0008 FFF7FEFF bl _Z6configv
在连接程序的最后阶段,出现错误提示:../User/main.cpp:35: undefined reference to `config()'
原因分析:
因为config.c程序在编译的时候被翻译成了.text.config代码段,而此段中存在一个函数标号config。也就是说config.c文件的config()函数在编译的时候,其对应的标号是config。
而main.cpp文件调用该函数的地方,编译后却使用了标号_Z6configv。那么显然在连接的时候,连接器无法将这两个标号连接到一起,而出现上述错误。
3、使用extern “C”关键字
修改config.h,内容为
#ifndef __CONFIG_H #define __CONFIG_H extern "C" void config(void); #endif
重新编译这些文件,结果main.cpp的编译结果出现了变化,而最终程序连接也通过了
35:../User/main.cpp **** config(); 42 .loc 1 35 0 43 0008 FFF7FEFF bl config
原因分析:
由于extern "C"修饰了void config(void),所以在main.cpp调用该函数的时候按照C文件的编译方式进行编译,所以其对应的标号为config。config函数所在的文件config.c文件编译出的标号与之一致,就能成功的连接。
参考博客:C++项目中的extern "C" {}