当前位置:   article > 正文

c与c++的区别_c和c++区别

c和c++区别

一、函数的区别

函数默认值参数

在函数声明或者定义的时候,给定参数默认值,如果实参传递时候,不给该形参传值,则会按照默认值传参

函数参数的默认值是在编译期生成指令的时候,直接生成入参指令。

函数的默认值参数需要从右往左依次赋值,不能跳过。

函数的值参数在同一作用域只能赋值一次,不能重复赋值。

因为函数参数的默认值是在编译期带入的,所以函数的参数的默认值只在本文件生效。

内联函数

在调用内联函数时,该函数会直接在调用点展开(在编译期展开指令)。

作用域只在本文件。

正常函数调用:

1.传参 2.  将下一行指令地址入栈 3.将ebp 寄存器入栈,esp  ebp 偏移 ——开辟栈帧 4.执行函数体 5.返回值 6.栈帧回退 7.恢复现场 8.参数清除

debug版本下,内联函数和正常函数调用方式一致。

realse版本下,在调用内联函数时,该函数会在调用点展开。

递归函数一定不能作为内联函数。(由于内联在编译期展开,编译期无法获取变量的值,而递归函数的终止条件一定需要有变量参与,所以,递归函数不可能被处理成内联函数)

inline 只是对系统的建议(做不做是系统的事,建议将其函数处理为内联函数。

符号:

所有的数据都会生成符号

指令中只有函数名会生成符号

符号:

1.全局符号 global 符号(所有的文件只要引用声明就可以使用)

2.局部符号  local 符号(只有本文件可以用)

inline 产生的符号是 local 符号,所以只能在本文件下使用。

函数重载

函数名相同,参数列表不同

c 语言函数编译生成函数符号 ,依赖函数名

c++中函数编译生成的函数符号 , 依赖函数名+参数列表

  1. bool compare(int a,int b)//c语言中生成的符号大致为 _compare
  2. {
  3. return a>b;
  4. }
  5. bool compare(char a,char b)//c++中生成的符号大致为_compare_char_char
  6. {
  7. return a>b;
  8. }

类型的自动转换

 普通函数

有类型安全校验(参数列表需要类型的都需要)

可以调试

会生成global 符号

静态函数

有类型安全校验

可以调试

会生成 local 符号

宏函数(替换)

没有类型安全校验

不可以调试

不生成符号

内联函数

有类型安全校验

可以调试(在debug 内联函数和静态函数表现一致(不会展开),在 release 版本下和宏函数表现一致)

在debug版本会生成local 符号,在release 版本不生成符号

二、c++与c语言之间的相互调用

1>如何用c++去调用c语言的代码

  1. extern "C"
  2. {
  3. void fun_c(int a,int b);//将括号中的按照c语言标准编译
  4. }

2>如何用c语言去调用c++的代码

1.可以直接在c++所需代码处加上extern "C",让其按照c语言标准编译就可链接上

  1. #include<iostream>//c++
  2. using namespace std;
  3. extern "C"
  4. {
  5. void fun(int a, int b)
  6. {
  7. cout << "fun()" << endl;
  8. }
  9. }

但是若没有c++源代码,这种方法就不可行

2.若没有c++源代码,没有定义,只有函数声明时,可借助一个新的.cpp 文件

自己写一个.cpp 文件

  1. void fun(int a, int b);//想要调用的c++函数的声明
  2. extern "C"//括号中的代码产生c语言链接
  3. {
  4. void fun_tmp(int a, int b)//借助第三方函数,此函数的返回值,参数列表应与想调用的函数相同
  5. {
  6. return fun(a, b);//return 想调用的函数
  7. }
  8. }

在c中调用自己所写的函数

  1. #include<stdio.h>
  2. void fun_tmp(int a, int b);//通过调用自己所写函数,来达到调用所需函数的目的
  3. int main()
  4. {
  5. fun_tmp(10, 20);
  6. return 0;
  7. }

三、指针、地址、数组名三者之间的区别

c++中三者一样

c语言中,数组名就是地址。

指针存放地址。(联系)

指针是个变量,地址是常量。(区别)

指针有类型。

  1. #include<iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. int a = 10;
  6. int b = 20;
  7. b = a;
  8. b = 10;
  9. }

 四、命名空间

