最详细的讲解C++中指针的使⽤⽅法(通俗易懂)
⼀、指针的定义与使⽤
指针是⾼级编程语⾔中⾮常重要的概念,在⾼级语⾔的编程中发挥着⾮常重要的作⽤,它能使得不同区域的代码可以轻易的共享内存数据。指针使得⼀些复杂的链接性的数据结构的构建成为可能,有些操作必须使⽤指针,⽐如申请堆内存,还有C++或者C语⾔中函数的调⽤中值传递都是按值传递的,如果在函数中修改被传递的对象,就必须通过这个对象指针来完成。指针就是内存地址,指针变量就是⽤来存放内存地址的变量,不同类型的指针变量所占⽤的存储单元长度是相同的,⽽存放数据的变量因为数据类型不同,因此所占的存储空间长度也不同。使⽤指针不仅可以对数据本⾝,也可以对存储数据变量的地址进⾏操作。很多萌新在刚开始学习编程的时候会被指针搞蒙,那现在就让我给⼤家详细讲解指针的使⽤。
1、指针的引⼊(函数返回中return语句的局限性)
函数的缺陷:
⼀个函数只能返回⼀个值,就算我们在函数⾥⾯写多了return语句,但是只要执⾏任何⼀条return语句,整个函数调⽤就结束了。
数组可以帮助我们返回多个值,但是数组是相同数据类型的结合,对于不同数据类型则不能使⽤数组
使⽤指针可以有效解决这个问题,使⽤指针我们想反回⼏个值就能返回⼏个值,想返回什么类型就可以返回什么类型的值。在程序设计过程中,存⼊数据还是取出数据都需要与内存单元打交道,计算机通过地址编码来表⽰内存单元。指针类型就是为了处理计算机地址数据的,计算机将内存划分为若⼲个存储空间⼤⼩的单元,每个单元⼤⼩就是⼀个字节,即计算机将内存换分为⼀个⼀个的字节,然后为每⼀个字节分配唯⼀的编码,这个编码即为这个字节的地址。指针就是⽤来表⽰这些地址的,即指针型数据不是什么字符型数据,⽽存的是我们内存中的地址编码。指针可以提⾼程序的效率,更重要的是能使⼀个函数访问另⼀个函数的局部变量,指针是两个函数进⾏数据交换必不可少的⼯具。
地址及指针的概念:
程序中的数据(变量,数组等)对象总是存放在内存中,在⽣命期内这些对象占据⼀定的内存空间,有确定的存储位置,实际上,每个内存单元都有⼀个地址,即以字节为单位连续编码。编译器将程序中的对象名转换成机器指令识别的地址,通过地址来存储对象值。
int i; double f;
计算机为int 类型数据分配4个字节,为double 类型分配8个字节。按对象名称存取对象的⽅式成为对象直接访问,如:i=100; f=3.14; 通过对象地址存取对象的⽅式成为指针间接访问
如图,这⾥有⼀个名为4000的存储空间它放的不是数值,⽽是 i 这个变量的地址,i 占有四个字节每个字节都有⼀个地址,这个变量的地址其实就是第⼀个字节的地址,在这⾥ i 的地址指就是他第⼀个字节的地址,假设第⼀个地址是4000,这⾥有⼀个p,先不管他是个啥东西,他占了⼀段存储空间,他⾥⾯放的就是 i 的地址,现在我们要访问 i 但是我们不直接访问,不出现 i 的名字,⽽是通过到 p 得到了p⾥⾯放的 i 的地址以后就能间接的去访问 i ,就是说我们不是直接访问这个地址的,⽽是通过 P 这个量去访问i 的值,这样的访问⽅式就叫做指针间接访问。即通过对象的地址来存储对象的⽅式称为指针间接访问。
现在我们来举个例⼦详细了解⼀下指针到底是⼀个什么东东
⼀个快递员送快递,如果送的是⼀个⽼顾客,那么快递员可以直接跑到顾客的办公地点,将快递交给她就⾏了,这就是直接访问。但是如果现在快递员拿到了⼀个不认识的⼈的快递他是⼀个新顾客,那么快递员跑到他的办公地点,办公地点有很多⼈,他也分不清谁是谁,于是快递员到了这⾥的⼀个保安问“张三是谁”保安就会给他指出张三就是这个⼈。然后快递员才能把快递送到顾客⼿上。此时这个中间来指出张三的⼈(保安)就是起了指针的作⽤,快递员⽆法直接到张三,因此他只有通过保安才能给他指出张三是谁,这就叫做间接访问。
2、指针的定义形式⽅法及其含义
c++将专门⽤来存放对象地址的变量叫做指针变量,下⾯是指针变量定义形式:
指针类型* 指针变量名;
例如:
int* p, i; //定义指针变量p,i为整形变量
指针函数的作用p = &i; //指针变量p指向i
把 i 的地址放到p
i ⾥放的是100这个数他的地址是4000,p⾥⾯放的就是i 的地址,然后就将p 指向i,他俩的关系即为p指向i,
假定指针变量p 的值是4000,三种写法:
char* p;
int* p;
double* p;
指针的类型其实就是它所指向的对象的类型
指针的类型:
指针的类型表明的就是他所指向的对象的类型,把p定义为char类型,即⽤char类型的指针P来间接访问它指向的对象时我们间接引⽤的是⼀个字节的空间,假设p放的是4000,则系统会默认p指向的是4000这⼀个字节⾥⾯的内容,若将p定义成整形那么意味着我们⽤间接引⽤的⽅式来⽤指针指向对象的时候,系统就会认为你 的指针占4个字节这个对象就是4000,4001, 4002, 4003 这四个字节共同组成这个对象,若定义为double类型,那么系统就会认为他指向的对象时8个字节的即4000~~4007这⼋个字节都认为是P所指向的对象。这就是指针类型的含义,指针的类型应该和他指向的对象的类型⼀致,即整形指针应该指向整形变量,实型指针指向实型的变量。
3、通过指针间接访问
通过间接引⽤运算 * 可以访问指针所指向的对象或者内存单元,即在指针前⾯加上⼀个 * 就表明指针所引⽤的东西
int a, * p = &a;
a = 100;//直接访问a(对象直接访问)
*p = 100;//*p就是a,间接访问a(指针间接访问)
*p = *p + 1;//等价于a=a+1
int a, b, * p1 = &a, * p2;
&*p1 的含义:
&和*都是⾃右向左运算符,因此先看p1是指针,*p1即为p1指向的对象,
因此*p1等价于a,因此a 的前⾯加⼀个&,表⽰的就是 a 的地址
因此:&*p1 ,p1 ,&a 三者等价
*&a 的含义:a的地址前加*表⽰的就是a本⾝,指针就是⽤来放地址的,地址前⾯加*表⽰的就是这个对象
因此: *&a ,a ,*p 三者等价
int main()
{
int i = 100, j = 200;
int* p1, * p2;
p1 = &i, p2 = &j;//p1指向i,p2指向j
*p1 = *p1 + 1;//等价于i=i+1
p1 = p2;//将p2的值赋给p1,则p1指向j
*p1 = *p1 + 1;//等价于j=j+1
return 0;
}
4、指针的初始化,可以在定义指针时对它进⾏初始化
指针类型* 指针变量名 = 地址初值,......
int a;
int* p = &a;//p的初值为变量a 的地址
int b, * p1 = &b;//p1初始化是变量b已有地址值
由于指针数据的特殊性,他的初始化和赋值运算是有约束条件的,只能使⽤以下四种值:(1)0值常量表达式:
int a, z = 0;
int p1 = null; //指针允许0值常量表达式
p1 = 0;//指针允许0只常量表达式
下⾯三中形式是错误的:
int* p1 = a;//错误地址初值不能是变量
int p1 = z;//错误整形变量不能作为指针,即使值为0
p1 = 4000;//错误,指针允许0值常量表达式
(2)相同指向类型的对象的地址。
int a, * p1;
double f, * p3;
p1 = &a;
p3 = &f;
p1 = &f;//错误p1和f指向类型不同
(3)相同指向类型的另⼀个有效指针
int x, * px = &x;
int* py = px;//相同指向类型的另⼀个指针
(4)对象存储空间后⾯下⼀个有效地址,如数组下⼀个元素的地址
int a[10], * px = &a[2];
int* py = &a[++i];
5、指针运算
指针运算都是作⽤在连续存储空间上才有意义。
(1)指针加减整数运算
int x[10], n = 3, * p = &x[5];
p + 1 //指向内存空间中x[5]后⾯的第1个int 型存储单元
p + n //--------------------------n(3)个
p - 1 //-------------------前⾯-----1个
p - n //
(2)指针变量⾃增⾃减运算
int x[10], * p = &x[5];
p++ //p指向x[5]后⾯的第1个int型内存单元
++p //-----------------1--------------
p-- //p指向x[5]前⾯的第1个int型内存单元
--p //--------------------------------
(3)两个指针相减运算
设p1, p2是相同类型的两个指针,则p2 - p1的结果是两⽀针之间对象
的个数,如果p2指针地址⼤于p1则结果为正,否则为负
int x[5], * p1 = &x[0], * p2 = &x[4];
int n;
n = p2 - p1;//n 的值为4 即为他们之间间隔的元素的个数
运算⽅法:(p2储存的地址编码-p1储存的地址编码)/4 若是double类型则除以8 char类型除以1
(1)指针加减整数运算
int x[10], n = 3, * p = &x[5];
p + 1 //指向内存空间中x[5]后⾯的第1个int 型存储单元
p + n //--------------------------n(3)个
p - 1 //-------------------前⾯-----1个
p - n //
(4)指针的运算关系
设p1、p2是同⼀个指向类型的两个指针,则p1和p2可以进⾏关系运算,
⽤于⽐较这两个地址的位置关系即哪⼀个是靠前或者靠后的元素
int x[4], * p1 = &x[0], * p2 = &x[4];
p2 > p1; //表达式为真
6、指针的const限定
(1)⼀个指针变量可以指向只读型对象,称为指向const对象的指针定义形式是:
const 指向类型 *指针变量,...
即在指针变量前加const限定符,其含义是不允许通过指针来改变所指向的const对象的值,不能通过间接引⽤来改变它所指向的对象的值
const int a = 10, b = 20;
const int* p;
p = &a;//正确 p不是只读的,把a的地址赋给p,给p赋值是允许的
p = &b;//正确,p不是只读的
*p = 42;//把42赋给p所指向的对象。错误,*p是只读的
(2)把⼀个const对象的地址赋给⼀个⾮const对象的指针是错误的,例如:
const double pi = 3.14;
double* ptr = π//错误,ptr是⾮const所指向的变量
const double* cptr = π//正确,cptr是const指针变量
(3)允许把⾮const对象的地址赋给指向const对象的指针,不能使⽤指向const对象的指针修改指向对象,然⽽如果该指针指向的是⼀个⾮const对象,可以⽤其他⽅法修改其所指向的对象
const double pi = 3.14;
const double* cptrf = π//正确
double f = 3.14;//f是double类型(⾮const类型)
cptr = &f;//正确,允许将f的地址赋给cptrf
f = 1.68;//正确,允许修改f的值
*cptrf = 10.3;//错误不能通过引⽤cptr修改f的值
(4)实际编程过程中,指向const的指针常⽤作函数的形参,以此确保传递给函数的参数对象在函数中不能被修改
void fun(const int* p)
{
...
}
int main()
{
int a;
fun(&a);
}
指针作为函数的形参,在主函数中,我们定义整形变量a然后将a的地址传递给了⼦函数,对于⼦函数来说,
他的形参就是⽤const修饰过的整型变量p指向主函数⾥a这个变量
这样的⼀系列操作就使得我们不能在⼦函数中通过p间接引⽤a 来改变a 的值,因为a 是⽤const修饰过的作就使得我们不能在⼦函数中通过p间接引⽤a 来改变a 的值,
7、const指针
⼀个指针变量可以是只读的,成为const指针它的定义形式:
指针类型* const 指针变量, ...;
注意观察将const放在变量名的前⾯,与上⾯的形式不同,
int a = 10, b = 20;
int* const pc = &a;//pc是const指针
pc = &b;//错误pc 是只读的
pc = pc;//错误pc是只读的
pc++;//错误pc是只读的
*pc = 100;//正确,a被修改
pc是指向int型对象的const指针
不能使pc再被赋值指向其他对象,任何企图给const指针赋值的操作都会导致编译错误
但是可以通过pc间接引⽤修改该对象的值
⼆、⼀维数组与指针
1、数组的⾸地址
数组有若⼲个元素组成,每个元素都有相应的地址,通过取地址运算符&可以得到每个元素的地址,数组的地址就是这⼀整块存储空间中,
第⼀个元素的地址即a[0]
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论