8

C语言陷阱与技巧第14节,数组操作的一些技巧,怎么只给一部分数组元素赋值?

 3 years ago
source link: https://blog.popkx.com/c-language-traps-and-techniques-section-14-some-techniques-of-array-operation-how-to-assign-only-a-part-of-array-elements/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

数组是大多数编程语言中非常重要的语法,在C语言中尤为如此,可以认为C语言中的数组是一种复合数据类型——若干数据类型元素组合而成的集合。

之前的文章讨论过,数据结构决定代码逻辑和算法,换句话说也就是决定可以处理什么样的问题。C语言中的数组特别适合存储大量相同类型的元素,而且数组的各个元素在内存中是紧密排列的,因此各个元素的地址都可知,所以C语言程序可以随机访问数组元素,这使得排序、查找最值等问题变得方便。

数组的定义和使用

在C语言中定义一个数组是方便的,上面说到数组是“若干数据类型元素组合而成的集合”,因此可以如下定义数组:

<数据类型> 数组名[纬数][纬数]...;

<数据类型>可以是基础数据类型,也可以是其他复合数据类型。例如定义一个 int 型的一维数组,相关C语言代码可以如下写:

int a[3];

这样就定义了一个可以存放 3 个 int 元素的数组 a。使用 a[0] 可以访问第一个元素,a[2] 可以访问第三个元素。应该注意的是,a[3] 已经不属于数组 a 了,使用 a[3] 可能会带来比较严重的错误。

使用 a[3] 是初学者常常会跳进的“陷阱”。

初学者看到 a[3] 不可用时,常常会感到迷惑,明明是 int a[3]; 呀!为什么还说 a[3] 不可用呢?应该明白,即使是相同的符号,编译器也有可能根据C语言程序的上下文将其解释为不同的含义的,例如:

int b = 3;
int c = -b;
int d = c - b;

上面这几行C语言代码中,编译器会将“-”号分别解释为“负号”和“减号”。类似的,定义数组时的 int a[3]; 中的“a[3]”和之后访问数组的“a[3]”具有不同的含义。前者表示数组 a 中共有 3 个元素,而C语言中的数组是从 0 开始计数的,因此之后只应使用 a[0]、a[1]、a[2] 这三个元素,a[3] 已经是第 4 个元素了,是不应该使用的。

上例中,如果非要使用 a[3],C语言程序也不一定会出错,而且你甚至可以使用 a[4]、a[5]。但是不出错不代表没有错,具体原因我之前的文章里讨论过。

初始化数组的方法

在定义数组时,可以赋给其初值,例如:

int a[3] = {1, 2, 3};

上面这段C语言代码不仅定义了一个 数组,而且还赋了初值{1, 2, 3},它相当于下面这几句:

int a[3];
a[0] = 1;
a[1] = 2;
a[2] = 3;

可以看出,定义时赋初值可以少写一些C语言代码,更方便些。不过应该小心一个“陷阱”——给数组赋初值只能在定义的时候进行,已经定义好的数组,只能逐元素赋值。所以下面这段C语言代码是非法的:

int a[3];
a[3] = {1, 2, 3};   // 非法
a = {1, 2, 3};  // 非法

其实原因也很简单,在数组被定义好之后,a[3] 就表示第 4 个元素了,自然无法再存放 {1, 2, 3}。而数组名 a 在某种程度上相当于一个地址,自然也是无法存放 {1, 2, 3}的。同样的道理,下面这样的操作也是不合法的:

int a[3] = {1, 2, 3};
int b[3];
b[3] = a[3]; // 非法
b = a;  // 非法

定义一维数组时,可以不指定它的元素个数,例如下面这句C语言代码:

int a[] = {1, 2, 3};

这种情况下,程序员必须为数组指定初值,因为编译器要根据初值确定数组的元素个数。

例如上面这个例子,根据 {1, 2, 3} ,编译器会认为数组 a 有 3 个元素。

初始化数组的一些技巧

有时候,在定义完数组之后,需要将其清零。按照上面的介绍,C语言代码可以如下写:

int a[3] = {0, 0, 0};

这样的确可以实现需求,但是如果数组的元素个数很多,再这么写就非常麻烦了。此时可以借助 memset() 函数将数组中所有元素归零:

int a[1024];
memset(a, 0, sizeof(int)*sizeof(a));

不过,如果每定义一个数组,就需要写一次 memset() 还是有些麻烦。更方便的做法如下,请看相关C语言代码:

int a[1024] = {0};

虽然看起来上面这行C语言代码只对数组 a 的其中一个元素赋值,但是编译器确实会将其他元素也归零,感兴趣的读者可以自己写代码试一试。应注意,这里也有一个小“陷阱”——有些C语言初学者希望定义一个数组,并且将其所有元素赋值为 1,于是写出下面这样的代码:

int a[1024] = {1};

数组 a 中的元素自然不会与预期一致,编译器会将其他省略掉的元素归零,所以上面这句C语言代码执行之后,只有 a[0] 等于 1,其他元素都为 0。

在C语言程序开发中,有时候可能会希望将 a 中的某些元素赋初值,例如:

int a[1024];
a[100] = 99;
a[101] = 99;
a[102] = 99;
a[200] = 11;

似乎只能这么写,没有办法在定义时赋初值。以前的确如此,但是现在很多C语言编译器支持下面这种写法:

int a[1024] = { 
    [100 ... 102] = 99, 
    [200]=11 
};
64f1f6817a3190d83ce636bd20ecea17.png

这与上面那几行逐元素赋初值的C语言代码是等价的,编译这段C语言代码,得到如下输出:
# gcc t.c
# ./a.out 
99 99 99 11

本节主要介绍了C语言中数组相关的一些“陷阱”与使用“技巧”。需要再说明的是,本节讨论的一些“技巧”其实是 gcc 的扩展,这对代码的可移植性有些损害。因此,其中的取舍需要由读者自己把握。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK