10.1 数组git
数组由一系列相同的元素构成。数组
数组声明中包括数组元素的数目和元素的类型。函数
一些数组声明的例子:优化
float candy[365]; /*365个浮点数的数组*/code
char code[12]; /*12个字符的数组*/对象
int states[50]; /*50个整数的数组*/索引
方括号表示标识符为数组,方括号的数字指明了数组所包含元素数目。内存
要访问数组中的元素,能够使用下标数字来表示单个元素。下标数字也称索引(index),是从0开始计数的。所以,candy[0]表示candy首元素,candy[364]是第365个元素,也就是最后一个元素。编译器
10.1 初始化it
程序中一般使用数组来存储数据。
C为数组初始化引入如下新语法:
int powers[8] = {1,2,4,6,8,16,32,64}; /*只有ANSI C 支持这种初始化方式*/
从上例中能够看出,可使用花括号括起来的一系列数值来初始化数组。数值之间用逗号隔开在数值和逗号之间可使用空格符。
程序清单10.1 day_mon1.c程序
#include <stdio.h> #define MONTHS 12 int main (void) { int days[MONTHS] = {31,28,31,30,31,30,31,31,30,31,30,31}; int index; for(index=0;index<MONTHS;index++) printf("MONTH %d has %d days.\n",index+1,days[index]); return 0; }
注意本例采用标识符常量MONTHS来表明数组的大小。这是一种咱们所推荐的作法。若是须要修改数组大小 ,只须要修改#define语句便可,无须查找并修改程序中每一处使用数组大小的地方。
对数组使用const的方法
有时须要使用只读数组,也就是程序从数组中读取数值,可是不向数组中写数据。在这种状况下声明并初始化数组时,建议使用关键字const。咱们对程序清单10.1的一部分进行优化,以下:
const int days[MONTHS] = {31,28,......};
使用未经初始化的数组会出现什么状况呢?
与普通变量类似,在初始化以前数组元素的数值是不定的。编译器使用的数值是存储单元中已有的数值。
*************************************************
存储类解释
和其余变量类似,数组 能够被定义为多种存储类(stotage class),第12章将详述此主题。目前,只须要了解本章的数组属于自动存储类。也就是说,数组 是在一个函数内声明的,而且声明时没有使用关键字static。到目前为止,本书所用的变量和数组都是自动类型的。
如今提起存储类的缘由是,不一样存储类有时具备不一样的属性,所以不能把配音的知识推广到其余存储类。例如,若是没有进行初始化,一些存储类的变量和数组会把它们的存储单元设置为0.
************************************************
初始化列表中的元素数目应该和数组大小一致。若是两者不一致,会出现什么状况?
当数值数目少于数组元素数目时,多余的数组元素被初始化为0。也就是说,若是不初始化数组,数组元素和未初始化的变量同样,其中存储的是无用数值;但若是部分初始化数组,未初始化的元素则被设置为0。
若是初始化列表中项目的个数大于数组大小,编译器会绝不留情的认为这是一个错误。然而,能够采用另一种形式以免受到编译器的这种奚落:您能够省略括号中的数字,从而让编译器自动匹配数组大小和初始化列表中的项目数目。
程序清单10.4 day_mon2.c程序
#include <stdio.h> int main (void) { int days[] = {31,28,31,30,31,30,31,31,30,31,30}; int index; for(index=0;index<sizeof days / sizeof days[0];index++) printf("MONTH %d has %d days.\n",index+1,days[index]); return 0; }
程序清单10.4 中有两点须要注意:
一、当使用空的方括号对数组进行初始化时,编译器会根据列表中的数值数目来肯定数组大小。
二、注意for循环的控制语句。因为人工计算容易出错,所以可让计算机来计算数组的大小。运算符sizeof给出其后的对象或类型的大小(以字节为单位)。所以sizeof days是整个数组的大小(以字节为单位),sizeof days[0]是一个元素的大小(以字节为单位)。整个数组的大小除以单个元素的大小就是数组中元素的数目。
三、自动计数的缺点:初始化的元素个数有误时,咱们可能意识不到(上例中初始化项目只有10个,应该是12个)。
10.1.2 指定初始化项目
c99增长了一种新特性:指定初始化项目。
此特性容许选择对某些元素进行初始化。在初始化列表中使用带有方括号的元素下标能够指定某个特定的元素:
int arr[6] = {[5]=212}; //把arr[5]初始化为212
对于一般的初始化,在初始化一个或多个元素后, 未经初始化的元素都将被设置为0。
程序清单10.5 designate.c程序
#include <stdio.h> #define MONTHS 12 int main (void) { int days[MONTHS] = {31,28,[4]=31,30,31,[1]=29}; int index; for(index=0;index<MONTHS;index++) printf("MONTH %d has %d days.\n",index+1,days[index]); return 0; }
输出结果以下:
1 31 2 29 3 0 4 0 5 31 6 30 7 31 8 0 9 0 10 0 11 0 12 0
从输出结果能够看出指定初始化项目有两个重要特性。
第一,若是在一个指定初始化项目后跟有不止一个值,例如在序列[4]=31,30,31中这样,则这些数值将用来对后续的数组元素初始化。也就是说,把31赋给days[4]以后,接着把30和31分别赋给days[5]和days[6]。
第二,若是屡次对一个元素进行初始化,则最后一次有效。例如,前面把days[1]初始化为28,然后面的指定初始化[1]=29覆盖了前面的值,因而days[1]的数值最终为29。
10.1.3 为数组赋值
声明完数组后,能够借助数组的索引(下标)对数组成员进行赋值。
/*数组赋值*/ #include <stdio.h> #define SIZE 50 int main (void) { int counter,evens[SIZE]; for (counter = 0;counter < SIZE;counter++) evens[counter]=2*counter; ... }
注意这种赋值的方式是使用循环对元素逐个赋值。C不支持把数组做为一个总体来进行赋值,也不支持用花括号括起来的列表形式进行赋值(初始化的时候除外):
/*无效的数组赋值*/ #define SIZE 5 int main (void) { int oxen[SIZE] = {5,3,2,8}; /*这里是能够的*/ int yaks[SIZE]; yaks = oxen; /*不容许*/ yaks[SIZE]=oxen[SIZE]; /*不正确*/ yaks[SIZE]={5,3,2,8}; /*不起做用*/
10.1.4 数组边界
使用数组的时候,须要注意数组索引不能超过数组边界。也就是说,数组索引应该具备对于数组来讲有效的值。
程序清单 10.6 bounds.c程序
//bounds.c --超出数组的边界 #include <stdio.h> #define SIZE 4 int main (void) { int value1=44; int arr[SIZE]; int value2=88; int i; printf("value1 = %d,value = %d\n",value1,value2}; for (i = -1;i<=SIZE;i++) arr[i] = 2*i+1; for (i = -1;i<7;i++) printf("%2d %d\n",i,arr[i]); printf(value1 = %d,value2 = %d\n",value1,value2); return 0; }
程序不检查索引的合法性。
在标准C中,若是使用了错误的索引,程序执行结果是不可知的。也就是,程序也许可以运行,可是运行结果能够很奇怪,也可能会异常中断程序的执行。咱们使用Digital Mars8.4运行程序,其输出结果以下:
value1 = 44,value2=88 -1 -1 0 1 1 3 2 5 3 7 4 9 5 5 6 1241520 value1=-1,value2=9
注意,咱们使用的编译器看起来是value2正好存储在数组后面的那个存储单元中,把value1存储在数组前面的那个存储单元中(其余编译器可能采起不一样的顺序在内存中存储数据)。这样arr[-1]就和value1对应同一存储单元,arr[4]就和value2对应同一存储单元。
所以,使用超出数组边界的索引会改变其余变量的数值。对于不一样编译器,输出结果可能不一样。
一件须要记住的简单事情就是,数组的计数是从0开始的。避免出现这个问题比较简单的方法是:在数组声明中使用符号常量,而后程序中须要使用数组大小的地方都直接引用符号常量:
#define SIZE 4 int main(void) { int arr[SIZE]; for(i=0;i<SIZE;i++) ... }
这样作的好处是保证整个程序中数组大小始终一致。
10.1.5 指定数组大小
在前面提到的例子中,咱们声明数组时使用的是整数常量。
还容许使用什么?直到C99标准出现以前,声明数组时在方括号内只能使用整数常量表达式。整数常量表达式是由整数常量组成的表达式。sizeof表达式被认为是一个整数常量,而一个const值却不是整数常量。而且该表达式的值必须大于0:
int n=5; int m=8; float a1[5]; //能够 float a2[5*2+1]; //能够 float a3[sizeof(int)+1]; //能够 float a4[-4]; //不能够,数组大小必须大于0 float a5[0]; //不能够,数组大小必须大于0 float a6[2.5] //不能够,数组大小必须是整数 float a7[(int)2.5]; //能够,把float类型指派为int类型 float a8[n]; //C99以前不能够 float a9[m]; //C99以前不能够
遵循C90标准的C编译器不容许最后两个声明。而c99容许这两个声明,但这建立了一种新数组,称为变长数组(variable length array),简称VLA。
VLA有些限制,例如,声明时不能进行初始化。