指针,是学好数据结构的关键。而大多数同学只掌握了指针的基本概念,这周,我们就来挖掘一下指针更深层次的知识。业精于勤荒于嬉,不懒惰,不浪费,积极进取,目光长远。
前言
- 什么是整型指针?
- 什么是二级指针?
- 什么是数组指针?
- 什么是指针数组?
- 他们的用法有何不同?
本节,我们就来揭开这些谜题。
提示:以下是本篇文章正文内容,下面案例可供参考
1. 指针基本概念
-
指针是一个变量,储存的是所指变量的地址,也能通过指针解引用来找到相应地址的内容
-
指针存的是地址,地址所占内存是4个字节(x64环境)或8个字节(x86环境)。如下代码,pa,pc,pf都占用四个字节。
3. 既然指针的大小都是4/8,那么为什么会有 int *pt,
char* pc 等等类型的指针呢?int * 为整型指针类型,
说明指针指向的是int类型的数据, char* 同理
- 指针类型作用2:不同类型的指针,加1,跳过的字节数是不同的。
int * 类型的指针加一,跳四个字节;
char * 类型的指针加一,跳一个字节;
其他类型同理,与所指的数据类型所占的字节数相同。
如下图所示,int *指针par+1,跳四个字节,指向数组的第二
个元素。char*类型的指针pc1加一,跳一个字节,指向数组的
第二个元素。
2. 字符指针
字符指针是指向字符的指针。这里,我们通过题目来理解。
- 题目1,求打印结果
const char * p = "abcdef";
printf ("%s\n", p);
大家思考一下,上面的代码结果如何呢?
结果是将abcdef打印出来了,为什么呢?
这里,指针p存的是字符串首元素也就是’a’的地址,在打印字符串时,可通过由首地址开始,遇’\0’截止,将字符串打印出来。
由于"abcdef"为常量字符串,加const修饰,防止警告。
2. 题目2.求打印结果
const char* p1 = "abcdef";
const char* p2 = "abcdef";
char arr1[] = "abcdef";
char arr2[] = "abcdef";
if (p1 == p2)
printf("p1==p2\n");
else
printf("p1!=p2\n");
if (arr1 == arr2)
printf("arr1 == arr2\n");
else
printf("arr1 != arr2\n");
结果如下,你做对了没?
p1==p2
arr1 != arr2
为什么呢?
由于,"abcdef"是常量字符串,放在只读区域,只存一份。当p2也指向字符串时,程序不会再重新定义一个字符串,p1 与 p2 指向的是同一块区域,所以p1 == p2.
由于 arr1与arr2是两个独立开辟的空间,虽然内容相同,但起止地址不同,所以两个数组不相等。
3. 指针数组
指针数组,本质上是一个数组,数组的每个元素都是指针类型。如下代码
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
int* parr[3] = { arr1, arr2, arr3 };
int * parr[3] ,parr是一个数组,数组类型是int * [3],
开辟了三个空间, 每一个元素都是整型指针。用数组名代替
了首元素地址,存到指针数组里。
想用这个指针数组把元素打印出来,该怎么办呢?
我们知道,数组名是首元素地址,那么每个元素的首地址都有了,打印出来就不难了。
在指针数组parr 里,parr[1]也就是a数组名,
就是 arr1数组的首地址,所以arr1[i]可以用
*(arr1+i)表示,也可以用*(parr[0]+i)表示,
同理,arr2[3]可以用*(parr[1]+i)表示,具体代码如下
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", *(parr[i] + j));
}
printf("\n");
}
4. 数组名的意义
一般情况下,数组名代表首元素的地址,但有两种特殊情况。
- sizeof,sizeof(数组名)代表求整个数组的内存大小。
int arr[10] = {0};
printf("%d", sizeof(a));
// 答案是40,十个int 型元素,10*4=40
- &数组名,取出的是整个数组的地址
int arr[7] = {1,2,3,4,5,6,7};
int *p = &a+1;
printf("%d", *(p-1));
上述代码输出结果是什么呢?
答案是7,为什么呢?
&a+1,意思是跳过整个数组,p指向的是数组后面的位置
输出的是*(p-1),即p向前挪动一个整型字节,指向了7,解引用指针,输出7。
5. 数组指针
数组指针,本质上是一个指针,指向一个数组。
int arr[10] = { 0 };
int (*p2)[10] = &arr;
p2 是指针,指向int【10】类型的数组。
6. 数组指针应用
数组指针主要应用于二维数组。由于二维数组的首元素是第一行的元素,是一个一维数组,所以,接收时,用指针时,需要数组指针接收。
如下代码,用数组名即首地址将二维数组传给函数,由于二维
数组首地址是第一行元素,是一个一维数组,所以,需要用数
组指针接收。p是数组指针,指向的元素类型是int [5],一个
有五个元素的一维数组。
p存的是第一行的地址,(p+i)就是第i+1行的地址,
*(p+i)就是第i+1行首元素的地址,*(p+i)+j 是
a[i][j]的地址,*(*(p+0)+j )就是a[i][j]的元素。
int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
print2(arr, 3, 5);
void print2(int (*p)[5], int r, int c)
{
int i = 0;
for (i = 0; i < r; i++)
{
int j = 0;
for (j = 0; j < c; j++)
{
printf("%d ", *(*(p + i) + j));
//printf("%d ", p[i][j]);
}
printf("\n");
}
}
7. 数组传参
7.1 二维数组传参
int a[3][5];
fun11(a);//传的是一维数组地址
fun2(a);
void fun1(int a[][5])//必须表明列数
void fun2(int (*arr)[5])//数组指针
用指针接收二维数组,int a[3][5]
数组名为首元素地址, 二维数组首元素为第一行,
是一个一维数组的地址
void fun(int (*p)[5],int r,int c)
打印用 printf("%d"*(*(p+i)+j)));
*(p+i)相当于一行的数组名,p[i]
p,指向数组的指针,int (*)[5],p+1,跳过5个元素
或者 void fun(int a[][5],int r,int c)
注意,二维数组的行可以省略,列数不能省略
7.2 一维整型数组传参
int a[5]={0};
fun1(a);
fun2(a);
void fun1(int a[])
{}
void fun2(int *a)
{}
7.3 一维指针数组传参
一维指针数组,每个元素都是指针,传指针的首元素地址,用二级指针接收。二级指针,存放的是一级指针的地址。
int *arr[5]={0};
fun1(a);
void fun(int *arr[])
{}
void fun(int **a)//二级指针可存放一级指针的地址
{}
int *arr[5]={0};
fun(a);
void fun(int *arr[])
{}
void fun(int **a)//二级指针可存放一级指针的地址
{}
总结
指针的知识点能挖掘的地方还有很多,需要反复理解。为了美好的未来,我们加油吧~