DSAA in C 看到hash table的建立,对Figure5.8的程序中二级指针、一级指针和数组下标的用法有些疑惑,梳理一下指针和数组的关系。
写段代码看看:
int main()
{
int a0 = 0;
int a1 = 1;
int a[2]; //a is the name of an int array, also a int const pointer
a[0] = a0;
a[1] = a1;
int *pa; //pa is a pointer to int
pa = a; //since the name of an array is a const pointer, pointer = pointer, don't use "pa = &a"
int (*p)[2]; //p is a pointer to an int array
p = &a; //pointer to an int array = &(an int array)!
int **ppa; //ppa is a pointer to (a pointer to int)
ppa = &pa; //ppa = &(a pointer to int)
int *p1[2]; //p1 is a pointer array, elements in p1 are pointers
p1[0] = &a0;
p1[1] = &a1;
int **p2; //p2 is a pointer to pointer
p2 = p1; //use "p2 = p1" rather than "p2 = &p1"
return 0;
}
在GDB中看看几个值:&a
是0xbfffef7c,pa
也是0xbfffef7c,而a
是{0, 1}。
(gdb) p &a
$1 = (int (*)[2]) 0xbfffef7c
(gdb) p pa
$2 = (int *) 0xbfffef7c
(gdb) p a
$3 = {0, 1}
但是在程序中却写的是pa = a;
?若写成pa = &a
则编译告警,运行出错。如何理解呢?
参考数组名和数组名取地址的区别这篇文章以及《C和指针》:
首先引用《C和指针》p141中的理论:
在C中, 在几乎所有使用数组的表达式中,数组名的值是个指针常量,也就是数组第一个元素的地址。 它的类型取决于数组元素的类型: 如果它们是int类型,那么数组名的类型就是“指向int的常量指针“。
而在《C和指针》p142中说到,在以下两中场合下,数组名并不是用指针常量来表示,就是当数组名作为sizeof操作符和单目操作符&的操作数时。 sizeof返回整个数组的长度,而不是指向数组的指针的长度。 取一个数组名的地址所产生的是一个指向数组的指针,而不是一个指向某个指针常量的指针。
所以&a后返回的指针便是指向数组的指针,跟a(一个指向a[0]的指针)在指针的类型上是有区别的。
由于pa是一个int型指针,所以它可以幅值为a(一个指向a[0]的指针,a[0]是int),即pa = a
;但是&a
是一个指向数组的指针,因此不能pa = &a
。
那么&a
可以赋值给谁呢?当然是一个指向数组的指针,比如int (*p)[2];
,定义了一个指向数组的指针p(也称为数组指针),指向一个int型一维数组,这个数组的长度为2,然后就可以p = &a;
了。在GDB中看一下:
(gdb) p p
$4 = (int (*)[2]) 0xbfffef7c
...
(gdb) p &a[0]
$10 = (int *) 0xbfffef7c
发现pa
、&a[0]
、&a
和p
的值均为0xbfffef7c,但是要注意它们的含义:&a[0]
和pa
是a[0]的地址,&a
和p
则是整个数组a的地址,虽然地址相同,但含义不同。在K&R的5.3节中也有这样的说法:
因为数组名所代表的就是该数组最开始的一个元素的地址,所以赋值语句 pa = &a[0] 也可以写成下列形式:pa = a;
补充一下,虽然在含义上可以说a
和&a[0]
是相同的,但是&a
不能写成&(&a[0])
:
(gdb) p &(&a[0])
Attempt to take address of value not located in memory.
(gdb) p &a
$11 = (int (*)[2]) 0xbfffef7c
从下标运算的角度来看,K&R的5.3节中说:
对数组元素a[i]的引用也可以写成*(a+i)这种形式。
&a[i]和a+i的含义也是相同的。如果pa是个指针,那么在表达式中也可以在它的后面加下标。pa[i]与*(pa+i)是等价的。
所以可以看到,虽然上面说到&a[0]
和&a
的值相同(但含义不同),而对于指针pa来说,&pa
和&pa[0]
的值不同(含义也不同),&pa[0]
即&(*(pa+0))
即为pa
:
(gdb) p &a
$12 = (int (*)[2]) 0xbfffef7c
(gdb) p &a[0]
$13 = (int *) 0xbfffef7c
(gdb) p &pa
$14 = (int **) 0xbfffef6c
(gdb) p &pa[0]
$15 = (int *) 0xbfffef7c
(gdb) p pa
$16 = (int *) 0xbfffef7c