14.4 单文件综合示例:求数组最大值
下面的程序综合使用数组、循环、选择、sizeof、断言和格式化输出。它适合在完成第 14 章后阅读,不作为第一个 C 程序。
c
#include <assert.h>
#include <float.h>
#include <stdio.h>
int main(void) {
double maximum = -DBL_MAX;
double numbers[6] = {
[0] = 1,
[2] = 2.26,
[3] = (double)(5 / 2),
[4] = .1,
[5] = 3.E-2
};
size_t count = sizeof numbers / sizeof numbers[0];
assert(count == 6);
for (size_t i = 0; i < count; ++i) {
if (numbers[i] > maximum) {
maximum = numbers[i];
}
}
printf("The maximum in the %zu numbers is %g\n", count, maximum);
return 0;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
输出:
bash
The maximum in the 6 numbers is 2.26
(double)(5 / 2) 先执行整数除法,再把结果转换为 double,因此数组中的该元素是 2.0,不是 2.5。
1. 习题
#11414
⚡2⏳2
阅读下面程序,回答四个问题:
c
#include <float.h>
#include <stdio.h>
int main(void) {
double maximum = -DBL_MAX;
double numbers[5] = {
[1] = -3.5,
[3] = (double)(7 / 2),
[4] = 1 / 4
};
size_t count = sizeof numbers / sizeof numbers[0];
for (size_t i = 0; i < count; ++i) {
if (numbers[i] > maximum) {
maximum = numbers[i];
}
}
printf("The maximum in the %zu numbers is %g\n", count, maximum);
return 0;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
numbers的五个元素各是多少?- 程序求出的最大值是多少?
[1]处的元素是-3.5,绝对值比它大,为什么没有成为结果? - 把
(double)(7 / 2)改成(double)7 / 2后,最大值变成多少?这个改动改变了哪一步的计算类型? - 页首的示例在算出
count之后写了assert(count == 6);,上面这段程序没有写。这个断言检查的是什么?只要numbers的声明不变,它就一定成立,那它还有什么用?如果后来有人往初始化器里加了一个元素却忘了改断言,会发生什么?
#11415
⚡3⏳2
把页示例里的求最大值循环提取成函数,得到下面的程序:
c
#include <float.h>
#include <stdio.h>
static double array_max(const double values[]) {
double maximum = -DBL_MAX;
size_t count = sizeof values / sizeof values[0];
for (size_t i = 0; i < count; ++i) {
if (values[i] > maximum) {
maximum = values[i];
}
}
return maximum;
}
int main(void) {
double numbers[4] = {3.5, -1.0, 8.25, 2.0};
printf("%g\n", array_max(numbers));
return 0;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 用
-std=c17 -Wall -Wextra编译,贴出与sizeof有关的警告,说明函数里的values是什么类型。 - 运行程序,写出实际输出,解释为什么不是
8.25。 - 修改函数签名和调用处,让结果正确,同时保持函数能处理任意长度的数组。写出改后的完整程序和输出。
- 说明为什么
sizeof numbers / sizeof numbers[0]写在main里是对的,搬进函数就错了。
#11416
⚡4⏳3
写一个程序 stats,从标准输入读取空白分隔的 double,输出一行统计结果:
text
count=5 max=12 mean=2.61
要求:
- 用
scanf("%lf", ...)逐个读取(double对应的转换说明是%lf),循环条件是读取成功;遇到第一个无法转换的内容就视为输入结束,已经读到的数照常统计。 - 输入的数值可能全是负数,最大值不能因此错成
0。 - 一个数也没读到时,向标准错误输出
no numbers,以状态 1 结束。这是对输入内容的处理,不要用assert。 - 计数的输出可以照页示例用
%zu,也可以把size_t转成int后用%d;两种写法的输出相同。
验收:
- 输入
3 -7 12 0 5,输出count=5 max=12 mean=2.6。 - 输入
-4.5 -8.25 -0.5,输出count=3 max=-0.5 mean=-4.41667。 - 输入
1 2 x 4,输出count=2 max=2 mean=1.5。 - 空输入时标准错误上出现
no numbers,退出状态为 1。
#11417
⚡5⏳3
页示例把求最大值的循环直接写在 main 里,数组长度也在同处算出。换到需要反复统计别的数组的场合,这段逻辑就得复制一遍。把它做成一个模块:
c
#include <stddef.h>
typedef enum {
STATS_OK,
STATS_EMPTY
} StatsResult;
typedef struct {
size_t count;
double max;
double min;
double mean;
} Stats;
StatsResult stats_compute(const double *values, size_t count, Stats *out);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
要求:
count == 0时返回STATS_EMPTY,并且不写*out。- 非空时只遍历数组一遍,同时求出最大值、最小值和总和;
mean用总和除以count,除法按浮点进行。 - 不要用
-DBL_MAX、DBL_MAX之类的哨兵初值:先检查count,再从values[0]开始初始化。 - 写一个驱动程序,从标准输入最多读 8 个
double(scanf("%lf", ...)),调用stats_compute并按count=... min=... max=... mean=...输出:count用%zu(或转成int用%d),另三项用%g;返回STATS_EMPTY时输出empty。
验收:
- 输入
3 -7 12 0 5,输出count=5 min=-7 max=12 mean=2.6。 - 输入
-4.5 -8.25 -0.5,输出count=3 min=-8.25 max=-0.5 mean=-4.41667。 - 空输入输出
empty。
最后回答:
- 若把
maximum初值写成0.0而不是从values[0]开始,哪一组验收数据会暴露错误?输出会错成什么? - 页示例用
-DBL_MAX作初值,在这个新接口里为什么不再需要它?返回STATS_EMPTY比返回一个特殊数值好在哪里?