在C++中支持三种域:局部域、名字空间域和类域。
        名字空间域是随标准C++而引入的。它相当于一个更加灵活的文件域(全局域),可以用花括号把文件的一部分括起来,并以关键字namespace开头给它起一个名字:

namespace将类型名,重命名放在一个空间之中,对外封闭。可以防止命名冲突

 直接使用会报错。

若想使用,可以using namespace,整个命名空间里的东西都可以使用

 若想只使用其中的一个,可以using Type::INT; 只使用命名空间Type中的INT,其他就不可以使用

        花括号括起来的部分称声明块声明块中可以包括:类、变量(带有初始化)、函数(带有定义)等。最外层的名字空间域称为全局名字空间域(global namespace scope),即文件域。
        名字空间域的引入,主要是为了解决全局名字空间污染(global namespace pollution)问题,即防止程序中的全局实体名与其他程序中的全局实体名,命名冲突。

命名空间创建

  1. //普通的命名空间
  2. namespace zyt
  3. {
  4. int a_max = 10;
  5. int g_min = 0;
  6. int my_add(int a, int b) { return a + b; };
  7. }
  8. //名字空间域可分层嵌套,同样有分层屏蔽作用
  9. namespace primer
  10. {
  11. double pi = 3.1415926;
  12. double my_add(double a, double b) { return a + b; }
  13. namespace Matrix
  14. {
  15. char my_max(char a, char b) { return a > b ? a : b; }
  16. }
  17. }
  18. //同一个工程中允许存在多个相同名称的命名空间
  19. //便一去最后会合成一个命名空间
  20. namespace zyt
  21. {
  22. float pi = 3.14;
  23. int my_sub(int a, int b)
  24. {
  25. g_min = a - b;
  26. return g_min;
  27. }
  28. }

一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中。

名字空间使用

1.加名字空间及作用域限定符

  1. int main()
  2. {
  3. int a = zyt::my_add(12, 23);//::作用域限定符
  4. printf("%lf\n", primer::pi);
  5. printf("%lf\n", zyt::pi);
  6. primer::Matrix::my_max('a','b');
  7. return 0;
  8. }

2.使用using将名字空间中成员引入

使用using声明可只写一次限定修饰名。using声明以关键字using开头,后面是被限定修饰的名字空间成员名:
 

  1. using zyt::pi;
  2. using primer::Matrix::my_max;
  3. //名字空间类成员matrix的using声明
  4. // 以后在程序中使用matrix时,就可以直接使用该成员名,而不必使用限定修饰名
  5. int main()
  6. {
  7. printf("%lf\n", primer::pi);
  8. printf("%lf\n", pi);
  9. my_max('a', 'b');

3.使用 using namespace 名字空间名称引入

        使用using指示符可以一次性地使名字空间中所有成员都可以直接被使用,比using声明方便。using指示符;以关键字using开头,后面是关键字namespace,然后是名字空间名。
using namespace名字空间名;

​​​​​​​ 

  1. using namespace zyt;
  2. int main()
  3. {
  4. printf("%lf\n", primer::pi);
  5. printf("%lf\n", pi);//zyt
  6. my_add(12,23);
  7. return 0;
  8. }
  1. //A.h
  2. namespace zyt
  3. {
  4. int my_add(int, int);
  5. int my_sub(int, int);
  6. }
  7. //A.cpp
  8. #include"A.h"
  9. namespace zyt
  10. {
  11. int my_add(int a, int b)
  12. {
  13. return a + b;
  14. }
  15. int my_sub(int a, int b)
  16. {
  17. return a - b;
  18. }
  19. }

使用using指示符
        标准C++库中的所有组件都是在一个被称为std的名字空间中声明和定义的。在采用标准C++的平台上使用标准C++库中的组件,只要写一个using指示符:
using namespace std;
就可以直接使用标准C++库中的所有成员。这是很方便的。

五、动态开辟空间

c语言中使用mallocfree   

一维数组  二维数组

  1. //给一个变量申请一个空间
  2. int *p=(int *)malloc(sizeof(int));
  3. free (p);
  4. //一维数组的申请
  5. int* p1 = (int*)malloc(10 * sizeof(int));
  6. //一维数组的释放
  7. free(p1);
  8. //二维数组的申请arr[5][10]
  9. int** p2 = (int **)malloc(5 * sizeof(int*));
  10. for (int i = 0; i < 5; i++)
  11. {
  12. p2[i] =(int *) malloc(sizeof(int) * 10);
  13. }
  14. //二维数组的释放,先释放里层,再释放外层
  15. for (int i = 0; i < 5; i++)
  16. {
  17. free(p2[i]);
  18. }
  19. free(p2);

c++ 中使用newdelete

  1. //给变量申请一个空间
  2. int* q = new int(10);//申请一个空间,并给q赋值为10
  3. int* q = new int();//申请一个空间,并给q赋值为默认值,0
  4. int* q = new int;//申请一个空间,并给q赋值为随机值
  5. //释放
  6. delete q;
  7. //动态开辟一个10个元素大小的一维数组空间
  8. int* q1 = new int[10];
  9. //释放
  10. delete[]q1;//释放数组要加上[],代表变量是一个数组
  11. //二维数组的申请
  12. int** q2 = new int* [5];
  13. for (int i = 0; i < 5; i++)
  14. {
  15. q2[i] = new int[10];
  16. }
  17. //释放
  18. for (int i = 0; i < 5; i++)
  19. {
  20. delete[]q2[i];
  21. }
  22. delete[]q2;

 六、const

c语言中(.c),const 修饰的变量为常变量

c++中,const 定义的为常量,常量会在编译器将常量所在地方全部替换为该常量的值

const定义的为常量,是用const修饰,初始值为常量才可被称作常量

  1. #include<iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. const int a = 10;
  6. int* p = (int *)&a;//强转
  7. *p = 20;
  8. cout << "a= " << a << endl;//10
  9. cout << "*p= " << *p << endl;//20
  10. }

 若被const修饰,初始值为变量,则为常变量

