c与c++的区别

21 篇文章 0 订阅

一、函数的区别

函数默认值参数

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

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

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

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

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

内联函数

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

作用域只在本文件。

正常函数调用:

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

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

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

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

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

符号:

所有的数据都会生成符号

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

符号:

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

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

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

函数重载

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

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

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

bool compare(int a,int b)//c语言中生成的符号大致为 _compare
{
    return a>b;
}
bool compare(char a,char b)//c++中生成的符号大致为_compare_char_char
{
    return a>b;
}

类型的自动转换

 普通函数

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

可以调试

会生成global 符号

静态函数

有类型安全校验

可以调试

会生成 local 符号

宏函数(替换)

没有类型安全校验

不可以调试

不生成符号

内联函数

有类型安全校验

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

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

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

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

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

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

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

#include<iostream>//c++
using namespace std;
extern "C"
{
	void fun(int a, int b)
	{
		cout << "fun()" << endl;
	}
}

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

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

自己写一个.cpp 文件

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

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

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

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

c++中三者一样

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

指针存放地址。(联系)

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

指针有类型。


#include<iostream>
using namespace std;
int main()
{
	int a = 10;
	int b = 20;
	b = a;
	b = 10;
}

 四、命名空间

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

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

 直接使用会报错。

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

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

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

命名空间创建


//普通的命名空间
namespace zyt
{
	int a_max = 10;
	int g_min = 0;
	int my_add(int a, int b) { return a + b; };
}
//名字空间域可分层嵌套,同样有分层屏蔽作用
namespace primer
{
	double pi = 3.1415926;
	double my_add(double a, double b) { return a + b; }
	namespace Matrix
	{
		char my_max(char a, char b) { return a > b ? a : b; }
	}

}
//同一个工程中允许存在多个相同名称的命名空间
//便一去最后会合成一个命名空间
namespace zyt
{
	float pi = 3.14;
	int my_sub(int a, int b)
	{
		g_min = a - b;
		return g_min;
	}
}

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

名字空间使用

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

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

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

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

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

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

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

​​​​​​​ 

using namespace zyt;
int main()
{
	printf("%lf\n", primer::pi);
	printf("%lf\n", pi);//zyt
	my_add(12,23);
	return 0;
}
//A.h
namespace zyt
{
	int my_add(int, int);
	int my_sub(int, int);
}
//A.cpp
#include"A.h"
namespace zyt
{
	int my_add(int a, int b)
	{
		return a + b;
	}
	int my_sub(int a, int b)
	{
		return a - b;
	}
}

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

五、动态开辟空间

c语言中使用mallocfree   

一维数组  二维数组

//给一个变量申请一个空间
int *p=(int *)malloc(sizeof(int));
free (p);
//一维数组的申请
	int* p1 = (int*)malloc(10 * sizeof(int));
	//一维数组的释放
	free(p1);
	//二维数组的申请arr[5][10]
	int** p2 = (int **)malloc(5 * sizeof(int*));
	for (int i = 0; i < 5; i++)
	{
		p2[i] =(int *) malloc(sizeof(int) * 10);
	}
	//二维数组的释放,先释放里层,再释放外层
	for (int i = 0; i < 5; i++)
	{
		free(p2[i]);
	}
	free(p2);

c++ 中使用newdelete

	//给变量申请一个空间
	int* q = new int(10);//申请一个空间,并给q赋值为10
	int* q = new int();//申请一个空间,并给q赋值为默认值,0
	int* q = new int;//申请一个空间,并给q赋值为随机值
	//释放
	delete q;
	//动态开辟一个10个元素大小的一维数组空间
	int* q1 = new int[10];
	//释放
	delete[]q1;//释放数组要加上[],代表变量是一个数组
	//二维数组的申请
	int** q2 = new int* [5];
	for (int i = 0; i < 5; i++)
	{
		q2[i] = new int[10];
	}
	//释放
	for (int i = 0; i < 5; i++)
	{
		delete[]q2[i];
	}
	delete[]q2;

 六、const

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

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

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

#include<iostream>
using namespace std;
int main()
{
	const int a = 10;
	int* p = (int *)&a;//强转
	*p = 20;
	cout << "a= " << a << endl;//10
	cout << "*p= " << *p << endl;//20

}

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

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

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

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

七、引用

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

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

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

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

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

 

 

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

 

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

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

#include<iostream>
using namespace std;
void swap1(int* m, int* n)//指针
{
	int tmp;
	tmp = *m;
	*m = *n;
	*n = tmp;
}
void swap2(int &m, int &n)//引用
{
	int tmp;
	tmp = m;
	m = n;
	n = tmp;
}
int main()
{
	int a = 10;
	int b = 20;
	swap1(&a, &b);//指针
	cout << a << b << endl;//20 10
	swap2(a, b);//引用
	cout << a << b << endl;//10 20
	
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值