C和C++的区别
C和C++的区别
一、输入与输出
C:
#include<stdio.h>
int main()
{
int a=0;
char b='\0';
scanf_s("%d%c",&a,&b);
printf("a=%d,b=%c",a,b);
return 0;
}
C++:
#include<iostream>
int main()
{
int a=0;
char b='\0';
cin>>a>>b;
cout<<"a="<<a<<"\t"<<"b="<<b<<endl;
return 0;
}
二、引用(别名)
1.引用
类型&引用变量名称=变量名称。
int a=10;
int &b=a; //相当于给a起了别名,对b进行操作就是对a操作。
b是a的别名,b与a代表的是同一个变量,占内存中同一个存储单元,具有同一地址。
- 声明一个引用时是必须给予初始化;
- 没有空引用;
- 没有所谓的引用的引用。
2.常引用
能力只能进行收缩,而不能扩张。
int a=10;
const int &b=a;//ok
const int a=10;
int &b=a;//error
const int &b=a;//ok
3.引用作为函数参数
Swap(int &a,int &b)
{
int tmp=a;
a=b;
b=tmp;
}
int main()
{
int x=10;
int y=20;
Swap(x,y);
return 0;
}
三、inline内联函数
1. 内联函数
在C++中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了inline修饰符,表示为内联函数。
inline int Add(int a,int b)
{
return a+b;
}
内联函数的处理方式是在函数的调用点直接代码展开。
在计算机系统下,假如频繁的调用就会造成较大的时间开销。内联函数的引入减少了函数调用过程中开栈和清栈的开销。
2. 内联函数和普通函数的区别
- 内联函数在调用点代码直接展开,没有开栈和清栈的开销。
普通函数有开栈和清栈的开销。 - 内联函数体要求代码简单,不能包含复杂的结构控制语句。
- 如果内联函数函数体过于复杂,编译器将自动把内联函数当成普通函数来执行。
3. 内联函数和statc修饰的函数的区别
- static修饰的函数处理机制只是将函数的符号变成局部符号,函数的处理机制和普通函数相同,都有函数的开栈和清栈开销。
内联函数没有函数的开栈和清栈函数。 - inline 函数是因为代码在调用点直接展开导致函数本文件可见。
而static修饰的函数是因为函数符号从全局符号变成局部符号导致函数本文件可见。
4. 内联函数和宏的区别
- inline 函数的处理时机是在编译阶段处理的,有安全检查和类型检查。
而宏的处理是在预编译阶段处理的。没有任何检查机制,只是简单的文本替换。 - inline函数是一种更安全的宏。
5.内联函数的限制
从汇编层次来看。Inline函数调用明显是以实参压栈的方式来处理的。有开栈和清栈的开销产生。
- inline 函数一般写在头文件中。
- inline 函数只在Release 版本生效。在Debug版本是不生效。
- inline函数只是给编译器的一个建议。具体是否处理成inline 函数是编译器决定的。
inline 函数直接调用点代码展开没有开栈和清栈的开销,提高了运行效率,为什么不把所有函数默认成inline函数处理呢?
其实inline函数也不是十全十美的。如果一个项目下存在N多个inline函数的调用点。每个调用点都要将代码直接开。会导致目标文件变得非常庞大。所有内联函数是以代码膨胀为代价的。典型的以空间换时间的概念来设计的。
什么情况下采用inline处理合适,什么情况下以普通函数形式处理合适呢?
如果函数的执行开销小于开栈清栈开销,使用inline处理。这种情况下函数体较小,处理效率高。
如果函数的执行开销大于开栈清栈开销,使用普通函数方式处理。这种情况下函数体较大,空间利用率高。特别是函数的执行开销远远大于开栈清栈开销。这种情况下函数开栈和清栈的开销就可以忽略不记。这种情况采用普通函数处里最合适。
四、函数参数的默认值
函数参数的默认值必须从右向左依次赋值,不能跳跃。
// void fun(int a,int b = 0 ,int c ,int d = 0 ,int e=10) // error
void fun(int a, int b = 0, int c = 0, int d = 0, int e = 10)
{
printf("a = %d b = %d c - %d d = %d e = %d \n",a, b,c, d,e);
}
int main()
{
fun(12);
fun(12,23);
fun(12,23,34);
fun(12,(23,34,45),56);
// fun(12,,34); error;
return 0;
}
逗号表达式的运算过程:
- 从左向右逐个计算表达式;
- 逗号表达式作为一个整体,它的值为最后一个表达式的值;
- 逗号运算符的优先级别在所有运算符中最低。
五、函数重载
函数的原型包括函数返回类型、函数名、形参列表(其中形参名可以省略),且不需要函数体。
函数重载的依据?
函数名相同,参数列表不同(参数类型不同,参数个数不同,参数顺序不同)。
返回类型不同的函数是否可以重载?
不可以重载。
void fun(void);
int fun(void);
int x=fun();
在C++程序中,忽略函数的返回值,这种情况下,编译器不知道调用的是哪个fun函数,产生二义性。
所以只能靠参数而不能靠返回值类型的不同来区分重载函数。编译器根据参数为每个重载函数产生不同的内部标识符。
为什么C++可以函数重载而C语言不可?
C语言,编译器在编译文件时,只会给函数进行简单的重命名。具体的方法是给函数名之前加上”_”,所以两个函数名相同的函数在编译之后的函数名也照样相同,这就会因为不知道到底调用哪一个函数而出错。列如:在源代码中有两个相同的函数名 func,经过编译器编译后,这两个函数名就会变成 _func,这两个的函数名仍然相同,所以如果调用func函数时,就不知道究竟调用的哪一个func函数。
在c++中,编译器编译文件时,不仅会加上“_”,而且会加上函数类型。列如:在源代码中进行了函数重载,分别是func(int i,int j)和func(int i,double j)。经过编译器编译后,func(int i,int j)的函数名就会变成_funcii,其中最后两个ii就代表了形参的类型;同理,func(int i,double j)的函数名就会变成_funcid。这也解释了为什么函数重载时要求参数的个数不相同、类型不相同、顺序不相同。
名字粉碎。
extern关键字的使用。
如果要将C++的一个函数按照C语言的风格来编译,在函数声明前面加上extern “C”
六、模板函数
函数模板定义:
template<模板参数表>
返回类型 函数名(形式参数表)
{
//函数体
}
<模板参数表>(template parameter list)尖括号中不能为空,参数可以有多个,用逗号分开。模板参数主要是模板类型参数,也可以是非类型。
模板类型参数(template type parameter)代表一种类型,由关键字class或 typename(建议用typename )后加一个标识符构成,在这里两个关键字的意义相同,它们表示后面的参数名代表一个潜在的内置或用户设计的类型。
函数模板根据一组实际类型或值构造出独立的函数的过程通常是隐式发生的,称为模板实参推演(template argumentdeduction)。
七、new和malloc的区别
在C++中堆内存的分配和释放是通过new和delete来操作的。
开辟内存: 类型*指针变量名= new 类型(初始化数值); //自由区 //.heap
释放内存: delete 指针变量名;
区别:
-
malloc与free是c++/c语言的标准库函数,
new/delete是C++的运算符。 -
他们都可用于申请动态内存和释放内存。new/delete比malloc/free更加智能,其实底层也是执行的malloc/free。
new/delete更加的智能?因为new和delete在对象创建的时候自动执行构造函数,对象消亡之前会自动执行析构函数。 -
既然new/delete的功能完全覆盖了malloc和free,为什么C++中不把malloc/free淘汰出局呢?因为c++程序经常要调用c函数,而c程序只能用malloc/free管理动态内存。
-
new返回指定类型的指针,并且可以自动计算出所需要的大小。如 :
int p; p = new int; //返回类型为int类型,大小为sizeof(int);
int *pa; pa = new int[50];//返回类型为int *,大小为sizeof(int) * 100;
malloc必须用户指定大小,并且默然返回类型为void*,必须强行转换为实际 类型的指针。
-
new内存分配失败时,会抛出bac_alloc异常。malloc分配内存失败时返回NULL。
free释放的是指针指向的内存,不是指针。指针是一个变量只有程序结束时才会被销毁。所以释放内存后一定要将指针指向NULL。
八、namespace命字空间作用域
为了解决命名冲突的问题,C++提供了名字空间作用域的机制。
namespace 名字空间作用域名称
{
{
namespace yhp
{
int a;
int Sum(int a, int b)
{
return a + b;
}
}
全局变量a和Sum 函数定义在yhp这个名字空间作用域下。作用域将其隔离起来。访问方式如下:
int main()
{
cout << yhp::a << endl;
int rt = yhp::Sum(10,20);
return 0;
}
通过名字空间作用名称加上作用于访问符 :: 的方式进行访问。
C++中还提供了另外两种的访问方式。
- using声明
- using指示符
using 指示符是将using 指示符后名字空间作用域下的所以符号暴露在using 指示符所在的作用域。因为using指示符会导致名字空间作用域被污染。所以我们在编码过程中并不提倡使用using 指示符。