前言
在C语言中,我们对指针的规范并不严谨------我们常常会用void*
来传入一个不知道其变量类型的指针,为了兼容C
,Cpp
允许这样的语法存在,但是,由于这样的指针不具有安全性,我们无法通过*
来解引用void*
,这就需要用到我们的显式转换 。为了方便看指针的引用而不是由一堆*
组成了参数和参数类型,我们采用引用来让这种写法变得更加简洁;
文章目录
指针
什么是指针?指针是一个数字,是一个存储内存地址的数值。 指针是用来给我们取址用的操作,方便我们对数据的读取和写入,因为我们在代码中所做的每一件事,都是从内存中读取或写入内存------类型只是我们虚构出来的东西,好让我们这群写程序的知道这个指针到底有多少个字节组。
cpp
//The Example is from the Cherno
#include<iostream>
int main(){
void* ptr=0;//我们就这样创造出来了第一个指针变量
//std::cin.get();
return 0;
}
内存地址不会是0
,0
是无效的一个数值,我们也用NULL
来表达其值为空,对于指针来说,无效是一个完全可以接受的状态 ,++人家接受得了许多失败++ ,但是,很遗憾的是,0
既然意味着没有,那么就是说该数值无法读取或写入。
当然,#defined NULL 0
说明了在预处理器上NULL
就被处理成了0
,我们在C++11
引入了一个新的概念,称之为nullptr
。
请避免将
NULL
或零 (0
) 用作 null 指针常量;nullptr
不仅不易被误用,并且在大多数情况下效果更好。例如,给定
func(std::pair<const char *, double>)
,那么调用func(std::make_pair(NULL, 3.14))
会导致编译器错误。 宏NULL
将扩展到0
,以便调用std::make_pair(0, 3.14)
将返回std::pair<int, double>
,此结果不可转换为func
的std::pair<const char *, double>
参数类型。调用
func(std::make_pair(nullptr, 3.14))
将会成功编译,因为std::make_pair(nullptr, 3.14)
返回std::pair<std::nullptr_t, double>
,此结果可转换为std::pair<const char *, double>
。------From nullptr, the pointer literal (since C++11) - cppreference.com
类型完全不会影响指针
我们有:
cpp
#include<iostream>
int main(){
int var=23;
void* ptr=&var;
return 0;
}
这个时候void*
可以接收到var
为int
类型变换,
cpp
#include<iostream>
int main(){
int var=23;
double var=(double)&var;
return 0;
}
我们会发现在指针中获得的地址还是跟先前从void*
中读取到的int
的数值一样,这就证明了指针类型只是一种辅助工具,帮助我们和编译器去理解这个变量的类型。
cpp的强制转换类型是比c语言灵活一点 double(var)也可以。
我们解引用 告诉编译器,这是一个int
,short
,longlong
的变量,这样我们好确定我们的如何从内存中读取计算出我们的数值。
当然,我们可以利用char*
和new
关键字来确定一个内存大小。
cpp
#include<iostream>
int main(){
char* buffer=new char[8];
memset(buffer,0,sizeof buffer);
char** ptr=&buffer;
delete[] buffer;//用完后删除
return 0;
}
引用
引用(Reference)实际上是指针(Pointer)的一个扩展,我们如何写他们,和应用他们都有所不同,但是,本质上他们并无二异义,并且引用只能算一种语法糖。
cpp
#include<iostream>
int main(){
int a=5;
int& ref=a;//ref这个'变量'其实是引用。
ref=2;//实际上就是'变量'a这个主体变成了2.
return 0;
}
就好比说,我叫MongXin,但是我身份证不叫MongXin,在警察局里找不到MongXin,却不能说明这个引用不存在------同样的,申明完一个引用后,其并不真实存在,但是其就是那个变量的别名 ;我可以注册一个电话号码,注册完之后它即属于我身份证上的名字,也属于MongXin ,同样的,ref
就是有这样的作用,我们可以把它当a
使用。
&
不可更改
就像你有好几个好朋友,但是你能把好朋友A的别名冠给好朋友B吗?这本身算是一种不礼貌的行为,我们的Cpp
是一位绅士,拒绝这种情况的发生。
cpp
#include<iostream>
int main(){
int a=5;
int b=8;
int& ref=a;//ref这个'变量'其实是引用。
int& ref=b;//ERROR
return 0;
}
这点跟指针的const void*
一样,我们无法更改我们指向的内存空间,但是不妨碍我们把我们的家具挪一挪变成一个新的数值。
pass-by-value和pass-by-reference
如果我们有:
cpp
#include<iostream>
void Increment(int val){
val++;
}
void log(int val){
std::cout<<val<<endl;
}
int main(){
int a=5;
Increment(a);
log(a);
return 0;
}
我们会发现打印出来的数值是5
,并没有增加,而我们稍微动脑筋就知道,这个时候val
是一个局部变量,它是copy
出来的一份数据,局部变量传不出去,我们将这种引用数值的方式称之为Pass-by-value ,Pass-by-value 在不需要更改数值的时候可以使用,但是在我们对数值需要更改的情况下就显得笨拙,这时候我们采用------一种引用地址的方式,Pass-by-reference。
cpp
#include<iostream>
void Increment(int* val){
(*val)++;
}
void log(int val){
std::cout<<val<<endl;
}
int main(){
int a=5;
Increment(&a);
log(a);
return 0;
}
我们也存在利用引用的情况:
cpp
#include<iostream>
void Increment(int& val){
val++;
}
void log(int val){
std::cout<<val<<endl;
}
int main(){
int a=5;
Increment(a);
log(a);
return 0;
}
通过引用,我们会发现我们重写了这段代码,但是在编译后,我们得到的代码是一样的,只不过我们的源码现在看起来很容易懂。
二者差异
唯二的区别就是,写法不一样,以及引用名并不真实存在。
如果通过指针:
cpp
void log(*val){}//这里是解引用
int val=3;
int* ptr=&val;
log(ptr);//相当于log(&val);
如果通过引用:
cpp
void log(&val){}//这里是&
int val=3;
log(val);
写在后面
我们发现Cpp
跟C
本质上还是有差别的,一些C
里面麻烦的东西在Cpp
中得到了优化,而且Cpp
还可以兼容运行C
,这就说明了Cpp
的兼容性高,这也是我们下一篇章的类的重点。