指针和数组小记

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]&ap的值均为0xbfffef7c,但是要注意它们的含义:&a[0]pa是a[0]的地址,&ap则是整个数组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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值