理解指针

从来没有好好理解过指针,这次花了点时间大概看了一下,把学到的一些东西记录下。

1.指针

1.1 初步理解指针

        指针是C语言的一个重要特征,它是一个特殊的变量,里面存储的数值被解释成为内存里的一个地址

        在讲指针之前,先搞清楚两个运算符:&和*

&是取地址符,&a的运算结果是一个指针,指针的类型是a的类型加个*,指针所指向的类型是a的类型,指针所指向的地址就是a的地址。

*用于指针的间接引用,运算结果随着指针指向的内容而改变,比如可能是一个数值,也有可能是一个地址。*p的类型是p所指向的类型,它占用的地址是p所指向的地址。

通过一个例子来解释下&,*

在讲数组的时候经常会讲到a和&a的区别,比如给你一个数组 int a[4]。

a指向的是数组首元素的首地址,即a=&a[0], 所占内存区长度为sizeof(int),a+1是数组下一个元素的首地址

&a是数组的首地址,所占内存区长度为4*sizeof(int),&a+1是下一个数组的首地址

a和&a

        要获得数组中保存的内容时,可以使用*运算符。例如要获得a[1]的内容,即*(a+1)。

        言归正传:

        搞清一个指针,我们从四个方面的内容来认识指针:

  • 指针的类型
  • 指针所指向的类型
  • 指针的值(是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。)
  • 指针本身所占据的内存区(从指针所代表的的那个内存地址开始,长度为指针所指向的类型长度的一片内存区,注意指针未初始化时,内存区是不存在的)

        通过以下几个例子来说明:

  指针类型 指针所指向的类型
int *p int * int
int *p[5]  int *[5] int [5]
int (*p)[5] int (*)[5] int ()[5]
int **p int ** int *
int *(*p)[5] int *(*)[5] int *()[5]

1.int *p  p是一个指向int类型的指针

int a[5];

int *p=a;

p1

指针的值为数组元素首地址

指针本身所占据的内存区为sizeof(int)

2.int *p[5]  p是一个指向数组首元素的指针,该数组含有5个int *类型的元素

int *p[5];

P2

指针的值为数组元素首地址

指针本身所占据的内存区为sizeof(int *)

数组中的每个元素可以看成是指向int类型的指针,此时对数组中元素的赋值可如下:

int  b=0;

p[0]=&b;

3.int (*p)[5]  p是一个数组的指针,该数组含有5个int类型的元素,p是一个行指针

int a[5];

int (*p)[5]=&a;

p3

指针的值为数组首地址

指针本身所占据的内存区为sizeof(int)*5

4.int **p   p是一个指向int类型指针的指针

int *a[5];

int **p=a;

P2

指针的值为数组元素首地址

指针本身所占据的内存区为sizeof(int *)

5.int *(*p)[5]  p是一个指向数组的指针,该数组含有5个int *类型的元素,p是一个行指针

int *a[5];

int *(*p)[5]=&a;

p5

指针的值为数组首地址

指针本身所占据的内存区为sizeof(int *)*5

所以,当碰到指针时,先想想以上四个方面(指针的类型,指针所指向的类型....),这样可以更好的理解指针。

1.2 const修饰符和typedef

const修饰符:

在给一个变量加上const修饰符的同时,通常需要对它进行初始化,因为之后将不能对该变量进行修改。

const int n=5;

n=6;将会报错,因为n是const型的,不允许被修改。

const和指针在一起使用时,要注意是给谁加上了const。例如:

(1) const int *p;  p是一个指向const int类型的指针,因此*p的值不可以改变。

const int a=5;

const int *p=&a;

*p=6;// 错误error: assignment of read-only location

(2) int * const p; p是一个指向int类型const指针,因此p不可以改变,但是*p可以改变。

int a=5,b=6;

int * const p=&a;

p=&b;//错误 error: assignment of read-only variable `p'

(3) const int * const p; p是一个指向const int 类型的const 指针。p和*p的值都不能被改变。

const int a=5,b=6;

const int * const p=&a;

*p=6;//错误error: assignment of read-only location

p=&b;// 错误error: assignment of read-only variable `p'

(4) const int **p; p是一个指向const int类型的指针的指针

const int a=5;

const int *q=&a;

const int **p=&q;

*q=6;//错误,a的值不能被改变

(5) int * const * p; p是一个指向int类型的const指针的指针,*p的值不能改变,p的值可以改变

int a=5,b=6;

int *q=&a,*r=&b;

int * const *p=&q;

p=&r;//正确,p可以被改变

*p=r;//错误,*p不能被改变

*q=6;//正确,a,b的值可以被改变

(6) const int * const * p; p是一个指向const int类型的const指针的指针,*p的值不能变

const int a=5,b=6;

const int *q=&a,*r=&b;

const int * const *p=&q;

 p=&r;//正确,p可以被改变

*p=r;//错误

*q=6;//错误,a,b的值也是不能改变的

