C++ 指针与引用

一:指针

1.1:指针详解

变量的地址的概念
内存是以字节为单位进行编址的,内存中的每个字节都对应一个地址,通过地址才能找到每个字节。变量对应内存中的一段存储空间,该段存储空间占用一定的字节数,用这段存储空间的第一个字节的地址表示变量的地址。

指针的概念
指针是一个变量,与普通变量不同的是,普通变量中存储的是数据,指针变量中存储的是另一个变量的地址。这样我们就可以通过指针间接的对普通变量进行操作。

将指针变量加1后,其增加的值等于指向的类型占用的字节数

指向普通变量的指针2种初始化方法
方法一:
int a = 0;
int *p = &a;
*p = 3;
p:所指向的变量a的地址
&p:该指针变量p的地址
*p:所指向变量a的值
变量内存位于栈(stack)区

方法二:
int *p = new int;
*p = 100;
delete p;
变量内存位于堆(heap)区

1.2:指针与数组

数组的动态联编(运行时分配空间,数组长度在运行时确定)
int * p = new int[3];
p[0] = 1;
p = p + 1;
delete [] p;
p[1] = *(p + 1)

静态联编(数组长度编译时确定)
int arr[3] = {1,2,3};
int *p = &arr[0];
int *p = arr;
arr[2] = *(arr + 1);

数组名和指针等价,都是地址;(数组名指的是第一个元素的地址)
区别:
1、指针值可以修改,数组名不行;
2、sizeof对于数组是数组的长度,对于指针是指针的长度

arr[i] == *(ar_ptr + i)
&arr[i] == ar_ptr + i

指向一维数组的数组指针
int a[10] = {0};
int *p = a;
p[i]; p++; p+i;
注意:只有当指针指向一串连续的存储单元时,指针的移动才有意义。才可以将一个指针变量与一个整数n做加减运算。
数组名代表数组(元素)的首地址,即第一个元素的地址。对数组的首地址加上偏移量x就可以得到其它元素的地址。

指针数组,内部全部存储指针;
int* arr[10];

指向二维数组的指针
int arr[3][4];
int (*p)[4] = arr;

void fun(int *) 等效于 void fun(int arr[])
使用const修饰,可以避免修改数组值 void fun(const int arr[])

1.3:指针的指针

二维/二级指针,指针的指针(int**p)
int a = 12;
int *b = &a;
int **c = &b;

a 12
b &a
*b a、12
c &b
*c b、&a
**c *b、a、12

1.4:悬挂指针(野指针)

指向非法的内存地址(垃圾内存的地址),那么这个指针就是悬挂指针,也叫野指针,意为无法正常使用的指针

野指针的成因主要有3种:
1、指针变量没有被初始化。 任何指针(全局指针变量除外,全局指针变量为NULL)变量在刚被创建的时候不会自动成为NULL指针,它的缺省值是随机的。所以指针变量在创建的时候,要么设置为NULL,要么指向合法的内存。
2、指针p被free/delete之后,没有置为NULL(最好加一句p = NULL;),经常性的我们会以为p是个合法的指针。他们只是把指针指向的内存给释放掉,并没有把指针本身干掉,此时指针指向的就是“垃圾”内存。所以我们应该在释放完之后,立即将指针置为NULL,防止出现乱指的情况
3、 指针操作超越了变量的作用范围。不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。也就是说指向的对象的声明周期已经结束了,然而我们还使用该指针访问该对象

二:引用

2.1:引用详解

概念::
1、引用:就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。
2、引用的声明方法:类型标识符 &引用名=目标变量名;
3、定义一个引用时,必须对其初始化
4、引用仅是变量的别名,而不是实实在在地定义了一个变量,因此引用本身并不占用内存,而是和目标变量共同指向目标变量的内存地址
5、使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好
6、不能建立引用的数组

作用::
1、能够修改调用函数中的数据对象
2、通过传递引用而不是整个数据对象,可以提高程序的运行速度

使用场景::
对于传递使用的值而不做修改的函数:
1、如果数据对象很小,如内置数据类型或小型结构,则按值传递
2、如果数据对象为数组,则使用指针,并将指针声明为指向const的指针
3、如果数据对象是较大的结构,则使用const指针或者const引用,以提高程序的效率。这样可以节省复制结构所需的时间和空间
4、如果数据对象是类对象,则使用const引用

对于修改调用函数中的数据的函数
1、内置数据类型用指针
2、数据对象为数组,则只能使用指针
3、数据对象为结构,使用引用或指针
4、数据对象为类对象,则使用引用

引用作为函数返回值::
1、不能返回局部变量的引用
2、不能返回函数内部new分配的内存的引用
3、可以返回类成员的引用,但最好是const

引用与多态::
引用是除指针外另一个可以产生多态效果的手段。这意味着,一个基类的引用可以指向它的派生类实例

2.2:引用与指针的区别

★相同点:
都是地址的概念;指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名。具体来说,指针是一个变量的地址,引用是一个变量的别名

★不同点:
1、指针是一个实体,而引用仅是个别名;
2、引用必须被初始化,指针不必
3、引用只能在定义时被初始化一次,之后不可变;指针可以改变所指的对象
4、引用不能为空,指针可以为空;
5、sizeof 引用得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;
7、程序为指针变量分配内存区域,而引用不需要分配内存区域
8、指针和引用的自增(++)运算意义不一样;
9、引用是类型安全的,而指针不是 (引用比指针多了类型检查)

2.3:传值与传址

1、采用传值调用方式时,在被调用函数中改变形参的值,只是改变其副本值,而不会影响调用函数中实参值
2、采用引用调用方式时,传递的是变量的地址值,这样在被调函数中,对形参的操作实际上操作的是实参本身
3、数组作为函数传递时,实际采用引用调用方式

2.4:变量a、指针*a、引用&a作为函数形参时的区别

#include <iostream>
using namespace std;
//变量作为形参
void swap1(int a, int b)
{
       a = a + b;
       b = a - b;
       a = a - b;
}
//指针作为形参
void swap2(int *a, int *b)
{
       *a = *a + *b;
       *b = *a - *b;
       *a = *a - *b;
}
//引用作为形参
void swap3(int &a, int &b)
{
       a = a + b;
       b = a - b;
       a = a - b;
}
int main()
{
       int a = 2;
       int b = 3;
       swap1(a, b); //普通变量时,不影响实参
       cout << a << ',' << b<< endl;//a = 2, b = 3
       swap2(&a, &b);//指针变量时,不影响指针本身,但通过指针可影响其指向的变量的值
       //要想修改指针本身,则函数形参为指针的指针**p
       cout << a << ',' << b << endl;//a = 3, b = 2
       swap3(a, b); //引用变量时,可修改实参值
       cout << a << ',' << b << endl; // a = 2, b = 3
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值