前言

C语言和c++对于函数在编译后的处理不同,所以也要注意源文件不要混用


正文

项目里新建俩文件

1
2
3
4
5
6
7
8
9
10
11
12
13
//main.cpp
#include<iostream>
using std::cout;
using std::endl;

int Ave(int a, int b){
return (a + b) / 2;
}

int main(){

return 0;
}
1
2
3
4
//hh.cpp
int Ave(int a, int b){
return (a + b) / 2;
}

编译的时候就发现报错了:

然后新建两个C语言源文件:

1
2
3
4
5
6
7
8
9
#include<stdio.h>

int Ave(int a, int b){
return (a + b) / 2;
}

int main(){
return 0;
}
1
2
3
int Ave(int a, int b){
return (a + b) / 2;
}

编译前先注释掉main.cpp的main函数,因为一个程序只能有一个main函数。
然后就是注释掉hh.cpp的ave函数。

能够看到,函数编译后的结果在.obj文件中时,c++和C语言的函数名都不一样。

显然c++的函数名编译后还是很详细,有类型 函数名 参数 主体之类的,而C语言就加了个下划线
还记得函数重载的话,就会想到这个问题,因为C语言没有函数重载,所以他的函数除了冲突就没用别的要判断,而c++反而因为有了重载,比较起来会更加繁琐。

而你.c和.cpp混用的时候,两个函数在.obj里面的表现不一样,所以编译通过了。

当头文件有声明,.c文件有定义,.cpp调用,同样的也会报错,函数是以C语言的模式编译,cpp就整不明白这个函数了。

所以也要用到extern做声明,extern声明变量应该是在上一章写过了,声明函数的格式:
extern "C" int Ave(int a,int b);
或者
extern "C"{} 这样主要是有多个函数需要声明的时候可以用花括号套起来。

但是声明归声明,别忘了C语言没有重载,不要做二愣子操作

1
2
3
4
5
6
7
8
//emc.h
#pragma once
extern "C" int Ave(int a, int b);

//emc.c
int Ave(int a, int b){
return (a + b) / 2;
}
1
2
3
4
5
6
7
8
9
10
//main.cpp
#include<iostream>
#include"cma.h"
using std::cout;
using std::endl;

int main(){
cout << Ave(2,3);
return 0;
}

这样就不需要瞎搞成cpp的函数方式了,直接用c的方法去读就行了。

extren除了套函数声明,还可以直接套头文件:

1
2
3
4
5
6
7
8
9
10
11
12
//main.cpp
#include<iostream>
extern "C"{
#include"cma.h"
}
using std::cout;
using std::endl;

int main(){
cout << Ave(2,3);
return 0;
}

这样操作后,emc.h里面就不需要用extern指定了。


当头文件被.c和.cpp同时调用,函数会被编译成啥鸟样

1
2
3
4
5
6
7
8
9
10
//main.cpp
#include<iostream>
#include"cma.h"
using std::cout;
using std::endl;

int main(){
Ave(2,3);
return 0;
}
1
2
3
4
5
6
7
8
9
10
//emc.h
#pragma once
extern "C" int Ave(int a, int b);

//emc.c
#include"cma.h"

int Ave(int a, int b){
return (a + b) / 2;
}

看到是由extern "C"发出的错误。
大概就明白extern "C"这种写法属于c++的,C语言没有这种操作,所以才会报错。

如果真的需要让两个不同的源文件调用,可以用#ifdef 去判断
在c++中有个隐藏的宏定义,__cplusplus,这样也省得我们去设置宏。

1
2
3
4
5
//emc.h
#pragma once
#ifdef __cplusplus
extern "C" int Ave(int a, int b);
#endif

但更合理的做法感觉是只选extern "C"那块

1
2
3
4
5
#pragma once
#ifdef __cplusplus
extern "C"
#endif
int Ave(int a, int b);

因为函数声明都在头文件的前提,只有当源文件类型不同时,才会显示特别的声明。
如果都放在#ifdef里面,那么C语言文件如果有对函数的定义就会变成既声明又定义了,因为这块内容编译后展开对于C语言源文件不存在。

声明较多的时候感觉这样写也问题不大:

1
2
3
4
5
6
7
8
9
10
#pragma once
#ifdef __cplusplus
extern "C" {
#endif

int Ave(int a, int b);

#ifdef __cplusplus
}
#endif

记住C语言没有重载,用extern去用c风格的时候不要带入重载的。


LNK4042

1
2
3
4
5
6
7
8
9
//hh.h
#pragma once
int ave(int a,int b);
extern "C" int add(int a,int b);

//hh.c
int add(int a,int b){
return a + b;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
//hh.cpp
int ave(int a,int b){
return (a + b) / 2;
}

//main.cpp
#include<iostream>

int main(){
std::cout << ave(2,5) << std::endl;
std::cout << add(2,5) << std::endl;
return 0;
}

定义分别在两个源文件中。调用在main.cpp。

这里源文件能重名是因为类型不一样,这种写法不理智,这就举个例子。

所以当编译的时候会将源文件编译成.obj 最后给链接成.exe
当出现重名的源文件时,两个重名的.obj链接就多余了。也就导致了报错。

这个问题其实正常人应该没这无聊去混用这两个。


结语

还是辣句话c++可以这样兼容c语言,但是反过来不行