數組和指針

C语言中指针与数组这两个概念之间的联系是如此密不可分,以至于如果不能理解一个概念,就无法彻底理解另一个概念。

C语言中的数组值得注意的地方:
(1). C语言中只有一维数组,而且数组的大小必须在编译期就作为一个常数确认下来。C语言的数组可以是任何元素,所以数组的元素是另一个数组也是可以的。这样要”仿真”出一个多维数组也不是难事。

(2). 对于一个数组我们只需要做两件事
这里写图片描述

其他有关数组的操作,哪怕看上去是以数组下标形式进行运算的,实际上都是通过指针(变量)进行的

如果客官未能弄懂以上两点的隐含意思,那么数组运算也许就会给您带来麻烦。特别指出,编程者应该能够将数组运算与它对应的指针运算灵活切换、毫无置疑。因为在C语言中索引运算是以指针算术的形式来定义的,即数组在进行编译时会被编译器转化为指针形式(具体原因稍后会解释)。

int a;
int b[10];
int c[6][10];
int d[3][6][10];、

要理解C语言中数组的运行机制,就需要理解如何声明一个数组,如上
a 就是简单的整数;
b就是一个包含10个元素的数组;
c可以看做包含6个元素的数组,其中每个元素是自身包含10个整型元素的数组;
d可以看做包含3个元素的数组,每个元素是又包含6个元素的数组,而这6个元素自身又是包含10个整型元素的数组。

int array[3]; //其内存储存形式

其内存图像

int arr[3][6]; //其内存储存形式

这里写图片描述
这我也提供咯一张在VS2013下二维数组在内存空间的储存形式:
这里写图片描述
这里写图片描述
这些例子说明咯数组元素的存储顺序。(更新于2017年4月8日)

数组名

//考虑下面这些声明
int a;
int b[10];

我们把变量a称为标量,因为他是一个单一的值。
我们把变量b称为数组,因为他是一些值的集合。下标和数组名一起使用,用于标识该集合中某个特定的值(如b[0]表示数组b的第一个值)。每一个特定值都是一个标量,可用于任何使用标量的上下文环境。

通过上面可知b[0]的类型是整型,那么b的类型又是什么?他又表示什么?一个人合乎逻辑的答案就是整个数组,但是事实并非如此,在C中,几乎所有使用数组名的表达式中,数组名的值是一个指针常量,也就是数组第一位元素的地址,他的类型取决于数组元素的类型(如 数组元素为int型,那数组名的类型就是”指向int型的常量指针”)。
重点来咯,如果此时你认为数组和指针是相同的,那你就大错特错咯。数组具有一些和指针完全不同的特征,下面我会一一列出:
这里写图片描述

又上图可知编译器为其产生的只是一个指针常量,你可不能修改常量的值。如果你想修改这个值就相当于把整个数组移动到内存的其他位置。但是,在程序完成链接之后,内存中数组的位置是固定的,所以在程序运行时,再想要移动整个数组的位置就为时已晚咯。因此,数组名的值只是一个指针常量。

特别注意,只有在两种场合数组名并不用指针常量来表示——就是当数组名作为sizeof 或 单目操作符&的操作数时。sizeof返回的是整个数组的长度。去一个数组名的地址所产生的是一个指向数组的指针,而不是指向某个指针常量的指针。

说道这里,我想再提一下什么是声明,什么是定义(这里所说的对象并非面向对象程序语言里的对象,这里的对象只是跟链接器有关的”东西”,如函数和变量):
这里写图片描述
提炼一下:
声明相当于普通的声明,他所说明的并非自身,而是描述在其他地方创建的对象。
定义相当于特殊的声明,系统会为他分配内存。

这里写图片描述
这里写图片描述
这是我在C专家编程(好书)中截的图,大家可以看看在理解理解它们之间的不同!

下标引用 = 间接访问
在前面我们知道咯数组名其实是一个指向数组第一个元素的指针常量。
那么我们也应该思考一下,它是否能够像指针变量一样进行间接访问操作(⊙o⊙)?
如有 int b[10], *(b + 2) 和 b[2] 是否相同, b[2] 和 2[b] 是否相同?
答案是完全相同的两组表达式。
对于第一组表达式:在使用下标的地方,你可以使用对等的指针表达式来代替。使用上面指针表达式的地方,也可以使用下标表达式代替。

思考下面的例子:

int array[10];
int* ap = array + 2; // arr + 2 等同于 &a[2]

*ap:即array[2]的值;
ap[0]:如果你觉得“这不是个数组,不能这样做”那么你就陷入咯“其他语言不能这样做的”惯性思维中咯!?记住C的下标引用和间接访问表达式是一样的。 ap[0] 也可以写成 ( ap + (0) )即 (ap + 0);
*ap + 6:即ap[2] + 6;
ap[-1]:这是个负值的下标,我们应该怎么定义它呢?还是那句话下标引用就是间接访问表达式,即 *(ap - 1) => array[1] 也就是从指向array的第三个元素,使用偏移量 -1使我们得到它的前一个元素,即array[1];
对于第二组表达式 b[2] 和 2[b] 是否相同,也就不言而喻咯(也就是 ( b + (2) ) 和 ( (b) + 2 ) 的区别 就是没区别)。

这里我要提出一个问题,有部分人认为,在编写数组算法时,使用指针比使用数组“更有效率”。然而,这是说法在通常情况下是错误的。在现在的编译器下,一维数组与指针引用所产生的代码并不具有显著的差别。不管怎样,数组的下标定义实在指针的基础上的,所以优化器常常可以把它转化为更具有效率的指针表达式形式,并生成相同的机器指令。即在处理一维数组的时候,指针并不见得比数组快,C语言吧数组下标改写成指针的偏移量的根本原因是指针和偏移量是底层硬件所使用的的基本模型(这句话源自C专家编程,这里涉及到汇编语言大家只需记住即可)。

我们在先前就知道咯数组是在内存中连续存储的,即使用下标引用时是在连续的内存上移动的,所以编译器必须计算器步长。计算方法就是:偏移量 * 每个数组元素所占的字节数。(更新与2017年4月9日)

“作为函数参数的数组名 ”等同于指针
先回顾一下C语言中的一些术语,如下图:
这里写图片描述

这里摘抄与C语言专家编程:“标准情况下规定“类型的数组”的形参的声明必须调整为“类型的指针”。在函数形参定义这个特殊情况下,编译器必须把数组形式改写成指向数组第一位元素的指针形式。即编译器只向函数传递数组的地址,而不是整个数组的拷贝。(更新于4月12日)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值