也就是说,如果使用变量初始化常量,常量会退化为常变量

  1. int c = 10;
  2. const int a = c;//如果使用变量初始化常量,常量会退化为常变量
  3. int* p = (int *)&a;//强转
  4. *p = 20;
  5. cout << "a= " << a << endl;//20
  6. cout << "*p= " << *p << endl;//20

const修饰的量必须初始化,不初始化,后期就无法对其赋值。

七、引用

一个与指针密切相关的特殊数据类型。

底层是一个指针。所有使用到该引用的地方,在编译期会自动替换为底层指针的解引用。

引用是一个变量的别名,定义引用类型变量,实质上是给一个已定义的变量起一个别名。
引用类型变量与其相关的变量使用同一个内存空间。
定义引用类型变量的一般格式为:
类型符 & 引用名=变量名(& 为引用声明符)
如: int a=3;
int &b= a;
注意区分:引用声明运算符和取地址运算符
int *b= &a;
为什么引用必须初始化:所有使用到该引用的地方,在编译期会自动替换为底层指针的解引用。不初始化,后面就没机会了。

为什么一经引用就无法改变引用的目标:所有使用到该引用的地方,在编译期会自动替换为底层指针的解引用。

int &f ,引用底层是一个指针,但e是一个常量,引用一个常量,将常量的地址泄露给非常量的指针,定义一个常引用。

 

 

10会产生一个临时量,临时量都具有常属性

 

 临时量的生命周期只在当前指令。

如果临时量被引用,他的生命周期就会扩大到和引用一致。

  1. #include<iostream>
  2. using namespace std;
  3. void swap1(int* m, int* n)//指针
  4. {
  5. int tmp;
  6. tmp = *m;
  7. *m = *n;
  8. *n = tmp;
  9. }
  10. void swap2(int &m, int &n)//引用
  11. {
  12. int tmp;
  13. tmp = m;
  14. m = n;
  15. n = tmp;
  16. }
  17. int main()
  18. {
  19. int a = 10;
  20. int b = 20;
  21. swap1(&a, &b);//指针
  22. cout << a << b << endl;//20 10
  23. swap2(a, b);//引用
  24. cout << a << b << endl;//10 20
  25. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/191391
推荐阅读
相关标签
  

闽ICP备14008679号