C++编程是一门相对底层,可直接访问内存的高级语言,其中指针、引用、数组是C++访问内存数据的重要方式,下面从内存出发,以数组进行举例,并对指针和引用进行详细说明
内存
在介绍指针、引用、数组之前,我们先来了解内存:
内存是程序运行时存储数据的区域,通常分为栈区、堆区、全局/静态区、常量区、代码区这五个区域
其中,栈区主要用来存储局部变量;堆区主要用于存储动态数组、对象;全局/静态区是专门存储全局变量和静态变量的区域;常量区是存储字符串常量的区域;代码区是存储程序的二进制执行代码的区域
每个分区内部的同一分配单元的内存都是连续的,下面用数组进行举例~
数组
C++中的数组是在连续的存储空间中存储同类型数据的数据结构。数组主要存储在栈区、全局/静态存储区(动态数组存储在堆区)
数组的常见表达式为
int arr[6] = {1,2,3,4,5,6};
以下便是数组的存储结构示意图:
如图所示,这是整型数组的内存存储结构,6个整型的内存单元连续排布,每个内存单元占 4 字节,整体占用 24 字节的连续内存空间。其中,每个内存单元都有独一无二的地址标识符
其中,数组中第一个元素在内存中的起始地址就是数组的首地址。在C++中,数组名就代表数组的首地址。
在上述例子中,int arr[6];的首地址就是arr(下面指针重点讲)
指针
指针是存储内存地址的变量
声明方法
指针的是特殊的变量,声明时需要用*标识符来表示某个类型的指针,如果是一个整型的指针,就用int *来声明:
int a = 10;
int* p = &a;
上述代码中,int*是指针类型声明符,声明p是一个指向 int 类型的指针变量
&a中&是取地址运算符,作用是获取变量a对应的内存单元的地址
总体是把a的地址值,赋值给指针变量p,也就是说,指针p存储变量a的地址
注意:这里的指针是p,而不是
*p,*只是作为标识符
指针的作用
指针可以存储变量的地址,那么这有什么用呢?主要有以下三方面的作用:
首先是指针可以直接操作内存单元,实现高效的数据访问和修改,指针通过存储内存单元的地址,就能实现直接定位内存目标,实现高速的访问和数据修改。比如,int arr[6]; int* p = arr,p+1能直接跳转到下一个数组元素的内存单元,比下标访问更贴近内存底层,效率更高
第二就是可以减少数据拷贝。当传递大的数据时,直接传变量会复制一份数据到函数中,占用内存较大,而传指针只需传递内存地址,能避免资源浪费,提升程序性能
// 传值:拷贝整个数组(低效,尤其数组很大时)
void modifyArray(int arr[6]) { arr[0] = 100; }
// 传指针:仅传递数组首地址(高效,无论数组多大)
void modifyArrayByPtr(int* p) { *p = 100; }
除此之外,指针还可以实现动态内存分配, 实现复杂数据结构等等,在此处就不作详细说明~
✳的意义
*在指针的声明中和指针的使用中有不同的意义
✳在声明中
*在指针声明中是类型修饰符,表示变量是指针类型
比如int* p = &a;,这里的int*是一个整体,*是 int 类型的后缀,共同表示p的类型
✳使用中(解引用)
*在指针使用中表示解引用,根据指针存储的地址,找到对应的内存单元
- 取p指向的内存单元的值:
printf("a的值:%d\n", *p);
- 通过
*修改指针指向的内存单元的值:
*p = 20;
这里的 *p 不再是类型声明,而是操作内存,p存的是a的地址,*p就是地址对应的那个内存单元里的内容
再次提醒:这里的
*p是整体,和在声明时不一样~
以上就是✳在指针的声明中和使用中的不同作用,初学者很容易混淆~
指针与数组的关系
在C/C++中,指针与数组的关系非常紧密~
由上述例子可以得知,数组每个内存单元都有独一无二的地址标识符,指针可以存储和访问这些标识符
int arr[6] = {10,20,30,40,50,60};
int* p = &arr[0];
上面提到了数组的首地址,并了解到数组名就代表数组的首地址
由于指针访问数组的首地址是
int* p = &arr[0];
用数组名的表示方法可以改成
int* p = arr;
数组首地址&arr[0]和数组名arr在C++数组上是等价的,数组首地址就是数组名
换句话说,数组的首地址就是个指针常量,意思是指针本身是常量,区别于普通的指针是变量,指针常量是指针变量存储的地址值不可修改,但该地址指向的内存空间里的值可以修改~
引用
引用就是内存单元的别名,引用和指针不一样,引用不是新变量,只是目标变量的别名,和目标变量共享同一块内存地址
声明方法
引用的声明语法格式如下:
int a = 10; // 定义原变量a,值为10
int &ra = a; // 定义引用ra,作为a的别名
其中,&是引用声明符,是类型int的一部分,表示ra是引用类型,ra是引用名
=必须在定义引用时必须初始化,不能先定义再赋值
取地址运算符
&除了可以作为类型声明符外,还可以做取地址运算符
当&做取地址运算符时,其作用是返回操作数在内存中的唯一内存地址
往往和指针一起搭配使用
int *p = &a;
&a获取a的地址,赋值给指针p
数组元素取地址如下:
int arr[6] = {10,20,30,40,50,60};
cout << "arr[0]的地址:" << &arr[0] << endl; // 数组首元素地址
cout << "arr[3]的地址:" << &arr[3] << endl; // 第四个元素地址
一定不能混淆&在引用声明和引用使用中的作用,在声明引用时,&是类型声明符,在非声明时,&就是取地址运算符
指针与引用
联系
指针和引用本质都是对内存地址的操作,二者的底层逻辑都是地址寻址,实现传址操作,修改原数据,而且都可间接操作数组元素,甚至在部分场景是可以互相替代的,例如绑定单个变量
指针写法:
int *p = &a; *p=20;
引用写法:
int &ref=a; ref=20;
都实现了对a原值的修改
区别
指针和引用虽然有比较紧密的联系,但是二者在本质上还是有所区别
指针是存储地址的独立变量,并且是占用内存的;引用是变量的别名,无独立内存,和目标变量共享内存
指针可自由修改指向;引用一旦绑定,永远不能改指向
在数组问题上,指针可以可指向数组名,支持指针偏移遍历;引用只能绑定数组单个元素,不能绑定数组名
总结
本文从内存介绍出发, 以数组举例, 进而讲解指针和引用基本知识和用法, 主要是为了帮助大家进一步了解指针与引用的区别和联系, 同时要注意不要混淆*和&在不同场景中的不同作用, 以上就是本篇文章的所有内容~
如果这篇文章对你有用的话,欢迎点赞+收藏哦~