C++与C的一些区别及C/C++混合编程
1 C++与C中的函数差异
C++中的函数和C语言中的一些差异
函数:声明 + 定义!!!
返回值类型 函数名(形式参数类型列表)
{
函数体;(函数的实现)
}
1.1 默认参数(default argument)
在C++中,编写函数的方式和C语言差别不大,但是可以给函数的形式参数设置默认值。
int sum(int a,int b)
{
return a+b;
}
可以给sum函数的形式参数(a,b)设置默认值
======>
int sum(int a=10,int b=10)
{
return a+b;
}
当我们调用带有默认参数的函数时,如果提供实际参数,则使用提供的值。如果没有提供实际参数,就使用默认值。
用法:直接在函数定义的时候,把形式参数的默认值写在形式参数后面。
int sum(int a=10,int b=10);
cout << sum() << endl; //以a=10,b=10匹配上面的函数
cout << sum(1) << endl; //以a=1,b=10匹配上面的函数
注意:
1.靠右原则
在定义时,如果某一个参数设置了默认值,那么这个参数的右边的所有参数都应该设置默认值(拥有默认参数的形参,都放在右边)
int sum(int a=10,int b); //ERROR
在使用时,如果某一个参数使用了默认值,那么该参数右边所有的参数都应该使用默认值。
2.当函数的声明和定义分开的时候,默认值需要设置在函数声明的地方,函数定义的地方不需要再次设置。
1.2 函数重载(overload)
重载就是重新装载的意思,即同一个函数名能够完成不同的功能(同一个函数名可能存在多个定义)。
- 函数重载允许使用同一个函数名定义多个不同的函数。
- 编译的时候会根据实际提供给函数的参数(数量,类型)去匹配不同的函数。
- 程序员只需要记住一个函数名,就可以完成一系列相关的任务。
1.2.1 在C++中,函数重载的条件
- 在相同的作用域
- 函数名相同
- 函数的参数列表不同(参数的数量和类型)(const修饰的自定义参数类型也算不同的类型)
同时满足上面三个条件的函数,是重载关系。注意(函数重载和默认参数不冲突):
int sum(int a=0,int b=0,int c=0);
int sum(int a,int b);
======>
sum(1,2); //产生二义性
sum(1,2,3);
sum();
编译器在编译的时候,不知道是以(1,2,0)为参数调用第一个函数。还是以(1,2)调用第二个函数。同样,将编译器设计为,这种情况优先调用某一种也是不合理的。
1.2.2 编译器换名机制
具体调用同名函数的哪一个版本,在编译的时候就确定了,难道编译器真的允许函数重名吗?肯定不允许,如果真的有同名函数,调用同名函数的时候,编译器就不知道应该调用哪一个了!编译器是根据函数调用语句中实际参数个数和类型来判断应该调用哪一个函数。
原理:编译器换名机制
-
在C++中,重载函数经过编译器编译之后,编译器会整合参数的类型,形成一个新的函数名(符号)。
-
在调用函数的时候,编译器会推导实际参数的类型,也形成一个新的函数名,最终自动匹配相同的函数名.
g++ -c 1.cpp -o 1.o
把1.cpp编译成二进制文件(不能执行)
nm
命令可以查看二进制文件的符号表
nm 1.o
看到的函数名字都是被换名之后的名字
//nm 1.o
000000000000002b T _Z3sumdd
0000000000000045 T _Z3sumdi
0000000000000000 T _Z3sumii
0000000000000014 T _Z3sumiii
我们可以写代码去调用这些函数
g++编译器在编译的时候,会把函数的参数类型和个数整合形成一个新的函数名(定义函数和调用函数的时候都会换名)。
gcc编译器在编译的时候,不会对函数名进行任何处理。
只能使用gcc去验证(使用c代码去调用换名之后的函数)。
2 C/C++混合编程
C++在C的基础上发展而来的,是全面的兼容C语言的语法(也可以利用C语言的所有成果)。C++程序中,经常会调用C语言中的代码(C语言的各种库函数)。出现的问题主要在编译和链接的时候。
g++ 01main.cpp -o main -I./lib -lnb -L ./lib
/tmp/ccFUyjBz.o:在函数‘main’中:
01main.cpp:(.text+0x6a):对‘print_array(int*, int)’未定义的引用
collect2: error: ld returned 1 exit status
事实上,C++代码是无法直接调用C语言代码的,因为g++编译器在编译的时候生成的函数名和C语言编译器生成的函数名不一样(g++编译器找不到C语言库中的函数名)!
解决方法:在声明这个函数的时候,告诉编译器,此函数的调用方式按照C语言的方式调用(不对函数进行改名)。
声明这个函数以C的函数调用
extern "C" void print_array(int *p,int num);
如果有多个函数需要这样声明:
extern "C"
{
void print_array(int *p,int num);
//.....
}
你如果这样写,在C++中使用是没有问题的,但是如果在其他的C文件中使用这个头文件时,就会编译不通过因为:extern "C"
不是C语言的关键字,gcc编译器不认识,导致C语言程序又不能使用这个库了。能不能改进一下代码,gcc编译器的时候不使用这个关键字,g++编译器的时候就使用这个关键字
条件编译,利用g++编译器内置的宏 __cplusplus
#ifdef __cplusplus
extern "C"
{
#endif
void print_array(int *p,int num);
#ifdef __cplusplus
}
#endif
3 内联函数
内联函数是为了提高程序的运行效率而实现的,使用关键字inline
修饰的函数,称为内联函数。
inline 返回值类型 函数名(参数列表)
{
函数体;
}
内联函数的特点:
- 将函数定义为内联函数,编译器在编译的时候,就会将函数的函数体复制到函数的调用点。
- 在函数调用的时候不需要跳转到函数定义的地方,也不需要在函数运行完毕的时候跳转回来。
- 可以节省函数的调用开销,提高程序的运行效率。
- 内联函数的思想是使用空间换时间。
注意:
inline
只是对编译器的一种提示,编译器不一定会按照程序员的要求来,编译器有自己的判断规则
所以,内联函数不能太长,不能包含复杂的逻辑运算,如:递归,循环,switch…
在C++中,一般使用内联函数替代宏函数。
#define MIN(a,b) ((a)<(b)?(a):(b)) //有BUG
int a = 7,b = 8;
MIN(a++,b); //会对a++展开多次
====>
inline int MIN(int a,int b)
{
return a<b?a:b;
}
4 C++中的组合类型(结构体/共用体/枚举)
在C++中,结构体/共用体/枚举类型,成为了真正意义上的独立类型。最直接的体现:
当你定义好类型之后,就可以直接使用类型名来定义变量。
4.1 结构体
C++中的结构体的基本用法和C语言类似,但是在C语言的基础上增加了很多功能。而且弱化了"结构体"的概念,突出了"类型"的概念 ,结构体表示的就是一个具体的类型。C中的struct里面不能有函数(行为),C++中的struct里面可以有函数(行为)。C++是面向对象的编程思想,一切皆对象!!结构体就是用来描述复杂的对象的。对象应该具有:静态的属性(变量)和动态的行为(函数)。
如:
struct Dog //Dog就是一个类型名
{
int color;
int age;
};
//C语言:
struct Dog wc;
//C++:
Dog wc;
4.2 共用体
C和C++中的用法,没有区别(多个成员共用一块内存区域)
union Test
{
long a;
int b[5];
double c;
};
24
4.3 枚举类型
枚举就是把一个变量可能出现的值,一个一个的列举出来,变量的值,只有可能是枚举值中的一种。如:
enum WEEK{MON,TUE,WED,THU,FRI,SAT,SUN};
这句话有两个作用:
1.声明WEEK是一个新的数据类型。
2.声明MON,TUE,WED,THU,FRI,SAT,SUN为符号常量,通常称之为枚举值,值默认为0~6。
在传统的枚举中,枚举量的值是暴露在最外层的作用域的,这样如果在同一个作用域下面有两个不同的枚举类型但是包含相同的枚举值时,就会有问题。
enum WEEK{MON,TUE,WED,THU,FRI,SAT,SUN};
enum WORKDAY{MON,TUE,WED,THU,FRI};
在新标准中使用范围枚举(给枚举值加了一个作用域范围),同时解决了传统枚举总是被隐式转换为int的问题(内部默认实现就是int)。
范围枚举:
enum class 枚举类型名{枚举常量};
如:
enum class WEEK{MON,TUE,WED,THU,FRI,SAT,SUN};
枚举常量的使用方式:
枚举类型名::枚举常量
如:
WEEK::MON
编译: g++ 05enum.cpp -std=c++11
传统的枚举总会被实现为整型(int),在C++11中可以指定底层的实现类型。
enum class 枚举类型名:存储类型名{枚举常量};
====>如:
enum class WEEK:char{MON,TUE,WED,THU,FRI,SAT,SUN};