博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
有关指针和数组的理解
阅读量:4579 次
发布时间:2019-06-09

本文共 3118 字,大约阅读时间需要 10 分钟。

下面这四个变量声明代表四个不同含义:

type **a; 指向指针的指针

type a[n1][n2]; 二维数组
type *a[n]; 指针数组(即数组元素是指针)
type (*a)[n]; 指向数组的指针

1.type **a 代表是指向指针的指针,其实声明类似“type **...*a”形式的都合法,代表“指向指针的指针的...指针的指针”。

例程:
 int a = 9;
 int * b = &a;
 int **c = &b;
 int ***d = &c;
 int ****e = &d;
 int *****f = &e;
 printf("value of a is %d\n",a);
 printf("address of a is %x\n",b);
 printf("address of b is %x\n",c);
 printf("address of c is %x\n",d);
 printf("address of d is %x\n",e);
 printf("address of e is %x\n",&e);
输出:
 value of a is 9
 address of a is 12ff7c
 address of b is 12ff78
 address of c is 12ff74
 address of d is 12ff70
 address of e is 12ff6c
每一个指针的值都是上一个变量的地址,同时也能验证了栈地址空间增加的方向是从高到低的。
另外,这种类型的指针只能初始化一维数组,可以初始化形式如 :
 type *a = (type *)(malloc(sizeof(type) * n));  //创建一维数组,元素是type型
 type **a = (type **)(malloc(sizeof(type *) * n));  //创建一维数组,元素是type *型
其实,函数malloc(n)只关注参数n的值,n代表申请的字节数,不论n有多大,malloc一概返回void *指针,
除非堆空间内存不够时返回NULL。前面再加上强制类型转换,使返回值变成想要的类型(只要它和void * 可以强制转换)。
就算你写成 int a = (int)(malloc(1000));
单看这一条语句编译器不会报错,没有类型宽度检查。
申请一块内存想让它以什么方式被编译器认可,编译器就以什么方式认可,至于以后会不会出错人家不管。
又由于,编译器对"type **...*a"这种方式声明的变量最多按照一维数组处理,即:
 type * a = (type *)(malloc(...)),a[m]是type型
 type **a = (type **)(malloc(...)), a[m]是type *型
 type ***a = (type **)(malloc(...)), a[m]是type **型
 ...
以上语句后,出现“a[m][n]”会出错!!
所以只能初始化一维数组,不管指针指向多大空间,都安一维数组处理。

2.a[n1][n2]代表一个二维数组,数组可以使任意维度,声明类似“a[n1][n2]...[nm]”都合法,代表m维数组。

以整型二维数组为例:int a[n1][n2]初始化方法有两种,
 静态初始化:a[n1][n2] = {
   {1,2,3,..., n2},
   {2, 2, 3,..., n2},
   ...
   {n1, 2, 3,..., n2} 
      }; 
 动态初始化:(后面再提);
静态初始化后,数组所有元素被分配在程序栈空间内。有以下等式成立:
&a = a = &a[0] = a[0] = &a[0][0];
&a代表叫做a这个名字的变量的地址;a代表数组a首元素的地址;
由于数组a是在栈上分配空间的,所以数组名a这个变量和数组本身分配在一个地址上,所以&a = a;
a[0]是数组首元素,所以&a[0] = a;
a[0]代表数组a[0]的首元素地址,a[0][0]是a[0]的首元素,所以&a[0][0] = a[0];
其实往简单了想,a,a[0],a[0][0]都是二位数组的头部,所以起始地址是一样的。
所以静态初始化的二维数组:&a = a = &a[0] = a[0] = &a[0][0],永远成立。
(对于&a, a, &a[0], a[0], &a[0][0]相等与否还有另外两种情况,后面再提)。

3.type *a[n] 代表一个数组,数组的元素是指针,类似于"type **...*a[n]"的声明都是合法的,代表数组的元素是n维指针。

先明确这里a是一个数组,语句 int * a[n]执行后,编译器在栈区开辟一个空间,保存n个指针值。
对于这样的语句  int * a[10] = (int *[10])(malloc(...));(或int * a[10] = new int*[10];)
是编译错误的,因为不能把一个void * 型转化成数组型,从另一个角度考虑,数组可以看做指针常量,不能给一个常量赋值。
如果一定要在堆区建立指针数组,可以使用指针的指针:
 int **a = (int **)(malloc(...));
也可以使用指针的指针:
 (后面再提)。
上面说到,二维数组的动态初始化,可以用指针数组完成。
 int *a[10];
 for(int i = 0; i < 10; ++i){
  a[i] = (int *)(malloc(sizeof(int) * 10))
  或
  a[i] = new int[10];
 }
这样就完成了动态初始化,创建了一个10 * 10的数组,可以用a[n][m]访问具体元素。
现在分析一下这种情况下&a, a, &a[0], a[0], &a[0][0]的关系:
数组a在栈区建立,所以 &a = a = &a[0],所有a[i]指向的数组在堆区建立,所以a[0] = &a[0][0].
即&a = a = &a[0] != a[0] = &a[0][0].
另外a[i]之间的地址,也不一定满足a[i + 1] - a[i] = 4 * 10,因为每一个a[i]都是分别创建空间。

4.type (*a)[n],代表指向数组的指针。“int (*a)[n1][n2]...[nm]”的声明都是合法的,表示指向m维数组的指针。

其实,int (*a)[n] 和 int b[n][...],这里a和b比较类似。a是指针名,b是数组名。
区别是b是常量,值固定,a是变量,值可变,可以做赋值运算,例如 a = b;
也可以动态初始化二维数组:
 int (*a)[10] = (int (*)[10])(malloc(4 * 10)) 或 int (*a)[10] = new int[...][10];
再来分析一下&a, a, &a[0], a[0], &a[0][0]的关系。
由于变量a在栈区建立,而二维数组其他部分在堆区建立,所以有
&a != a = &a[0] = a[0] = &a[0][0],而且相邻a[i]之间的差值一定为 4 * 10。
对于上面提到的,在堆区分配指针数组问题,可以这样解决:
 int *(*a)[10] = (int *(*)[10])(malloc(10 * 4));

  

^_^

 

转载于:https://www.cnblogs.com/baiyan/archive/2011/01/11/1932934.html

你可能感兴趣的文章