(指针)——图文并茂形象理解指针(一级,二级,指针与一维数组,指针与二维数组,指针与函数)
指针难,是因为指针针对不同的情况,有不同的用法,一头扎进去会比较混乱,搞不清楚状况,让人头疼,但是!!!理清关系之后,对于指针的运用会很清晰。
这里,我们通过对指针各种情况的说明来认识指针,看看指针真的有那么难吗?
1、什么是指针?
首先!指针是一种变量类型
其次!指针是一种存放地址的变量类型
这里我们可以看到,在内存单元当中,每一个内存单元都有一个地址(物理地址)
指针就是用来保存被指向的内存单元的地址(如图上的:0x100)
而指针 p 本身也有一个物理地址(如图上的:0x105)
只有先把最基本的搞清楚,再来看看这个概念
什么是一级指针,什么是二级指针?
1、一级指针:
1、这里再强调:
*p = *(&a )= *(0x100)= 6
这里的 * 表示 “取值运算符” ,也就是说:将里边的内容取出来
2、所谓一级指针就是指针 * 一次(取值一次),就得到了变量的内容,这样的指针就是一级指针。
2、二级指针:
这里看到是二级指针的表示,实际上不论是一级指针,二级指针,还是五级指针,八级指针,(当然一般二级就够用了),道理都只一样的,无非就是指针保存前级指针的地址(指针也是一个变量,也在内存中存放着,也有自己的物理地址)
接着我们看看指针与数组、函数等等的各种关系,一边推导一边深入理解指针
1、定义一个指针指向一维数组的 [ 0 ] 号元素(首元素)( A[5] )
这里可以看到:
当指针指向一维数组的第一个元素时, p = &A[0] = A(数组名)
A—>数组名----->首地址 和 &A[0]相等
补充个知识:p[n] = *(p+n)
因此:
对于第一个元素 A[0]来说
p = &A[0] = A
*p = *(&A[0]) = p[0] = *(A)= A[0]
(这他娘的看着复杂,其实很简单,就是一个加 * 取值的过程,这些都是等价的)
对于第二个元素 A[1] 来说
p+1 = &A[0] + 1 = &A[1] = A+1
*(p+1) = *(&A[0] + 1) = *(&A[1]) = *(A+1) = A[1] = p[1]
对于第三个元素 A[2] 来说
p+2 = &A[0] + 2 = &A[2] = A+2
*(p+2) = *(&A[0] + 2) = *(&A[2]) = *(A+2) = A[2] = p[2]
以此类推,对一维数组的每一个元素的遍历都可以这样利用指针来进性表示、访问、操作。
2、定义一个指针指向一维数组整体( A[5] )
这里注意:此时的指针是指向 A[5] 这个数组整体的
像一个渔网一样,网的范围是包住了这个数组整体
因此定义这样的指针:
数据类型:int [ 5 ]
指针:(*p)—>这里的p就是指针名,*号仍然是标志作用,告诉程序 变量p是一个指针
按理说:指针定义应该是这样的:
int [5] (*p)
前边是类型:int [ 5 ]
后边是指针名和 *号:(*p)
但是为了表达起来比较直观,比较好看
所以一个指向数组整体的指针就是这样的:
int (*p)[ 5 ] = &A
注意
指针的定义,指向谁就取谁的地址,比如我们要指向一维数组的第一个元素,那么 p = &A[ 0 ]。只不过一维数组名(A)也表示数组首地址,因此 p = &A[ 0 ] = A 这样也可以(A和&A[ 0 ]等价)
这里我们指向的是一个数组整体,因此这个指针 p = &A
那么对于指针指向一维数组整体
对于数组的第一个元素:
p = &A
*p = *(&A) = A = &A[ 0 ]
**p = *( * (&A) ) = *(A) = *( &A[ 0 ] ) = A[ 0 ]
对于数组的第二个元素:
*p = *( &A ) = A = &A[ 0 ]
*p + 1 = &A[ 0 ] + 1 = &A[ 1 ] :这里就通过首地址加一得到第二个元素的地址
*( *p + 1)= *( &A[ 0 ] + 1 )= *( &A[ 1 ] ) = A[ 1 ]
对于数组的第三个元素:
还是同上边一样,这里直接用指针来表示
A[ 2 ] = *( *p + 2 )
3、定义指针指向二维数组中的首元素的第一个数据(这里数组为A[3][3])
注意:
A[ 0 ] 为二维数组的第一个元素(首元素)
A[ 1 ] 为二维数组的第二个元素
A[ 2 ] 为二维数组的第三个元素
这里:指针定义为指向二维数组 的首元素 的第一个数据
int *p = &A[0][0];
p = &A[0][0]
对于第一个数据 A[0][0]
p = &A[0][0]
*p = *( &A[0][0] ) = A[0][0]
对于二维数组的 首元素的 第三个数据 A[0][2]
p = &A[0][0]
p + 2 = &A[0][0] + 2 = &A[0][2]
*(p + 2) = *( &A[0][0] + 2 ) = A[0][2]
对于二维数组 的第二个元素的第一个数据 A[1][0]
p = &A[0][0]
p + 3 = &A[0][0] + 3 = &A[1][0]
*(p + 3) = *( &A[0][0] + 3 ) = *( &A[1][0] ) = A[1][0]
对于二维数组的第三个元素的第三个元素 A[2][2]
p = &A[0][0]
p + 8 = &A[0][0] + 8 = &A[2][2]
*(p + 8) = *( &A[0][0] + 8 ) = *( &A[2][2] ) = A[2][2]
这样的方式,通过定义指针指向二维数组的第一个元素(首元素)的第一个数据,来对二维数组进行遍历、访问、操作。
定义一个指针指向二维数组首元素整体
首先我们将二维数组 A[3][3] 分成行列式的形式,这样便于我们查看指针的移动状态。
定义:指针指向二维数组的首元素整体—>A[ 0 ]
int (*p)[ 3 ] = &A[ 0 ];
p = &A[ 0 ];
对于二维数组的首元素的第一个数据 A[0][0]
p = &A[ 0 ]
*p = *( &A[ 0 ] ) = A[ 0 ] = &A[ 0 ][ 0 ] (由于A[ 0 ]为二维数组的首元素,也代表第一个数据的首地址,所以 A[ 0 ] = &A[0][0])
*(*p) = *( &A[0][0] ) = A[0][0]
对于二维数组的第零个元素(首元素)的第二个数据 A[0][1]
p = &A[ 0 ]
*p = *( &A[ 0 ] ) = A[ 0 ] = &A[ 0 ][ 0 ]
*p + 1 = &A[ 0 ][ 0 ] + 1 = &A[0][1]
*( *p + 1 ) = *( &A[ 0 ][ 0 ] + 1 ) = *( &A[ 0 ][ 1 ] ) = A[ 0 ][ 1 ]
对于二维数组的第一个元素的第二个数据 A[ 1 ][ 1 ]
p = &A[ 0 ]
p + 1 = &A[ 0 ] + 1 = &A[ 1 ]
*(p + 1) = *( &A[ 1 ] ) = A[ 1 ] = &A[ 1 ][ 0 ]
*(p + 1) + 1 = &A[ 1 ][ 0 ] + 1
*( *(p + 1) + 1 ) = *( &A[ 1 ][ 1 ] ) = A[ 1 ][ 1 ]
对于二维数组的第二个元素的第三个数据 A[ 2 ][ 2 ]
p = &A[ 0 ]
p + 2 = &A[ 0 ] + 2 = &A[ 2 ]
*(p + 2) = *( &A[ 2 ] ) = A[ 2 ] = &A[ 2 ][ 0 ]
*(p + 2) + 2 = &A[ 2 ][ 0 ] + 2 = &A[ 2 ][ 2 ]
*( *(p + 2) + 2 ) = *( &A[ 2 ][ 2 ])
对于指针针与数组的情况就是这样,用指针先搞清楚它指向谁,怎样移动,移动多少,* 的运用不是乱加,并且指针的知识还有很多,不仅是与数组,还有与函数等等,指针的灵活性很高,是一个很方便的工具。
在C语言中 * 的使用有这几种:
1、乘法运算: * (乘法运算符号)
2、定义指针时:* (起标志作用,告诉程序这是一个指针)
3、配合指针变量:* (取值运算符—>将指针指向的空间的值取出来)
接下来,我们看看指针与函数
1、指针函数:
指针函数:
首先它是一个函数
怎样的函数?
一个返回值为指针类型的函数
什么意思呢?
void Func0 (int , int) :这是一个函数名为Func0的函数,返回值为空(void),函数的参数为(int, int)两个整型参数
int Func1 (int, int):这是一个函数名为Func1的函数,返回值为一个 int 整型数,函数参数为 (int, int)两个整型参数
int * Func2 (int, char):这是一个函数名为Func2的函数,返回值为一个指向int型变量的指针,函数参数为 (int, char)一个int型,一个char型。
2、函数指针
函数指针:
首先是一个指针
什么样的指针?
一个指向函数的指针
什么意思呢?
int Func (int, char)
所以要定义一个指针指向这个函数,那么这个指针的数据类型就是 int (int, char)
一般我们这样定义一个函数指针:
typedef int (*FUNC)(int, int)
这样我们就可以用 FUNC 来定义一个函数指针了
当我们需要用到该函数时,直接调用指针即可,就相当于在调用函数,会比较方便。
这里的FUNC只是一个名字,随便起一个其他名字都可以,但一般起名称无论是函数名、数组名、变量名,最好做到见名知意。
指针数组
指针数组:
首先是一个数组
什么样的数组?
一个数组元素全都是指针的数组
char *Arr[ 3 ] = { " hello " , " world " , " abcd " };
这个数组有三个元素:
第一个元素表示 hello 这个字符串的首地址
第二个元素表示 world 这个字符串的首地址
第三个元素表示 abcd 这个字符串的首地址
printf ( " %s ", Arr[ 2 ] );
就会输出 abcd 这个字符串