娓娓道来c指针 (3)指针和数组

                            (3)指针和数组

在c中指针和数组似乎有着千丝万缕的关系。其实它们不是一回事:指针是指针,数组是数组,两者不相同。

说它们有关系,不过是因为常见这样的代码:

int main()
{
	int array[] = {1,2,3,4,5};
	int n = sizeof(array) / sizeof(int);
	int *p = array;
	int i;
	for (i = 0; i < n; i++)
		printf("p[%d]...%d\n", i, p[i]);
	system("pause");
	return 0;
}
运行


在上面的代码中,指针和下标运算符的结合使用,给人一种指针和数组是一样的感觉。

本质是:数组名是一个指向数组起始元素的常量指针。这也是数组和指针的唯一联系!

之所以可以使用 p[i] 来访问数组中的元素,是因为在编译器中 p[i] 被解释为 *(p+i),这仍然是指针的功能。对编译器而言,用p[i]表达*(p+i)的含义是没有意义的,它只是为了让人看着舒服,用着方便。这是语法糖

    p[i]是*(p+i)的简单写法,实际上,至少对于编译器来说,[]这样的运算符完全可以不存在。

    可是对于人类来说,*(p+i)的写法在解读上比较困难,写起来也麻烦(键入量大)。因此,c语言引入[]运算符。

    就像这样,这些仅仅是为了让人类容易理解而引入的功能,的确可以让我们感受到编程语言的甜蜜味道(容易着手),有时我们称这些功能为语法糖(syntax sugar 或者 syntactic sugar)。

以上摘自《征服c指针》,借此推荐这本书。书中一针见血地指出:只有在声明语句中,[]才表达数组的含义,在表达式中,[]与数组无关!

总结起来就是,看似数组的用法:p[i],其实是*(p+i)的语法糖,p仍然是指针,与数组并无关系。


指针和数组的不同之处,还可以从下面的例子看出

void fun(int array[5])
{
	printf("  sizeof(array)...%d\n", sizeof(array));
}
int main()
{
	int array[] = { 1, 2, 3, 4, 5 };
	printf("  sizeof(array)...%d\n", sizeof(array));
	fun(array);
	return 0;
}
运行


从运行结果看,函数形参虽然用数组的方式进行了声明,但仍然被当做指针。这揭示了c语言中传递数组时的规则:传递过去的是地址,是指向数组起始元素的地址。之所以这样,基于两点;

  1. 从效率上考虑,若是把整个数组赋值过去,太耗时,也耗空间。还不如传地址过去,使用同一份内容。
  2. 在c语言设计之初,赋值操作就仅限于基本类型(char、int、float……),而数组是聚合类型。
这给我们的编程启示是:传递数组时,不要忘了把数组大小也传递过去。否则,函数那边由于不知道数组大小,极易越界。应这样设计函数 void fun(int *array, int n),n是数组大小。
还有一点需要指出,即使函数被设计成void fun(int array[5], int n),array依然被看成是指针。也就是说即使数组带了长度,该长度也会被编译器忽略掉。一句话:形参中的数组统统看成指针。
既然如此,还不如直接写成void fun(int *array, int n)。指针的形式,更能表达本意。


再思考:如果p[i]是*(p+i)的意思,由于加法具有交换律:p+i=i+p,那么i[p]同样可以表达p[i]的意思,是这样的吗?实验验证:
int main()
{
	int array[] = { 1, 2, 3, 4, 5 };
	int n = sizeof(array) / sizeof(int);
	int *p = array;
	int i;
	for (i = 0; i < n; i++)
		printf("  %d[p]...%d\n", i, i[p]);
	return 0;
}
运行

实验证明,我们的猜想是正确的:p[i]确实是*(p+i)的语法糖。i[p]这样的写法是否很逆天呢!

总结:只有在函数形参中,仅有这一种情况,声明的数组,如 int array[]会被看作是指针。其它情况下,指针与数组并无联系。

&array的含义
还有一点,对于 int array[5];array表示指向数组起始元素的指针,那么&array又是什么呢?实验下:
int main()
{
	int array[] = { 1, 2};
	printf("   array...%p\n", array);
	printf("  &array...%p\n", &array);
	printf("&array+1...%p\n", &array+1);
	return 0;
}
运行

分析实验结果:0031FCEC与0031FCE4相差8,而sizeof(array)就是8。
结论就是:array和&array都是指针,但类型不同。array的类型是int*,而&array的类型是int(*)[2]。array是指向普通int类型的指针;&array是数组指针,该数组元素是int类型的,且数组大小是2。
至于array和&array两者的值是一样的,应该很好理解。

补充
标量(scalar):简单讲,标量就是指char、int、double和枚举类型等数值类型,再加上指针。
至于数组、结构体和共用体这样将多个标量进行组合的类型,我们称之为聚合类型(aggregate)。

那么为什么int(*)[2]表示的是数组指针呢?这需要透彻理解c的声明语法。又比如,二维数组(更甚者,多维数组)的数组名又是什么类型的指针呢?这需要了解c中数组的实际含义,后序讲解。

更多指针和数组的内容见(5)c数组本质


专栏目录:


  • 28
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值