在C++数组的创建及使用中,简单介绍了数组中指针的使用,但是还有一些其他问题,这里主要展开一些关于二维数组指针、二级指针以及实现动态二维数组的内存连续。
1、任何指针变量被创建时不会自动成为NULL指针,它的默认值是随机的,它会乱指一气。因此如果在短期不用的话,就先把指针赋值为NULL,例如:int * p = null;
2、以内存需求的角度来看,不同类型的指针没有什么不同,其所占空间与操作系统有关(32位操作系统指针占用4字节,64位系统指针占用8字节),不同之处在于其所寻址出来的对象(object)不同。也就是说指针类型会教编译器如何解析某个特定地址中内存内容机器大小。
3、一级指针、二级指针的区别
int *p表示的是一级指针,表示p所指向的地址里面存放的是一个int类型的值。
int **p表示的是二级指针,表示p所指向的地址里面存放的是一个指向int类型的指针。
一级指针存放变量的地址,指向的值是变量的内容。如int* p1={1,2,3}, p1=数组的首地址,*p1=数组的第一个值;
二级指针存放一级指针的地址,指向一级指针。如int*p1 ={1,2,3}, int**p2=&p,p2=指针p1的首地址,*p2=数组的首地址,**p2=数组第一个值1。再比如有一个变量x,其值为57,p1指向了该变量,p2又指向了p1变量,可简单用如下图示表示:
即
int x = 57;
int * p1 = &a;
int ** p2 = &p1
4、再谈二维数组中 *int *p[n]和int (p)[n]的区别
int *p[n] 分析:(指针数组)
[]的优先级大于*,所以,int *p[n] 就等价于int (p[n]),这样就清晰多了,再进化一下就是 (int *)(p[n]) ,这样就完整了,显然,p是一个数组,其数据类型为(int * ),即以n个整型地址(int)为元素,数组名为p;
**int (*p)[n]**分析:(数组指针)
()和[]具有相同的优先级,但是符号是从左向右,所以可以写成(int)((*p)[n]),可以认为p是指向n个元素(数组)的指针,数组内有n个整型元素,根据数组的知识可知,*p就是数组首元素的地址,所以p在这里也是双重指针,这里可以将p认为是二重数组的数组名;
更多不同可参考 https://blog.csdn.net/wuyuzun/article/details/82778553
5、直接抄过来的
int i; i是整型。
int *i; i是整型指针。
int **i; i是整型指针的指针。
int *i[5]; i是含有5个元素的整形指针数组。
int (*i)[5]; i是指向5个元素的指针。
int *i(); i是返回值为整型指针的函数。指针函数
int(*i)(); i是返回整型的函数指针。函数指针
int *(*i)(); i是函数指针,函数返回整型指针。
int *(*i[])(); i是函数指针数组,函数返回整型指针。
int *((*i)())[5] i是函数指针,函数返回指向5个整型指针原则的指针。
6、关于二维数组的指针
对于int **p来说,p所指的对象是指针,那么++p就是让p的值增一个指针的大小,虽然指针所指对像可大可小,但是指针自己的大小却是一定的,目前的系统上一般是4字节(或8字节)。也就是说,对char *p和long *q来说,p和q本身占内存容量是一样大的(虽然char和long占内存不一样大)。
一个典型的错例:
int a[2][3];
int **p = a;//错误
这个代码的错误之处在于a是一个数组,它的成员也是数组,所以a叫做“数组的数组”——C++中严格说来没有二维数组。那么,要用一个指针来记录a,就要用一个能表示“数组的数组”的指针,以下代码是正确的:
int (*p)[3] = a;//正确
只有这样才能保证++p使p指向a的下一对像(该对像是一个数组)。
顺便提一句:不要写成“int *p[3];”
7、new一个动态二维数组并实现内存连续 (这里参考了https://blog.csdn.net/u013534071/article/details/50877336)
#include
using namespace std;
int main()
{
int **p;//新建一个二级指针p,即指向指针的指针
p = new int*[3];//开辟3个【用来存储指针(即地址)】的空间,{ p[0] p[1] p[2] },其中p→p[0]
*p = new int[12];//开辟12个【用来存储整形】的空间,{ ⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ }其中p[0]→⑴
p[1] = p[0] + 4;//让p[1]→⑸
p[2] = p[1] + 4;//让p[1]→⑼
int i, j, k = 1;
int *q;//新建一个指针q,目的在于让q移动而不是让p移动,p必须保证位置不动,方便后面释放内存
q = p[0];//指针q赋值为p[0]的内容,即q→⑴
for (i = 0; i<12; ++i){//由于内存是连续的,通过q++来移动指针进行【从1到12】的赋值
*q = k;
q++;
k++;
}
for (i = 0; i<3; ++i){
for (j = 0; j<4; ++j){
cout << p[i][j] << endl;//p[i][j]=(p[i])[j],把p[i]看成一个整体,“[]”可以看成取值符号“*”
}//即p[0][0]=**p,以此类推
}
delete[] p[0];//或者delete[] *p
delete[] p;//先delete p[0]再delete p
return 0;
}
以上通过int**p新建了一个二级指针,并开辟了三个存储指针的空间,即让p指向了一个指针数组,而此时p的值,即该指针数组首元素的地址,通过*p得到该首元素的值(int*),即另一个地址,然后通过*p= new int[12],在首元素值所指向的空间new了一个3*4的空间,如下图所示:(为了表示二维,将int[12]写成int[3][4],其实在该空间中内存上是连续的)
然后为了保证地址连续,并没有在每一个指针数组都new一个空间,而是让该指针数组中的地址指向了刚才new出的空间,即让p[1]、p[2]指向刚才new出的空间的某处,以实现整个地址的连续:如下图所示: