天天看点

C/C++ 中函数的地址传递和值传递

最初学习指针的时候这里就不是太清晰,后来发现坑终归是要补的,否则迟早掉进更大的坑里。
           

看到某位博主关于函数内地址传递和值传递的文章,感觉写的很不错,引文贴在下面,之后是我个人的一些理解。

https://blog.csdn.net/scrence/article/details/79835572

要想理解按值传递和按地址传递,首先要清楚什么是值和地址。

int a=1;   //a的值是1 
cout<<" a = "<<a<<endl;   // a = 1
int* b=&a;  //b是存储地址的数据类型——指针
cout<<" b = "<<b<<endl;  //b = 00A2F884
*b+=5;
cout<<" *b = "<<*b<<endl;  //*b = 6
cout<<" a = "<<a<<endl;  // a = 6
           

首先要明确一个概念就是: 地址是一种数据类型 , 地址绝对不只是输出出来的一个字符串(这一点可以尝试用string类型或者char数组来存储 &a ,编辑器会报错),因此存储地址必须要用一种特殊的数据类型进行存储,而这种数据类型被称为指针。由于不同的数据占用的内存不同,不同的数据类型占用内存的形式不同(如列表和链表),因此指针也分不同的类型,这里可以理解为一种伴生类型, 即每有一种数据结构,就有这种数据结构对应的指针,一般内置的数据结构如 int float double等,它们的指针类型是后面加 * 。 这也就解释了为什么一般写int* b 而不是int * b,因为前者表示的是一种数据类型。

然后再看运算符 “ & ”和“ * “, &表示的是取地址运算符,这个在scanf中已经多次用到,假如看作运算关系的话,可以理解为通过&运算,得到原有类型的地址。 同理 * 表示的是内容运算符,运算的对象必须是指针。看作运算关系可以理解为通过 * 运算,得到原有类型的内容。这两者互为可逆操作。有了这些基础,上面的代码就可以解释为 先是创建了一个整形a,然后创建了一个整形的指针 int* ,变量名为b ,由于b是一个指针变量,只能存储地址,因此需要赋值时要用到取地址运算符。这时单独说b,它存储的是一个指针,这个指针进行取内容操作就可以取a的值。 然后对b取内容,发现改变b的内容就是改变a,两者是等价的。(因为 b==&a )因为操作先要获取地址,然后再改变这里地址的值。

然后再来看值传递和地址传递

#include <iostream>
using namespace std;

void plus(int *a){
	*a+=5;
	cout<<"*a = "<<*a<<endl;//*a = 6
	cout<<"a = "<<a<<endl;//a = 012FFD94
	a++;
	cout<<"a = "<<a<<endl;//a = 012FFD98
	return ;
}

int main(){
	int a=1;
	int* b = &a;
	cout<<"b = "<<b<<endl; //b = 012FFD94
    plus(b);
	cout<<"a = "<<a<<endl;//a = 6
	cout<<"b = "<<b<<endl;//b = 012FFD94
	return 0;
}


           

按值传递:在调用函数中将原函数的值拷贝一份过去被调用的函数,在被调用函数中对该值的修改不会影响原函数的值。

按地址传递:在调用函数的时候将原函数的值所在的地址拷贝一份过去,被调用函数对这个地址所作的修改会影响原来的值。

其实理解了对指针取内容的操作等价于对指针指向的变量的操作就可以理解,本质上值传递或者是地址传递都是值传递,只是前者传递的是变量的值,后者传递的是指针的值, 可以看见,所谓的进行地址传递改变原来参数的值,本质上就是应用了指针的性质,而不是函数的性质。

应用:

#include <iostream>
#include <cstring>
using namespace std;

void reverse(char* a){
	int n=strlen(a);
	for(int i=0;i<n/2;i++){
		swap(a[i],a[n-1-i]);
	}
	return;
}

int main(){
	char a[100]="hello";
	cout<<a<<endl;  //hello
	reverse(a);
	cout<<a<<endl;  //olleh
	return 0;
}
           

要知道C++不能直接返回一个数组,因此要对某个数组或者字符串进行操作时,如果采用地址传递的方式,函数体对一个地址进行操作,从逻辑上会让思路更加清晰,同时也更简便,需要注意的地方是采用地址传递,函数传入的是一个地址,因此在函数运行时要注意地址和值之间的转换。

补充:实现字符串的反转有内置的函数

#include <algorithm>
string s="hello";
reverse(s.begin(),s.end());
char c[]="hello";
reverse(c,c+strlen(c));
char x[6]={'h','e','l','l','o'};
reverse(x,x+strlen(x));
           

继续阅读