程序设计与C语言引论笔记——C语言数组的定义和初始化

3 篇文章 0 订阅

C语言数组的定义和初始化

一维数组

数组变量也是变量。数组定义的形式与简单变量类似,但需要增加有关元素个数的信息。在被定义变量名之后写一对方括号就是一个数组定义,指定元素个数的方式是在括号里写一个整型表达式。
数组元素个数必须能在编译时静态确定,因此这个表达式必须能静态求值,
最简单的情况就是写一个整型字面量(整数)。
给数组指定初值也是在定义变量时通过附加描述给出元素初值:各元素值的表达式顺序在一对花括号里,表达式间用逗号分隔。
全局数组和局部静态数组(用关键字 static)同样在程序开始执行前建立并初始化;
局部自动数组也是在程序执行进入相应函数时建立和初始化。

int a[10]; 
//定义数组时没做初始化,
//如果是外部数组或局部静态数组(用关键字 static),元素将自动初始化为 0,
//如果是自动数组,元素将不初始化,这些元素的值处在没有明确初始化的状态。

extern double a1[]; 
//在写数组变量的外部说明时不必写数组大小,只要在数组变量名后写一对方括号。
//这个说明通知本源文件的其他部分,有个数组a1在其他地方定义,它们的元素类型是双精度类型

int b[4] = {1, 1, 2, 3};  
//元素初值表达式必须是常量表达式,自动数组的初始化也只允许用常量表达式。
//初始化表示中元素的个数不超过数组元素的个数。

int b1[4] = {1, 2};  
//语言允许只给数组前一段元素指定初值,这时未指定值的元素将自动初始化为 0。
//因为 b1 的初值不够,b1[2]、b1[3]自动初始化为 0。
//(无论是外部数组还是自动数组)。

int b2[4] = {1, 2, 0, 0};//b1 和 b2 中各元素的值完全一样。

int fib1[] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55};  
//为了编程方便,C 语言规定,在给出了所有元素初值的情况下,定义数组时可以不写大小。
//这时系统根据初始化表达式的个数自动确定数组大小。

int fib2[10] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55}; 
//数组 fib1 和 fib2 都是 10 个元素的整数数组,两种定义方式等价。
//前一写法能带来一点方便,因为这样能减少维护两部分描述之间一致性的麻烦,有利于程序修改。

int arr[6] = {[5] = 212};
//指定arr[5]初始化。
//初始化部分元素后,未初始化元素都会被设置为0。

int arr2[6] = {[5] = 212};
//数组有6个元素。指定arr[5]初始化。
//初始化部分元素后,未初始化元素都会被设置为0。

int days[12] = {31,28, [4] = 31,30,31, [1] = 29};
//days[4]=31,代码将days[5]和days[6]初始化为30和31。
//开始将days[1]初始化为28,但之后会被[1]=29指定的初始化所覆盖。

应特别指出,这种为数组元素指定值的写法只能用在初始化时,语句里不能采用这种写法。
数组元素个数必须能在编译时静态确定,根据这个规定,下面数组定义不合法:

 void f(int n) {  
     int b[n]; // 此时局部数组 b 的大小依赖于函数的参数值,这个值在编译时无法确定。
 }

多维数组

C 语言里也能定义两维的和更多维的数组。

double a[3][2]; 
//在这里把两维数组看作一维数组的数组。
//也就是说,两维数组里的每个元素都是(成员类型相同,成员个数也相同的)一维数组。
//a 包含三个元素,每个元素各是一个双精度数组,其中包含了两个 double 元素。
//人们常说 a 是 3×2 的双精度数组。有时也说 a 是 3 行 2 列的数组,这是从矩阵概念那里借用的说法。

int a1[3][2][4];
//定义了一个三维数组.
//三维就不好说是二维数组的一维数组还是一维数组的二维数组了。
//准确地说,三维数组是一维数组的一维数组的一维数组。
//更多维的数组的定义也与此类似。

多维数组也可以在定义时直接初始化。

int a[3][2] = {{1, 2}, {3, 4}, {5, 6}};  
//内嵌各括号里的数据将用于初始化各个成员数组。
//这里要求各括号里表达式的个数都不超过成员数组长度,数据的组数不超过成员数组的个数。
//如果给出的表达式不够,相应成员数组 的其他成分也将被自动设置为 0。 

int b[3][2] = {1, 2, 3, 4, 5, 6}; 
//多维数组的初始化表示中也可以不写内嵌括号,采用平铺的写法。
//所列出的初始值按顺序,依次给各个成员数组的各成分置初值。
//如果初始值的个数不够,剩余成分也置 0。
//第二种形式写起来简单,但不如前一形式清晰。

int c[][2] = {{1, 2}, {3, 4}, {5, 6}};
int d[][2] = {1, 2, 3, 4, 5, 6};
/*如果定义时提供了对数组初始化的部分,并实际给出了全部元素的初始值,
那么就可以不写被定义数组第一个下标的元素数,
因为这个数可以根据初始化表示和该数组其余维的长度计算出来。*/

一维数组采用连续顺序存储元素的方式实现。多维数组的内部表示也完全一样,同样是依次连续存放数组元素,即其中的各成员数组),而这些成员数组也按同样方式存放它们的元素。

