工作之后最害怕的是对最基础知识的考查,因此还是有必要随时回炉学习,温故知新。今天再次回顾总结一下c语言数组相关知识。主要是学习《c语言陷阱与缺陷》的学习笔记。
c语言数组值得注意的有两点:
1,c语言中只有一维数组,而且数组的大小必须在编译时期就确定下来(旧标准)。然而,c语言数组的元素可以是任意对象,包括数组。这样就给多维数组的实现或者仿真提供了可能。
2,对于数组,我们只能够做两件事。一是获取数组的大小,二是获得指向数组下标为0的元素的指针。其他的任何操作虽说看起来像是数组,但是本质上其实都是基于指针的操作。
以上两天总结摘取自《c语言陷阱与缺陷》,让我想起了之前自己看过的国外的一份c语言数据结构与算法的讲义。其中关于c语言数组与指针的知识给了一个重点关注点的概括——除了作为sizeof的参数以外,其它时候c语言数组的名称跟指向数组首元素的指针是等价的。
关于c语言中数组的运算机制如何实现是关系到对数组理解的一个重点。其实,这就是要学会(或者说是记住)c语言数组声明定义的形式以及含义。
1,int a[3];
定义了一个元素个数为3个的数组a,其中每个元素都是int类型。
2, struct
{
int p[4];
double x;
}b[17];
定义了一个元素个数为17个的数组b,其中每个元素都是一个结构体。
3,int a[12][31];
声明定义了一个数组a,该数组有12个元素,其中每个元素是一个拥有31个元素的数组,每个最小子元素的类型都是int。这个地方理解时候一定不能够按照前面两个例子的顺序,从左到右一个个数字字符进行解析。需要记住的还是最前面的那一点,c语言中只有一维数组,而数组名称后面跟着的第一个数字维度就是数组的真实维度。除此之外,其他的参数则是数组元素的信息描述。
数组名称与指向数组的指针的等价之处在两种表示的等价,如果定义了一个数组a,那么数组的下表为i的元素可以表述为:
*(a + i) 或者a[i]
实际上,后者只是前者的一种常用的简写方式。
而由于上面这种描述,其实a[i]和i[a]表示的含义一样。编写如下测试代码;
#include"stdio.h"
int a[5] ={1,2,3,4,5,};
int main(void)
printf("%d\n",a[3]);
printf("%d\n",3[a]);
return 0;
}
其中,数组定义的时候我故意在最后的元素后面加上了一个逗号。其实,这并不是错误。本来我也不是很理解这种方式,在对了《unix编程艺术》一书之后才明白。其实,这算是c语言设计的一个优秀的特点,这种特征不仅方便数组元素的扩充时候的修改,而且方便数组的工具生成。程序的编译以及运行记录如下;
e:\workspace\01_编程语言\01_c语言\01_c语言陷阱与缺陷\exp02>gccexp02.c
e:\workspace\01_编程语言\01_c语言\01_c语言陷阱与缺陷\exp02>a
4
由上面的验证可以得出先前陈述的结论或者推论。
接下来通过简单的代码,回顾二维数组与指针的关系基础知识。编写代码如下:
int a[12][31];
int *p;
int i;
p = a[4];
printf("value of p: %p\n",p);
printf("size of a[4]:%d\n",sizeof(a[4]));
a[4][7] = 3;
i = a[4][7];
printf("value of i: %d\n",i);
i = *(a[4] + 7);
i = *(*(a + 4) + 7);
编译后,运行结果如下:
value of p:00405630
size of a[4]: 124
value of i: 3
其中,a[4]的含义自然是代表数组a的下标为4的元素,而元素的为31个int类型对象。通过计算其占用的空间可以看出这一点。而进一步来看,其实a[4]应该是一个31个元素的数组的数组名。这样,a[4]也就等同于这个数组的首地址。在程序中,我通过指针赋值的方式输出了这个地址数值。接下来的元素取值就值得去品味一下了:
第一次i取值:使用了数组名后面加中括号指明偏移来实现;
第二次i取值:进一步把第一步中的方式改成了指针取值的方式,通过首地址加偏移的方式。
第三次i取值:把迭代着的一个数组进一步展开。从最终的形式上看,这种方式比使用下标取值的简写方式要难理解不少。
最终i的三次输出都是相同的,其实这也算是对三种方式等价的一个最基本的验证。而把a赋值给p是错误的,因为a的含义是一个指向“二维数组的指针”。在代码中加入相应的代码测试的时候,gcc编译器会给出警告。不过,程序还是能够编译通过并运行。