调用函数的地方是实参,函数运行地方是形参,形参是对实参的拷贝
主流编程语言中,下面的函数运行结果都是0,因为形参x是对实参a的拷贝,值类型的拷贝会创建新的内存。这种传参类型也叫值传递。
void main(){
int a = 0;
f(a);
printf("%d", a);//0
}
void f(int x){
x = 10;
}
将参数改为指针类型,指针是变量的地址或者说是变量的引用,传递指针的值也就是传递了变量的引用,这种传递叫引用传递。
void main(){
int a = 0;
f(&a);
printf("%d", a);//10
}
void f(int *x){
*x = 10;
}
在java、js、python等语言中是没有指针的概念的,但是对象变量本身存储的是对象内存的地址,所以可以通过对象进行引用传递。
var obj = {a: 0}
func(obj) // 传递对象实际上就是传递的地址
console.log(obj.a) //10
function func(x){
x.a = 10;
}
因为只有对象才暗含指针的含义,所以基础数据类型时没办法实现在函数中修改自己的值的,此外如果想通过函数修改一个对象的地址也是做不到的,这在c++中需要用双指针,或者指针的引用才能做到。
在golang中,使用了指针和地址,与C语言非常接近, *int表示int指针类型,&a是对a取地址。
func main(){
a:=0
f(&a)
println(a) //10
}
func f(x *int){
*x = 10
}
改变结构体的值,注意golang中的语法糖 (*x).age
可以直接写成x.age
,golang自动判断如果是指针取属性,就自动换成指针指向的结构体取属性
type user struct{
age int
}
func main(){
a:=user{0}
f(&a)
println(a.age) // 10
}
func f(x *user){
// x.age = 10 这两种方法都可改变a.age但是原理不同,这一行是在原地址上进行修改age这一个属性
*x = user{10} //这一行是直接将a内存中的整个数据换成新的
}
C++的引用类型int &x
是指针的语法糖,可以简化使用指针的写法,如下
void main(){
int a = 0;
f(a);
printf("%d", a);//10
}
void f(int &x){
x = 10;
}
引用暗含一层指针,那么指针的引用则可以实现双指针效果:
void changePerson(Person* p1, Person* &p2, Person** p3){
p1->age ++;
p2->age ++;
(*p3)->age ++;
}
void changePerson2(Person* p1, Person* &p2, Person** p3){
p1 = new Person(11, "11"); // 没用
p2 = new Person(22, "22");
*p3 = new Person(33, "33");
}
int main()
{
Person *p1 = new Person(1,"1");
Person *p2 = new Person(2,"2");
Person *p3 = new Person(3,"3");
printf("p1 %p, p2 %p, p3 %p\n", p1, p2, p3);
changePerson(p1, p2, &p3);
printf("p1 %p, p2 %p, p3 %p\n", p1, p2, p3);
printf("p1 %d, p2 %d, p3 %d\n", p1->age, p2->age, p3->age);
changePerson2(p1, p2, &p3);
printf("p1 %p, p2 %p, p3 %p\n", p1, p2, p3);
printf("p1 %d, p2 %d, p3 %d\n", p1->age, p2->age, p3->age);
//delete(p1);生产环境记得清理内存
}
/*
打印
p1 0x55e9cfc03eb0, p2 0x55e9cfc03ee0, p3 0x55e9cfc03f10
p1 0x55e9cfc03eb0, p2 0x55e9cfc03ee0, p3 0x55e9cfc03f10
p1 2, p2 3, p3 4
p1 0x55e9cfc03eb0, p2 0x55e9cfc04380, p3 0x55e9cfc043b0
p1 2, p2 22, p3 33
对象内部属性的改变,单指针就可以完成,更不用说其他两个
而直接改变指针内的地址,则需要双指针或者指针的引用
*/
rust也是面向过程的编程语言,同样是struct和其他基础类型都是值类型。他没有地址和指针,但有引用的概念,引用作为函数参数又叫做借用。
rust有一些设计原则:
- 一个变量只能有一个owner,但是可以引用该变量,和借用变量放入函数中,引用与借用本质是内存地址
- 可变引用在一个作用范围只能有一个