Ref:
https://blog.youkuaiyun.com/wang386476890/article/details/82907259(指针)
https://blog.youkuaiyun.com/zhushh/article/details/40738809(指针引用传值)
https://blog.youkuaiyun.com/cherrydreamsover/article/details/81835976(c/c++区别)
指针
指针和变量一样,也是有地址的,只不过变量的值被解释成一个值,而指针的值被解释成一个地址。
函数参数传递
与按引用传递不同的是,函数内定义了一个局部的指针变量,消耗了内存空间,并且按地址传递,还可以对地址进行加减,指向另一个变量;
1.传递指针: 指向的地址相同,但指针地址不同
#include <stdio.h>
void func(int* p)
{
printf("*p = %d/n", *p);
printf("&p = %p/n", &p);
printf("&*p = %p/n", &*p);
}
int main(int argc, char *argv[])
{
int *a = new int(10);
printf("*a = %d/n", *a);
printf("&a = %p/n", &a);
printf("&*a = %p/n", &*a);
func(a);
return 0;
}
二级指针来传递就可以操作原有地址了
一级指针:里面的值是个地址,而这个地址里的值是个数值。
二级指针:里面的值是个地址,而这个地址里的值也是个地址。
void my_malloc(void** p, int size)
{
*p = malloc(sizeof(int)*size);
}
int *a;
my_malloc(&a , 10);//传递的是指针的地址
引用
按引用传递,可以改变实参的值,如上例中a==3,因为引用实际上变量的一个别名;
按引用传递,不会再定义局部变量,形参和实参共用一块内存区域;
按引用传递,可以不需要返回值,因为可以直接改变实参值;
按引用传递,适用于大型数组的传递,节省内存
总结:按值传递的函数无法改变实参;按引用和指针可以改变实参的值;按引用节省内存,所以比指针好用些。
二级指针,二维数组函数参数传递
传参规则
当二级指针作为函数形参时,能作为函数实参的是二级指针,指针数组,一级指针的地址
当数组指针作为函数形参时,能作为函数实参的是二维数组,数组指针
当二维数组作为函数形参时,能作为函数实参的是二维数组,数组指针
当指针数组作为函数形参时,能作为函数实参的是指针数组,二级指针,一级指针的地址
#include <stdio.h>
#include <stdlib.h>
void fun1(int **pp)
{
printf("fun1\n");
}
void fun2(int(*a_p)[5])
{
printf("fun2\n");
}
void fun3(int t_d_a[][5])
{
printf("fun3\n");
}
void fun4(int *p_a[5])
{
printf("fun4\n");
}
int main()
{
int *p_a[5]; //指针数组
int **pp = NULL; //二级指针
int *p = NULL; //一级指针
int t_d_a[5][5]; //二维数组
int a[5]; //一维数组
int(*a_p)[5] = &a; //数组指针
fun1(p_a);
fun1(pp);
fun1(&p);
//fun1(t_d_a);
//fun1(a_p);
printf("\n");
//fun2(p_a);
//fun2(pp);
//fun2(&p);
fun2(t_d_a);
fun2(a_p);
printf("\n");
//fun3(p_a);
//fun3(pp);
//fun3(&p);
fun3(t_d_a);
fun3(a_p);
printf("\n");
fun4(p_a);
fun4(pp);
fun4(&p);
//fun4(t_d_a);
//fun4(a_p);
printf("\n");
return 0;
}
传值/指针/引用对比:
1.传值:
输出:
before: &n =0x7fff579ae1f8, &m = 0x7fff579ae1fc
mySwap functioncalled:
mySwap: &a =0x7fff579ae1dc, &b = 0x7fff579ae1d8
mySwap function end:
after: &n =0x7fff579ae1f8, &m = 0x7fff579ae1fc
解释:
如之前所说,形参a和b的地址跟实参n和m的地址不一样,说明a和b所在的内存空间跟n和m的内存空间并不相同。
2.传指针:
输出:
before: &n =0x7fff6597d9e8, &m = 0x7fff6597d9ec
mySwap functioncalled:
mySwap: &a =0x7fff6597d9b8, &b = 0x7fff6597d9b0
mySwap: a =0x7fff6597d9e8, b = 0x7fff6597d9ec
mySwap function end:
after: &n =0x7fff6597d9e8, &m = 0x7fff6597d9ec
解释:
形参a和b有自己的内存空间,该空间存的值就是n和m的地址值。
3.传引用:
输出:
before: &n =0x7fff14d803e8, &m = 0x7fff14d803ec
mySwap functioncalled:
mySwap: &a =0x7fff14d803e8, &b = 0x7fff14d803ec
mySwap function end:
after: &n =0x7fff14d803e8, &m = 0x7fff14d803ec
解释:
形参a和b的地址分别和实参n和m的一样,只是说,a是n的别名,b是m的别名而已。
效率:
传值是需要拷贝和复制值,也就是说每次调用传值的函数,那么都会在子函数栈内开辟那么大的内存,然后再全部初始化赋值形参,在调用次数很大的情况下当然需要很多的时间。但是传指针是怎样的?因为指针每次只需要创建一个指针大小的内存空间,然后把指针的值赋值为指向main函数的结构体的内存地址空间,只需要一步操作即可完成。同样的对引用变量的操作也是如此
至于引用和指针的时间差是因为引用传值,引用更效率的原因是指针还要生成一个局部变量即给指针分配栈,而引用却不会生成
返回值
https://www.youkuaiyun.com/gather_2c/NtTaUg5sNTQtYmxvZwO0O0OO0O0O.html
返回非引用类型:
函数的返回值用于初始化在调用函数时创建的临时对象(temporary object),如果返回类型不是引用,在调用函数的地方会将函数返回值复制给临时对象。
在求解表达式的时候,如果需要一个地方存储其运算结果,编译器会创建一个没命名的对象,这就是临时对象。C++程序员通常用temporary这个术语来代替temporary object。
用函数返回值初始化临时对象与用实参初始化形参的方法是一样的。
当函数返回非引用类型时,其返回值既可以是局部对象,也可以是求解表达式的结果。
返回引用类型:
当函数返回引用类型时,没有复制返回值,相反,返回的是对象本身。
千万不要返回局部对象的引用!千万不要返回指向局部对象的指针!
当函数执行完毕时,将释放分配给局部对象的存储空间。此时对局部对象的引用就会指向不确定的内存!返回指向局部对象的指针也是一样的,当函数结束时,局部对象被释放,返回的指针就变成了不再存在的对象的悬垂指针。
返回引用时,要求在函数的参数中,包含有以引用方式或指针方式存在的,需要被返回的参数。
如果返回对象,最后多执行一次拷贝构造函数,如果返回引用,直接返回现存对象
1.引用作为函数的返回值时,必须在定义函数时在函数名前将&
2.用引用作函数的返回值的最大的好处是在内存中不产生返回值的副本
3.不能返回局部变量的引用。如上面的例子,如果temp是局部变量,那么它会在函数返回后被销毁,此时对temp的引用就会成为“无所指”的引用,程序会进入未知状态。
4.不能返回函数内部通过new分配的内存的引用。虽然不存在局部变量的被动销毁问题,但如果被返回的函数的引用只是作为一个临时变量出现,而没有将其赋值给一个实际的变量,那么就可能造成这个引用所指向的空间(有new分配)无法释放的情况(由于没有具体的变量名,故无法用delete手动释放该内存),从而造成内存泄漏。因此应当避免这种情况的发生
5当返回类成员的引用时,最好是const引用。