数组的元素是变量(除非数组被声明为const),但是数组名不是变量。
按顺序存储的一系列类型相同的值
作为一种存储多个相关项的便利方式
整个数组有一个数组名,通过整数下标(用于识别数组元素的数字被称为下标、索引或偏移量,下标必须是整数)访问数组中单独的项或元素(数组元素被依次存在内存中相邻的位置),数组元素的编号从0开始,不是从1开始。
数组和字符串的区别:数组无空字符,但字符串存在空字符(char类型的数组末尾包含一个表示字符串末尾的空字符\0,则该数组中的内容就构成了一个字符串)
初始化数组把静态存储区的字符串拷贝到数组中,而初始化指针只把字符串的地址拷贝给指针。
数组通常被用来存储程序需要的数据。
只存储单个值的变量有时也称为标量变量。
数组类型 名称[元素个数]={值,值,值,值......};
用以逗号分隔的值列表(用花括号括起来)来初始化数组,各值之间用逗号分隔。在逗号和值之间可以使用空格。根据上面的初始化,把 1 赋给数组的首元素(powers[0]),以此类推(不支持ANSI的编译器会把这种形式的初始化识别为语法错误,在数组声明前加上关键字static可解决此问题。第12章将详细讨论这个关键字)。
有时需要把数组设置为只读。这样,程序只能从数组中检索值,不能把新值写入数组。要创建只读数组,应该用const声明和初始化数组。
const 数组类型 名称[元素个数] = {值,值,值,值......};
这样修改后,程序在运行过程中就不能修改该数组中的内容。和普通变量一样,应该使用声明来初始化 const 数据,因为一旦声明为 const,便不能再给它赋值。明确了这一点,就可以在后面的例子中使用const了。
数组和其他变量类似,可以把数组创建成不同的存储类别(storage class)。第12章将介绍存储类别的相关内容,现在只需记住:本章描述的数组属于自动存储类别,意思是这些数组在函数内部声明,且声明时未使用关键字static。到目前为止,本书所用的变量和数组都是自动存储类别。
在这里提到存储类别的原因是,不同的存储类别有不同的属性,所以不能把本章的内容推广到其他存储类别。对于一些其他存储类别的变量和数组,如果在声明时未初始化,编译器会自动把它们的值设置为0。
初始化列表中的项数应与数组的大小一致。// 当初始化列表中的值少于数组元素个数时,编译器会把剩余的元素都初始化为0。// 当初始化列表的项数多于数组元素个数,编译器会将其视为错误。// 可以省略方括号中的数字,让编译器自动匹配数组大小和初始化列表中的项数。
PS:a.如果省略方括号中的数字,编译器会根据初始化列表中的项数来确定数组的大小。
b.注意for循环中的测试条件。由于人工计算容易出错,所以让计算机来计算数组的大小。sizeof运算符给出它的运算对象的大小(以字节为单位)。整个数组的大小除以单个元素的大小就是数组元素的个数。
利用该特性可以初始化指定的数组元素
声明数组之后,借助数组下标(或索引)给数组元素赋值。
使用数组时,要防止数组下标超出边界。也就是说,必须保证数组是有效的值。
C99之前,声明数组时只能在方括号中使用整型常量表达式(由整型常量构成的表达式)。sizeof表达式被视为整型常量,但是const值不是。表达式的值必须大于0。
C99引用变长数组主要是为了让C成为更好的数值计算语言。
float a8[n];
float rain[5][12]
//5行,每行12个数
const float rain[YEARS][MONTHS]=
{
{4.3,4.3,4.3,3.0,2.0,1.2,0.2,0.2,0.4,2.4,3.5,6.6},
{8.5,8.2,1.2,1.6,2.4,0.0,5.2,0.9,0.3,0.9,1.4,7.3},
{9.1,8.5,6.7,4.3,2.1,0.8,0.2,0.2,1.1,2.3,6.1,8.4},
{7.2,9.9,8.4,3.3,1.2,0.8,0.4,0.0,0.6,1.7,4.7,6.3},
{7.6,5.6,3.8,2.8,3.8,0.2,0.0,0.0,0.0,1.3,2.6,5.2}
};
可省略内部的花括号,只保留最外面的一对花括号。只要保证初始化的数值个数正确,初始化的效果效果与上面相同。但是如果初始化的数值不够,则按照先后顺序逐行初始化,直到用完所有的值。后面的值没有值初始化的元素被统一初始化为0。
可以把一维数组想象成一行数据,把二维数组想象成数据表,把三维数组想象成一叠数据表。
通常,处理三维数组要使用3重嵌套循环,处理四维数组要使用4重嵌套。对于其他多维数组,以此类推。
int (*pz)[2];
//pz指向一个内含两个int类型值的数组
把pz声明为指向一个数组的指针,该数组内含两个int类型值。
为什么要在声明中使用圆括号?
因为[]的优先级高于*。
int *pax[2];
//pax是一个内含两个指针元素的数组,每个元素都指向int的指针
由于[]的优先级高,先与pax结合,所以pax成为一个内含两个元素的数组。然后*表示pax数组内含两个指针。最后,int表示pax数组中的指针都指向int类型的值。声明的是一个指向数组的指针。
指针之间的赋值比数值类型之间的赋值要严格。
利用for循环得到多维数组
const并不是要求原数组是常量,而是该函数在处理数组时将其视为常量,不可更改。这样使用const可以保护数组的数据不被修改,就像按值传递可以保护基本数据类型的原始值不被改变一样。一般而言,如果编写的函数需要修改数组,在声明数组形参时则不使用const;如果编写的函数不用修改数组,那么在声明形参时最好使用const。
可以创建const数组、const指针和指向const的指针(不可用于改变值)【通常用于函数形参中,表明该函数不会使用指针改变数据】。
把const数据或非const数据的地址初始化为指向const的指针或为其赋值是合法的,只能把非const数据的地址赋给普通指针。
C标准规定,使用非const标识符修改const数据导致的结果是未定义的。
const可以声明并初始化一个不能指向别处的指针,关键是const的位置,可以用这种指针修改它所指向的值,但是它只能指向初始化时设置的位置。
在创建指针时还可以使用const两次,该指针既不能更改它所指向的地址,也不能修改指向地址上的值。
1.C++允许在声明数组大小时使用const整数,而C不允许。
2.C++的指针赋值检查更严格:C++不允许把const指针赋给非const指针,但是C允许这样做。
是否可以在声明数组时使用const变量?
C90标准不允许(也可能允许),数组的大小必须是给定的整型常量表达式,可以是整型常量组合,如20、sizeof表达式或其他不是const的内容。由于C实现可以扩大整型常量表达式的范围,所以可能允许使用const,但是这种代码可能无法移植。
C99/C11标准允许在声明变成数组时使用const变量,所以该数组的定义必须是声明在块中的自动存储类别数组。
C99新增了变成数组,允许使用变量表示数组的维度。
变成数组中的“变”不是指可以修改已创建数组的大小,一旦创建了变成数组,它的大小则保持不变。这里的“变”指的是:在创建数组时,可以使用变量指定数组的维度。
变长数组还允许动态内存分配,这说明可以在程序运行时指定数组的大小。普通C数组都是静态内存分配,即在编译时确定数组的大小。由于数组的大小是常量,所以编译器在编译时就知道了。
都可用于创建在运行时确定大小的数组
变长数组是自动存储类型。因此,程序在离开变长数组定义所在的块时(该例中,即vlamal()函数结束时),变长数组占用的内存空间会被自动释放,不必使用 free()。另一方面,用malloc()创建的数组不必局限在一个函数内访问。例如,可以这样做:被调函数创建一个数组并返回指针,供主调函数访问,然后主调函数在末尾调用free()释放之前被调函数分配的内存。另外,free()所用的指针变量可以与 malloc()的指针变量不同,但是两个指针必须储存相同的地址。但是,不能释放同一块内存两次。
对多维数组而言,使用变长数组更方便。当然,也可以用 malloc()创建二维数组,但是语法比较繁琐。如果编译器不支持变长数组特性,就只能固定二维数组的维度。
1.声明数组时,用常量表达式表示数组的维度,用数组名访问数组的元素。可以用静态内存或自动内存创建这种数组。
2.声明变长数组(C99新增特性)时,用变量表达式表示数组的维度,用数组名访问数组的元素。这种数组只能在自动内存中创建。
3.声明一个指针,调用malloc(),将其返回值赋给指针,使用指针访问数组的元素。该指针可以是静态的或自动的。
PS:后面两种方法创建的是动态数组,可在程序运行时选择数组的大小和分配内存。