C和C++的区别(上)

时间:2022-09-09 18:22:57

cc++的区别先总结4点

1.  函数的默认值

C++可以给函数定义或声明时给参数赋初值

(c语言后来制定的c99标准也能给默认值,但由于现在普遍学习的是c89标准,因此没有此功能)

使用默认值的好处是什么?

使用默认值的原理又是什么?

使用默认值有何规则限定?请接着看。

比如main函数先定义俩变量

int a;

int b;

然后调用sum(a,b)的时候

压入参数的时候的时候执行的汇编就是

Move  eaxdword ptr[ebp-8]

Push eax

 

Move  ecx,dword ptr[ebp-4]

Push ecx

可以看出压入参数的顺序先ba的。是从右往左的

如果在定义或者声明的时候给函数的参数了默认值,再或者调用时直接参数给了立即数

比如 Int sum (int a,int b=10);这样

到时候执行main的调用sum这行的汇编就会变成

 

Push  0Ah

Move  ecx,dword ptr[ebp-4]

Push ecx

可以看到少了一行汇编指令,直接把默认值作为立即数压入。

无疑是提高了效率

 

 

那么使用默认值的规则是什么。

刚才从原理上看到了,参数压栈的顺序在底层的汇编体现是从右往左压的。

因此默认值的的给定也必须从右往左给,可以少给,但不可以跳着给。

比如sum2(int a,int b,int c);这个函数可以

sum2(int a,int b,int c=3);

sum2(int a,int b=2,int c=3);

sum2(int a=1,int b=2,int c=3);

其中任意一种给发。

 

但绝不能这样给

sum2(int a=1,int b,int c);

sum2(int a=1,int b,int c=3);

sum2(int a,int b=2,int c);

为什么不能以上这些给发

比如sum2(int a,int b=2,int c);这样给。是不是以为着调用的时候

必须这样sum2(1,,3);这显然违反语法的。

刚只是用声明举例。

然而实际上是可以声明和定义都可以给,但不能给重复,一个参数的默认值只能给一次

 例如

Int sum(int a,int b=20);

Int sum(int a=10,int b);

两次声明是可以的。

单独来看并不符合Int sum(int a=10,int b);刚说的从右至左的给默认值规则。

但由于它的上一行存在Int sum(int a,int b=20);已对b给过默认值了。因此再只给a默认值并不违法。


如果

Int sum(int a,int b=20);

Int sum(int a,int b=20);

这样是不可以的

 

 

最后值得注意的是,刚才说的都是声明与定义在main函数上方,因为编译时的顺序是一行一行从上往下读的,如果这样给定默认值,显然也是不对的。

例如

Int  sum2(int a,int b,int c=3);

 

Main()

{

....

Sum2(1)

...

}

 

Sum2(int a,int b=2,int c);

 

 

 

 

2.  内敛函数inline

   内联函数是在编译时 在调用点把内联函数的代码展开在调用点处,并不会生成函数的符号。

   例如

   Inline Int sum (int a,int b)

{return a+b;}

Main()

{

Int x=10;

Int y=20;

Sum(x,y);

 

}

 

编译时就是

Main()

{

Int x=10;

Int y=20;

x+y;

}

并不会给sum函数开辟个新栈,而是直接的代码替换

  

   这么看来内联函数和宏非常的相似。那么既然内敛叫内敛,宏叫宏,那么肯定有区别所在。

区别就是 宏是单纯的字符替换,在预编译的阶段,不会做任何的词法解析,类型检查,也就是说宏出错的可能性非常高,不安全。而inline内敛函数是在编译时期,会进行词法解析,类型检查,词法、类型有误就会编译失败。

所以说,inline相当于安全版的宏

 

那么既然内敛函数名字中有个函数,那么它和函数有是否有本质区别呢?

答案是肯定的。

首先一个是在调用方的代码替换,一个是另开辟新栈的相对独立执行。

其次一般函数会生成符号,而内敛函数本质来讲只是个安全宏,所以不会生成符号。

 

那么既然内敛函数这么安全,也不用开辟新栈效率高,为什么不定义所有的函数为内敛函数呢?