(7) int ** const p; p是一个指向int类型的指针的const指针,p的值不能改变

int a=5,b=6;

int *q=&a,*r=&b;

int ** const p=&q;

p=&r;//错误,p的值不能改变

*p=r;//正确,*p可以被改变

*q=6;//正确,a,b的值可以被改变

(8) const int ** const p; p是一个指向const int类型的指针的const指针,p不能被改变

const int a=5,b=6;

const int *q=&a,*r=&b;

const int * *const p=&q;

p=&r;//错误,p不能改变

*p=r;//正确,*p可以被改变

*q=6;//错误,a,b的值也不能被改变

(9) int * const * const p; p是一个指向int类型的const指针的const指针

int a=5,b=6;

int *q=&a,*r=&b;

int * const * const p=&q;

p=&r;//错误,p的值不能被改变

*p=r;//错误,*p的值也不能被改变

*q=5;//正确,a,b可以被改变

(10) const int * const * const p; p是一个指向const int 类型的const指针的const指针

const int a=5,b=6;

const int *q=&a,*r=&b;

const int * const * const p=&q;

p=&r;//错误,p不能被改变

*p=r;//错误,*p不能被改变

*q=5;//错误,a,b的值也不能被改变

typedef的用法:

typedef给出了一种方式来克服 “ * 只使用与变量而不适用与类型”的弊端。例如:

typedef int * PINT;

PINT p,q;

p,q都声明为指向int类型的指针。

typedef还可以定义返回值类型。比如:

typedef  int * b(),定义b的类型为int * ();

typedef int * b();

int * f()
{
int a=5;
int *p=&a;
return p;
}

int main()
{
b *c;
c=f;
cout<<c<<endl;
return 0;
}

对于复杂的声明,例如 int * (* (*fp1) (int) ) [10];我们可以使用“右左法则”来理解这个声明。这个法则运用如下:从最内部的括号开始阅读声明,向右看,然后向左看。当你碰到一个括号时就调转阅读的方向。括号内的所有内容都分析完毕就跳出括号的范围。这样继续,直到整个声明都被分析完毕。

以int * (* (*fp1) (int) ) [10];为例,阅读步骤:

(1) 从变量名开始——fp1

(2)  往右看,声明也没有了,碰到了),因此往左看,碰到一个*——一个指针

(3) 跳出括号,碰到了(int)——一个带一个int参数的函数

(4) 向左看,发现一个*——(函数)返回一个指针

(5) 跳出括号,向右看,碰到[10]——一个10元素的数组

(6) 向左看,发现一个*——指针

(7) 向左看,发现int——int类型

总结:fp1被声明成为一个函数的指针,该函数返回指向指针数组的指针.

2.函数指针

函数指针可能是最容易引起理解上的困惑的声明。先来看一个例子:

int (*p)(int);

这里p被声明为一个函数指针,该函数带一个int类型的参数,并且返回值为int类型。

直接定义一个函数指针显得冗长,我们可以用typedef进行简化。

typedef  int(*func)(int);

此时,func定义为一个函数指针类型,可以用这个类型来定义变量,例如 func p;

下面举几个例子来了解一下到底什么是函数指针

(1) int * (*(*fp1)(int))[10];

对于fp1:

我们从里向外一点一点分析,首先(*fp1)(int),这说明fp1是一个函数指针,它有一个int类型的参数;然后我们来找这个函数指针类型的返回值,注意到*(*fp1)(int),所以我们可以断定它的返回值是一个指针,指针指向什么呢?

我们可以看到最外层剩余的部分是int * [10],因此这个函数的返回值是一个指针,这个指针指向一个包含十个int *类型数据的数组。

综上:fp1是一个函数指针,它所指向的函数有一个int类型的参数,并且这个函数的返回值是一个指针,这个指针指向一个包含10个int *元素的数组。

fp1cpp

(2) int (*(*fp2)(int, int, float))(int);

对于fp2

就不再赘述了。fp2是一个函数指针,它所指向的函数有三个参数,参数类型分别为int,int,float;它的返回值是一个函数指针,这个函数指针所指向的函数具有一个int类型的参数,且返回类型为int。

fp2cpp

(3) double (*(*(*fp3)())[10])();

对于fp3

fp3被定义为一个函数指针类型,这种函数指针所指向的函数的参数为空;它的返回值是一个指针,这个指针指向一个包含10元素的函数指针数组,这些函数指针所指向的函数的参数为空,返回值为double。

fp3cpp

(4) int (*(*fp4)[10])(); 

对于fp4

fp4是一个指针,这个指针指向一个包含10元素的函数指针数组,这些函数指针所指向的函数的参数为空,返回值为int。

fp4cpp

本文参考的一些资料:

如何理解c和c ++的复杂类型声明   (其中有些错误的地方,文中已修改)

函数指针

c语言指针用法难点

二维指针,二维数组,以及指向数组的指针的相关问题

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值