系列文章目录
指针进阶
指针与数组的 “ 爱恨情仇 ”——指针进阶(一)
前言
“指针”和“数组”这两个词,大家或许都已耳熟能详,数组又常被人称为是一种指针,前边我们也基本了解了数组与指针的关系,那么指针数组与数组指针又有是什么呢?它们又有着怎样“ 爱恨情仇 ”呢?接下来我们将会一一探讨。
数组与指针
在前边我们学习了初阶指针相关知识,主要为以下内容:
1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。
3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。
4. 指针的运算。
那么接下来我们将对指针进行深度解析。
数组与指针的区别
以字符指针为例:
字符指针的一般类型为:char*,常用的使用有以下两种:
1.通过指针修改变量的值
int main() { char ch = 'w'; char *pc = &ch; *pc = 'w'; return 0; }
2.通过指针找到字符串
int main() { char* pstr = "hello world."; printf("%s\n", pstr); return 0; }
这里可能会有很多人会误以为是将字符串存储到了指针pstr中,然而它的实质含义是将字符串首元素地址存放到指针pstr中,通过字符串首元素的地址,进而找到整个字符串。
我们来看一下以下代码,区别一下数组与指针。
#include <stdio.h> int main() { char str1[] = "hello world."; char str2[] = "hello world."; char *str3 = "hello world."; char *str4 = "hello world."; if(str1 ==str2) printf("str1 and str2 are same\n"); else printf("str1 and str2 are not same\n"); if(str3 ==str4) printf("str3 and str4 are same\n"); else printf("str3 and str4 are not same\n"); return 0; }
答案是什么呢?str1和str2不同,str3和str4相同。为什么呢?
str3和str4指向的是同一个常量字符串,C/C++会把常量字符串存储到单独的一个内存区域(代码区),不同的指针指向同一个字符串的时候,他们实际会指向同一块内存。
而用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。str1和str2是数组首元素的地址,所以所以str1和str2不同,str3和str4相同。
指针数组
什么是指针数组?
我们来类比一下,存放整形数据的数组被称为整形数组,存放字符串的数组被称为字符数组,那指针数组呢?
指针数组是一个存放指针的数组
基本类型:
int* arr1[10]; //整形指针的数组 char *arr2[10]; //一级字符指针的数组 char **arr3[5];//二级字符指针的数组
前边的类型表示所存储指针指向的类型。
例如:
int main() { int arr1[] = { 1,2,3,4,5 }; int arr2[] = { 2,4,6,8,0 }; int arr3[] = { 1,3,5,7,9 }; int* parr[] = { arr1,arr2,arr3 }; for (int i = 0; i < 3; i++) { for (int j = 0; j < 5; j++) { printf("%d ", *(parr[i] + j)); } printf("\n"); } return 0; }
arr1,arr2,arr3指的都是数组首元素地址,指针数组存放的就是地址,通过观察我们也可以发现,在打印元素时访问数组元素很像二维数组,而 *(parr[i] + j)不仅可以写成parr[i][j]二维数组的形式,还可以写成*( *(parr+i) + j)由此我们可以看出,*(parr+i)等价于parr[i],从这里也可以看出数组与指针有很大的相似之处,但它们确是两个不同的概念。
数组指针
前边我们知道指针数组的本质是数组,那数组指针的本质是什么呢?
数组指针本质上是指针,是指向数组的指针。
我们来看一下下面的代码:
int *p1[10]; int (*p2)[10];
哪个是数组指针,哪个又是指针数组?
它们有很多的相似之处,不同点就在于p2使用括号,使得p2优先与*结合。
int (*p)[10];
解释:p先和*结合,说明p是一个指针变量,[10]代表指向的是一个大小为10的数组,数组类型为int类型。
所以p2是一个指针,指向一个数组,叫数组指针。
这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合.
思考:
那指向一个整形指针数组(一维)的指针应该怎么写呢?
int* (*p)[10];
(*p)左边的类型就是指针p所指向的数组类型。
指针数组的使用
我们已经了解了数组指针基本概念,那要怎么使用呢?
例如以下代码:
#include <stdio.h> int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,0}; int (*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p return 0; }
数组指针存放的应该是数组的地址,但是我们很少这样去写代码。
数组指针一般很少与一维数组结合使用,因为基本没有意义,大多数的情况都是和二维及二维以上的数组搭配使用。
那我们要怎么用呢?
以下列代码为例:
我们来对比以下。
void print_arr1(int arr[3][5], int row, int col) { int i = 0; for (i = 0; i < row; i++) { int j = 0; for (j = 0; j < col; j++) { printf("%d ", arr[i][j]); } printf("\n"); } } void print_arr2(int(*arr)[5], int row, int col) { int i = 0; for (i = 0; i < row; i++) { int j = 0; for (j = 0; j < col; j++) { printf("%d ", arr[i][j]); } printf("\n"); } } int main() { int arr[3][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 }; print_arr1(arr, 3, 5); print_arr2(arr, 3, 5); return 0; }
两种方法很相似,但是前边我们知道,数组在传参时传的是数组首元素地址,那二维数组是怎么传参的呢?传过去的又是什么呢?
我们可以这样理解,arr[3][5]是一个存放一维数组的数组,一个二维数组里存放了3个一维数组,每个一维数组大小是5。
那么数组名arr,又表示首元素的地址
二维数组的首元素就是是二维数组的第一行(可以理解为第一个数组)
所以这里传递的arr,其实相当于第一行的地址(也就是一维数组的地址)
既然是地址那我们就可以用,数组指针来接收。
我们介绍了这么多,我来帮大家回忆和巩固以下:
int arr[5]; int *parr1[10]; int (*parr2)[10]; int (*parr3[10])[5];
第一个是数组,一个存放整形的数组。
第二个是一个指针数组,一个存放指针的数组,int* 是它所存放的数据类型。
第三个是一个数组指针,一个指向数组的指针,int [10],是数组的类型,*与parr2结合说明parr2是一个指针。
那第四个又是什么呢?
结合本文所介绍的内容来想一想。
先不看[10]int (*parr3)[5]是一个数组指针类型,那加上[10]又说明parr3是一个数组,所以,它是一个存放数组指针的数组。
总结
通过本文的介绍,相信大家已经对指针和数组有了更深入的了解。希望这篇博客能够对你的学习和工作有所帮助。