HOWTO · C
C 语言中的 MIN 和 MAX 函数
可以使用宏或循环来实现 C 语言中的 MIN 和 MAX 函数。
本页内容
在 C 中查找两个值中的较小值或较大值,可以定义 MIN 或 MAX 宏,也可以编写函数。标准 C 不提供通用的内置 MIN 和 MAX 函数。对于数组,可以用第一个元素初始化结果,再扫描其余元素。
C 语言内置了 MIN 和 MAX 吗?
C 标准库没有定义通用的 MIN 和 MAX 函数。项目通常会将这些名称定义为宏或普通函数。如果参数带有副作用,函数更容易推理;对于能够安全求值的值,简单宏则更简洁。
#define 指令用于创建预处理器宏。预处理器遇到宏调用时,会在编译器处理程序之前将其替换为替换文本。函数调用则会调用已编译的代码,并按照 C 的函数调用规则求值参数。
为两个值定义 MIN 和 MAX 宏
对于两个不带副作用的整数表达式,应将每个参数和整个替换表达式都括起来。这样,调用者传入 a + b 之类的表达式时,也能保持预期的运算分组。
#include <stdio.h>
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
int main(void) {
printf("Min = %d\n", MIN(0, 10));
printf("Max = %d\n", MAX(0, 10));
return 0;
}
Min = 0
Max = 10
条件运算符 ?: 会在比较结果为真时选择 ? 后面的表达式,否则选择 : 后面的表达式。括号可以处理运算符优先级,但无法避免重复求值:在 MIN(a, b) 中,被选中的参数会再次出现在替换文本里。不要传入 i++ 或会改变状态的函数调用等参数。这种宏适合简单的值;如果参数可能带有副作用,请改用函数。
使用 GNU C 宏让参数只求值一次
GNU C 支持语句表达式和 typeof(本例中写作 __typeof__)。下面的宏会将每个参数保存到临时变量中,再比较这些值。每个参数表达式在初始化临时变量时只求值一次。这属于 GNU C 扩展,并非可移植的 ISO C;可能还需要启用 GNU 扩展的编译器选项。
#include <stdio.h>
#define MAX(x, y) \
({ \
__typeof__(x) _max_x = (x); \
__typeof__(y) _max_y = (y); \
_max_x > _max_y ? _max_x : _max_y; \
})
#define MIN(x, y) \
({ \
__typeof__(x) _min_x = (x); \
__typeof__(y) _min_y = (y); \
_min_x < _min_y ? _min_x : _min_y; \
})
int main(void) {
int a = 4;
int b = 9;
printf("Min = %d\n", MIN(a++, b));
printf("Max = %d\n", MAX(a, b));
return 0;
}
Min = 4
Max = 9
这种写法适用于可用 < 或 > 比较的普通标量表达式。它并不是适用于所有类型的泛型函数:类型不兼容、没有合理排序关系的指针,以及不支持这些运算符的类型,都需要采用其他设计。条件表达式也遵循 C 的常规类型转换规则。若要编写可移植代码,请使用类型明确的函数,或为支持的类型分别编写函数。
查找数组中的最小值和最大值
遍历数组时,保存当前的最小值和最大值,并在遇到更小或更大的元素时更新它们。用第一个元素初始化两者,确保结果来自数组中的实际值。示例使用与原数组示例相同的数据,只扫描数组一遍。
#include <stdio.h>
#include <stddef.h>
void find_min_max(const int values[], size_t length, int *minimum, int *maximum) {
*minimum = values[0];
*maximum = values[0];
for (size_t i = 1; i < length; ++i) {
if (values[i] < *minimum) {
*minimum = values[i];
}
if (values[i] > *maximum) {
*maximum = values[i];
}
}
}
int main(void) {
int values[] = {6, 3, 15, 9, 10, 50, 99, 11};
size_t length = sizeof(values) / sizeof(values[0]);
int minimum;
int maximum;
find_min_max(values, length, &minimum, &maximum);
printf("Max number = %d\n", maximum);
printf("Min number = %d\n", minimum);
return 0;
}
Max number = 99
Min number = 3
此函数要求数组非空,且结果指针有效。调用者需要传入数组长度,因为数组形参会调整为指针;在 find_min_max 中,sizeof(values) 测量的是指针,而不是原数组。这里在 main 中计算元素个数,此时 values 仍是数组:用数组的总大小除以单个元素的大小即可。
处理浮点数据时,数组、最小值和最大值变量以及函数参数应使用一致的类型。printf 的转换说明符应与提升后的参数类型匹配;对于 double,使用 %f。如果函数需要接受多种数值类型,应编写类型合适的函数,或设计周全的抽象,而不是只把 int 改成 float。