前言
由于指针与数组的相似性,常常在使用中令人混淆。
笔者在此记录下使用指针时遇到的困难以及解决方法。解决问题时往往离不开数组,所以称为指针数组杂谈。
一、多维指针与多维数组的区别
我们先讨论常规的多维指针而不考虑多维数组作为函数参数退化成多维指针的地址的情况。
以三维指针为例,三维指针指向的一定是二维指针,也就是三维指针储存二维指针的地址。
int a=100;
int*pa=&a;
int**ppa=&pa;
int***pppa=&ppa;
此时的多维指针一层层解引用就相当于,不断退化成指向的元素。如三维指针解引用一次之后变成了它原本指向的二维指针。
那如果只有一个三维指针,想让它指向一个非指针变量怎么办呢?
int a=100;
int***pppa=(int***)malloc(sizeof(int**));//三维指针指向一个放二维指针的空间
*pppa=(int**)malloc(sizeof(int*));//二维指针指向一个放一维指针的空间
***pppa=a;//一维指针解引用之后是a,即一维指针指向放int的空间a
最后一行等同于
**pppa=&a;
多维数组在声明的时候已经划分好了一整块的内存空间,不需要手动分配内存。
在使用数组下标访问的时候,实际上是编译器根据数组划分维度的大小自动计算应该从数组头跳到哪里访问元素。
传参时只有一维数组和一维指针可以混用。
二维数组不能转化成二维指针
传入参数时,二维数组不能转化成二维指针,不然计算机无法自动识别跳位。
二维数组:
int ops[2][2]={
{2,2},{3,3}};
//op[1][1]相当于*(op+1*2+1),需要注意的是对与指针的加法不是单纯地址值加一,而是跳到指针所指的下一个位置
二维指针:
int maxCount(int m, int n, int** ops, int opsSize, int* opsColSize)
int op[2][2]={
{2,2},{3,3}};
int* p_ops[]={op[0],op[1]};
此时函数内对于ops1的调用如下
ops[1][1]代表先获取传入的p_ops的第1位指针int* p=op[1];
对于这个指针p,再次解引用得到p[1]
从这里可以看出,指针和数组在一个维度一个维度退化时,它们的调用可以等价。
对于一维指针,用指针形式调用和用数组形式调用都一样。
但是二维指针和二维数组的调用并不一样,因为二维数组传参退化后变成int (*)[4]
,指向int [4]的指针。二维数组本质上可以充当一维指针,原因是二维数组的数组名代表的地址储存的是int值。
然而,如果把二维数组强行解释成二维指针,那么二维数组和二维指针指向的地址是一样的。
但是地址上面储存的是一个int值而不是int*值。此时二维指针解引用之后错误把int值认为是int*值,无法再次解引用,程序崩溃。
二、指针数组与数组指针的概念
int *p1[500];//第一维被分配静态空间,第二维没有分配空间
int (*p2)[5];//第二维有,第一维没有
int(*p3)[100][100];//指向int [100][100]类型的指针 第一维没有分配空间
int p4[][100][100]; //传参时允许的形式
int* p1[3]是一个储存一维指针的数组
int(*p3)[3]是一个指向大小为3的一维数组的指针
三、函数指针
函数指针主要用于在传递参数时,传递函数的指针,从而实现需要调用其他函数的函数自身的封装。
例如,qsort就是一个需要调用compare函数的函数,但是如果直接调用本地的compare,就无法实现qsort的封装,因为每次都要在qsort之外写一个compare函数,
通过传入函数指针的方式,可以让使用者知道,qsort需要调用一个compare函数,并且可以自定义排序的方式。
四、数组大小之“堆”、“栈”、“数据段”、“BSS”
栈
在日常编写代码过程中,我们会发现如下代码会产生错误
int main(){
int m[1e8+1];
return 0;
}
这是因为储存局部变量的位置在“栈”里面,当函数生命周期结束,栈就会清除局部变量的内存。而栈的内存较小,数组开的过大就会报错。
实际上,栈是一种数据结构,后入先出结构,不过在这里不是我们讨论的重点。我们只需要知道计算机使用栈来储存局部变量即可。
栈的大小一般在1-8MB。如Windows的64位系统就默认栈的大小为4MB。
上述例子中,数组m的大小为4B*1e8,约为400MB,远超出栈的大小,显然不行。
那么如何解决呢?解决方法有二。
局部变量法——堆
全局变量法——数据段/BBS
堆
通过malloc函数圈出的内存就储存在堆里面
有点像找空白的地方,然后在上面堆东西,每个堆相互独立。
具体代码实现如下
long long int *dp=(long long int*)malloc((time+1)*sizeof(long long int));//容量为i的背包能装下的最大价值货物
memset(dp,0,(time+1)*sizeof(long long int));
malloc是包含于stdlib.h的函数,memset是string.h里面的函数
void* malloc(size_t);
memset(void*,赋值,size_t);
记得malloc动态分配的内存要free
堆的大小取决于硬件,一般是GB级别的
数据段/BSS
数据段和BSS是储存全局变量的地方,如果把数组开在全局变量,有初始化的数组会被放到数据段,没有初始化的数组会被初始化为0并被放到BSS。
两者大小都是GB级别