首先内敛函数只对本文件可见,且编译时不生成符号,这对于跨文件的工程来说,显然是不好的。

其次,如果多次使用内联函数,将进行大量的代码替换,造成.txt段过于冗余。

最后我们说的效率高,是在函数的执行开销小于栈帧的开辟开销的情况下成立的。

也就是说假如代码只有简单的短短几行,用内敛的确能提高效率。

但如果函数代码过于复杂,内敛的效率还是没有一般函数高的。

 

最后值得注意的是内敛函数只算一个给编译器的建议,也就是说编译器可能不会使用内敛,因为某些函数使用代码替换会导致错误,比如递归函数,递归的核心就是开辟栈帧递归数据然后层层计算最后收尾,简单的代码替换怎么能确定递归的尾巴?

并且inline只在release版本生效,debug版本是不启作用的。



3.函数的重载

      c语言中函数的定义不能重名,如果重名就会出现重定义,因为函数生成的符号只由函数名决定。

但是c++中的函数定义可以重名,但是重名的前提是函数的参数不能完全相同,因为c++的函数符号是由函数名和参数类型决定的。

     我们都知道在不同作用域可以定义相同名字的变量。比如main中的局部变量和本文件的全局变量就可以同名。并且main中优先使用的是离自己作用域最近局部变量。

int sum(int a,int b);

 int main()

 { 

int sum(char *a,char *p);

   sum(1,2);

 return 0;

}

 

例如这样就会出错,sum(1,2)只会看到int sum(char *a,char *p);

而无法看到int sum(int a,int b);

 所以说函数重载必须在同一个作用域内

 还有一个点就是实参和形参的转化问题。

我们先回一下c语言的类型转换

C和C++的区别(上)

箭头所指的线路都是编译器默认会选择的优先转向。

比如float类型变量参与运算时,都会默认转换成double型,然后参与运算,目的是为了提高精度。


举例

  比如short  a=-1 usigned int  b=1比较 。

 看似b比较大吧,但其实结果我们知道的,ab比较时结果却是a比较大,这是为什么?我们看a>b 这个式子,ashortbu_int ,按道理a要先转化成int型,这时是仍为-1,然后发现busigned,继续转化为usigned,然而usigned中不可能有-1,我们知道-1应该是很大的数,即2^32-1 。所以a>b这个式子其实最后值是1,即ab大。

 

可是如果不是箭头所指呢,即我定义的函数形参是float,然后重载一个形参为int的。

最后我传入实参为double型,编译器就不知道double是该优先转成float还是int,因此就会有问题。


4.  cc++函数互相调用

之前也说了,cc++的函数生成符号不一样,那么c++c在互相调用对方函数的时候就会出错,因为根本无法找到对应的符号。

而由于c++是基于c语言而来,肯定会想到兼容性的问题,因此如果c++想要使用c的函数,c++会有特殊处理,可以把函数符号转化成c的不带参数的形式,只用在c++文件里这样

  Extern C

{

 Int sum(int ,int )

}

这样编译时同时也会生成c语言版本的函数符号,到时候c++想要使用c中定义的函数也可以找到符号。

 

但是反过来呢,如果c想要使用c++文件里定义的函数呢。c可是不会兼容c++的,c可不能在自己文件里使用externc++这种东西。

那么该怎么做呢?

其实可以创建一个接口文件。

比如这是cpp文件

Sum.cpp

 

Int sum(int a,int b)

{

Return a+b;

}

 

这是.c文件

Main()

{

Int a=10;

Int b=20;

sum(a,b);

}

目前来看,.c文件是绝对使用不了.cpp里的sum定义的。

那么这时就要新建一个mid.cpp接口文件了

这个文件内容如下

Mid.cpp

Int sum(int a,int b)

externC

{

  Mysum(int a,int b)

{

 return sum(a,b);

}

}

首先mid文件中先声明了sum.cppsum函数,由于都是cpp文件,因此sum对于mid是可见的,那么mid就可以看到sum的定义体,那么接下来mid文件只要把这个sum函数封装一下,并且用externC生成C符号的形式封装出一个函数,这个函数的功能和sum完全一样,参数一样,返回值也一样,这不就能让c使用c++的函数了吗