一行(一个成员数组)的元素连续存放,这种形式又被称为按行存放,或者行优先存放。

字符数组

摘自程序设计与C语言引论笔记——字符数组与字符串
字符数组就是以字符作为元素的数组。由于人们常用 C 语言写与处理字符序列或文本的程序,而字符序列理所当然地应该存放在字符数组中,所以C为处理字符数组提供了特别支持。

对于程序中写出的字符串字面量,系统将用字符数组的形式存储它们:分配连续的若干个存储单元,顺序存入字符串中的各个字符,每个字符占据一个字节。在存储了字符串常量的所有字符之后,还要另外存一个空字符 '\0’作为字符串的结束标志。

例如下面的字符串虽然只有7个字符,其内部却要占据8个字节的存储。

image-20211029130022771

用这种方式表示字符串是为了处理方便。字符串数据与基本类型的数据不同,不同的字符串可能有不同长度。这种情况下,程序怎么才能从字符串的内部表示确定字符串结束的位置呢?

有了字符串末尾的空字符,处理字符串的程序就可以顺序检查,遇到空字符就知道遇到了字符串结束。虽然空字符不是字符串内容的一部分, 但却是字符串表示中不可缺少的部分。

标准库的字符串处理函数都是基于这种表示定义的, 我们自己写字符串处理程序时也应该遵守这种规则。

下面的问题是:能在自己定义的字符数组里存放字符串吗?回答是肯定的,根据字符串存储形式的规定,只要在数组里顺序存入所需字符,随后存一个空字符,这个字符数组里的数据就有了字符串的表现形式,这个数组也就可以当作字符串使用了。在这种情况下,人们也说这个数组里存了一个字符串。例如有下面定义:

char a[5] = {'i', 's', 'n', 'o', 't'}; 
//数组a里存的不是字符串,因为缺少表示串结束的空字符。

char b[5] = {'g', 'o', 'o', 'd'};
//数组 b是字符串,尽管提供的初始化表达式个数不够,
//但按C语言规定,数组剩余位置自动置为 0(字符值的 0 就是空字符),正好当作字符串结束标志。

char c[5] = {'f', 'i', 'n', 'e', '\0'}; 
//c 初始化时已在有效字符后加了一个空字符,所以它也存了一个字符串。

char d[5] = {'o', 'k', '\0'};
//数组 d 最后两个字符未给,同数组b一样,将自动设为空字符,对d作为字符串没有影响。
char e[5] = {'o', 'k', '\0', '?', '?'};
// 作为字符数组,e 的 5 个元素分别有了值,意义很清楚。
//如果将e 中数据当作字符串看待和处理,遇到空字符就认为串已结束,后面的东西对串处理已经没意义了。
//所以,如果 e 被作为字符串处理时,那就是一个只包含两个字符的串。

为了方便使用,C 语言为字符数组提供了特殊的初始化形式:允许以字符串形式为字符数组的一系列元素指定初值。这种初始化形式应看成一般形式的简写,例如可以写:

char a1[20] = "Programming";
//a1 的前 12 个字符指定了值,不但有明确写出的 11 个字符,还有一个作为字符串结束的空字符。
//随后部分自动用空字符填充  

char a[] = "Programming"; 
//这定义了一个 12 个元素的数组,其中依次存放各字符,最后元素存入一个空字符。
//用字符串做字符数组初始化时也允许不直接给出数组元素个数。
//这时的数组大小规定为初始化字符串的字符数加 1,因为需要在数组最后存一个空字符。

char *p = "Programming";
//这个定义有多重含义,实际上完成了三件事:
//(1)定义了字符指针 p;
//(2)建立了字符串常量"Programming",它以字符数组形式存储,最后有一个空字符;
//(3)给 p 指定初值,使它指向刚建立的那个字符串常量。
//p 是指针,虽然定义时令它指向创建的字符串常量,但这并不妨碍随后的程序里重新给它赋值。
//在指针指向字符串常量时,不允许通过它去修改字符串常量的值。

p = "Programming Language C"; 
//这个语句建立另一字符串常量,并把这个字符串常量的起始地址赋给指针 p。
//字符串常量以字符数组形式存储,最后有一个空字符。

//p 和 a 的类型不同,大小也不同。p 只占一个指针所需的存储空间,而 a 占据 12 个字符的空间。

指针指向的字符串和存储在数组里的字符串
图为指针指向的字符串和存储在数组里的字符串。
显然,上述定义char *p的效果与定义char a[]的效果不同。

字符数组可以用在各种需要字符串的地方。例如,如果程序里许多输出语句都用同样输出格式,一种可能方法就是定义一个公用的格式描述数组。例如下面例子:

 char outform1[] = "Two reals: %f, %f\n";  /* 程序中可以使用这个格式描述 */  
 printf(outform1, x, y);  

这样可以减少重复定义。对输入格式也可以采用同样技术。

参考书籍

《从问题到程序——程序设计与C语言引论》裘宗燕著,北京大学出版社,1999.4

《从问题到程序——程序设计与C语言引论》2003,2004年修订

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值