近几年感觉python用的很多,某一天在力扣上看cpp题解,发现突然有点忘了swap函数原理了,重新学习了下cpp基本知识。
当要交换变量值的时候,可以自己写swap函数,c++也有自带的swap函数,且不用include任何库。
#自写swap函数
错误写法
由于C++的传参是虚假的,在调用函数传入参数的时候会生成与原参数列表完全相同的临时参数参与函数的执行(相当于外部传入的真正参数就被丢弃了),所以如果不是传入指针或者引用,函数外参与调用的参数在函数执行结束后不会改变,比如:
void myswap(int a,int b){ //自己写swap函数的时候注意函数名不要直接用'swap',否则会和cpp自带的swap函数冲突
int tmp=a;
a=b;
b=tmp;
}
void main(){
int a=1,b=2;
myswap(a,b);
printf("%d %d",a,b); //仍旧是1,2
return 0;
}
在上面的主函数中调用myswap函数,调用时候会生成两个临时变量a’=a,b’=b(其实a’就是a,b同理。但是为了方便理解,写成a’),a’,b’取代主函数的a,b参与myswap函数的执行,所以执行结束仅仅是a’,b’的值交换,退出函数时销毁,主函数中真正的a,b还是没有变。
想要真正改变值,可以传入指针或者引用。
指针写法
指针是个地址值,在传入参数时,即使生成了临时变量,也是地址值,对地址指向的内容操作,就能改变变量值。
void myswap(int *a,int *b){//传入的是a和b的地址值
int tmp=*a; //除了声明或初始化指针时,*代表指针的意思,其他时候*在指针变量名左边代表指针指向的内容。这里tmp=a地址指向的内容,即a的值
*a=*b; //指针a指向的地址空间内容变成指针b指向的地址空间内容,即a=b
*b=tmp; //同理b指向的地方=tmp/a,完成交换
}
void main(){
int a=1,b=2;
myswap(&a,&b); //传入地址值
printf("%d %d",a,b); // 输出为2 1
return 0;
}
引用写法
使用指针不注意会引起一些越界错误,所以更加稳妥的是使用引用。
引用又叫别名,类似指针但是又不是指针,看到过一篇讲指针和引用区别的博文点这里,这里面讲的比较清楚:
从现象上看,指针在运行时可以改变其所指向的值,而引用一旦和某个对象绑定后就不再改变。这句话可以理解为:指针可以被重新赋值以指向另一个不同的对象。但是引用则总是指向在初始化时被指定的对象,以后不能改变,但是指定的对象其内容可以改变。
这段话可以这么理解:指针变量有自己的地址空间,这块地址空间存的内容是一个地址值,地址值(指针变量值)可以改变,当地址值改变的时候,就代表指针指向的地方改变。
而使用引用的时候,必须要初始化,比如int a=3; int &b=a;
变量a首先有一个自己的地址空间,假设为0x00000001,那么这个引用b可以理解为一根指向0x00000001地址的“指针”,一旦初始化,b这根“指针”就不能再像一般的指针一样,再指向0x00000002或者其他地址空间。但是它可以像指针一样改变0x00000001的内容。比如b=10;
,就是直接对地址空间0x00000001进行操作,这条代码执行后,a的值也变成了10,等于a=10;
。
从编译上看,程序在编译时分别将指针和引用添加到符号表上,符号表上记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为引用对象的地址值。符号表生成后就不会再改,因此指针可以改变指向的对象(指针变量中的值可以改),而引用对象不能改。这是使用指针不安全而使用引用安全的主要原因。从某种意义上来说引用可以被认为是不能改变的指针。
用图表示上面这段话:
符号表确定后,a,p,b所在的地址值就不会改变了。引用的地址即为绑定的变量a地址,所以对b进行操作,就是对a进行操作。p的地址2中的内容可以改变,如虚线箭头,可以改成1/2/5……代码int a=3; int &b=a;
和代码int a=3; int b=a;
的区别是前者a和b在符号表的地址是一样的,即两个变量名共享一个地址空间,而后者a和b的地址不一样,自己有自己的空间。
用指针的思想来理解引用,是这个意思:
初始化之后,b的绿色虚线只能指向a,而不能像p的蓝色虚线一样可以随便指向任何地址空间。
指针p的改变=指向的地址空间改变→通过*操作符改变指向地址空间的值。
引用b的改变=直接改变a的值,即地址1内存的内容(同时写法没有指针繁琐,也比指针安全)。
引用在调用函数的时候如果需要修改传入的参数值时用的比较多,如果没有跨越函数,只是对一个变量进行修改,直接利用原变量就行,不需要引用来多此一举,而为了在调用并修改参数值的时候更好理解引用,所以才用指针的思想去解释。引用本身不是指针。
void myswap(int &a,int &b){ //传入的为引用,调用该函数的时候,会生成引用a=a,引用b=b,所以函数里操作引用的时候,就是修改了原值。
int tmp=a;
a=b;
b=tmp;
}
void main(){
int a=1,b=2;
myswap(a,b);
printf("%d %d",a,b); // 2 1
return 0;
}
这段代码和第一段错误的代码中唯一的区别就是myswap函数的参数的类型,在错误的代码中,执行myswap函数时候,生成新的变量a和b,他们会有自己的内存空间(或栈中),在函数中改变(新的)a和b的值,先去找到(新的)a和b的地址,再改变,这个改变和原地址无关。但是当参数是引用时,新的变量a和b的地址就是原来a和b的地址,所以修改的时候找到a和b的地址的时候,找到的就是原地址,所以改了引用a和b,就是改变了原来的a和b。
一张图简单说明三种传递方式(这里的指针p只是一个普通的指针变量):
①错误的方式
②指针方式
③引用方式
#C++自带swap函数
void main(){
int a=1,b=2;
swap(a,b);
printf("%d %d",a,b); // 2 1
return 0;
}
调用的时候并不需要传入地址值,所以按照上面所说应该是使用了引用,查看C++API参考文档,可以看到确实传入的是引用,但是是比较高级的模板为参数,以适应各种参数的调用。