Mat*_*ner 265 c max min c-preprocessor
在C中的位置MIN和MAX定义,如果有的话?
实现这些的最佳方式是什么,尽可能通用和安全?(首选编译器扩展/内置主流编译器.)
Dav*_*nco 352
在C中的位置
MIN和MAX定义,如果有的话?
他们不是.
实现这些的最佳方法是什么,尽可能通用和类型安全(首选编译器扩展/内置主流编译器).
作为功能.我不会使用宏#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)),特别是如果您计划部署代码.无论是写自己的,使用类似标准fmax或fmin,或使用固定宏海湾合作委员会的typeof(你的类型安全奖励太):
#define max(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
Run Code Online (Sandbox Code Playgroud)
每个人都说"哦,我知道双重评估,这没有问题",几个月后,你将在几个小时内调试最愚蠢的问题.
注意使用__typeof__而不是typeof:
如果您正在编写包含在ISO C程序中必须工作的头文件,请编写
__typeof__而不是typeof.
Mik*_*kel 85
它也在GNU libc(Linux)和FreeBSD版本的sys/param.h中提供,并且具有dreamlax提供的定义.
关于Debian:
$ uname -sr
Linux 2.6.11
$ cat /etc/debian_version
5.0.2
$ egrep 'MIN\(|MAX\(' /usr/include/sys/param.h
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
$ head -n 2 /usr/include/sys/param.h | grep GNU
This file is part of the GNU C Library.
Run Code Online (Sandbox Code Playgroud)
在FreeBSD上:
$ uname -sr
FreeBSD 5.5-STABLE
$ egrep 'MIN\(|MAX\(' /usr/include/sys/param.h
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
Run Code Online (Sandbox Code Playgroud)
源存储库在这里:
dan*_*n04 70
有一个std::min和std::maxC++,但AFAIK,在C标准库中没有等价物.您可以使用宏来自己定义它们
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
Run Code Online (Sandbox Code Playgroud)
但如果你写一些像这样的东西会导致问题MAX(++a, ++b).
Lun*_*din 21
避免使用非标准编译器扩展,并将其作为纯标准C(ISO 9899:2011)中完全类型安全的宏实现.
解
#define GENERIC_MAX(x, y) ((x) > (y) ? (x) : (y))
#define ENSURE_int(i) _Generic((i), int: (i))
#define ENSURE_float(f) _Generic((f), float: (f))
#define MAX(type, x, y) \
(type)GENERIC_MAX(ENSURE_##type(x), ENSURE_##type(y))
Run Code Online (Sandbox Code Playgroud)
用法
MAX(int, 2, 3)
Run Code Online (Sandbox Code Playgroud)
说明
宏MAX根据type参数创建另一个宏.如果针对给定类型实现此控制宏,则用于检查两个参数的类型是否正确.如果type不支持,则会出现编译器错误.
如果x或y的类型不正确,则ENSURE_宏中将出现编译器错误.如果支持更多类型,可以添加更多此类宏.我假设只使用算术类型(整数,浮点数,指针等),而不是结构或数组等.
如果所有类型都正确,则将调用GENERIC_MAX宏.每个宏参数都需要额外的括号,作为编写C宏时的常用标准预防措施.
然后是C中隐式类型促销的常见问题.?:运算符将第2和第3个操作数相互平衡.例如,结果GENERIC_MAX(my_char1, my_char2)将是一个int.为了防止宏执行此类潜在危险类型的促销,使用了最终类型转换为预期类型.
合理
我们希望宏的两个参数都是相同的类型.如果其中一个类型不同,则宏不再是类型安全的,因为类似的运算符?:将产生隐式类型促销.并且因为它确实如此,我们总是需要将最终结果转换回预期类型,如上所述.
只有一个参数的宏可以用更简单的方式编写.但是,如果有2个或更多参数,则需要包含额外的类型参数.因为这样的事情很遗憾是不可能的:
// this won't work
#define MAX(x, y) \
_Generic((x), \
int: GENERIC_MAX(x, ENSURE_int(y)) \
float: GENERIC_MAX(x, ENSURE_float(y)) \
)
Run Code Online (Sandbox Code Playgroud)
问题是如果上面的宏被调用为MAX(1, 2)两个int,它仍然会尝试宏扩展_Generic关联列表的所有可能场景.所以ENSURE_float宏也会得到扩展,即使它不相关int.由于该宏有意只包含该float类型,因此代码将无法编译.
为了解决这个问题,我在预处理器阶段使用##运算符创建了宏名称,这样就不会意外地扩展宏.
例子
#include <stdio.h>
#define GENERIC_MAX(x, y) ((x) > (y) ? (x) : (y))
#define ENSURE_int(i) _Generic((i), int: (i))
#define ENSURE_float(f) _Generic((f), float: (f))
#define MAX(type, x, y) \
(type)GENERIC_MAX(ENSURE_##type(x), ENSURE_##type(y))
int main (void)
{
int ia = 1, ib = 2;
float fa = 3.0f, fb = 4.0f;
double da = 5.0, db = 6.0;
printf("%d\n", MAX(int, ia, ib)); // ok
printf("%f\n", MAX(float, fa, fb)); // ok
//printf("%d\n", MAX(int, ia, fa)); compiler error, one of the types is wrong
//printf("%f\n", MAX(float, fa, ib)); compiler error, one of the types is wrong
//printf("%f\n", MAX(double, fa, fb)); compiler error, the specified type is wrong
//printf("%f\n", MAX(float, da, db)); compiler error, one of the types is wrong
//printf("%d\n", MAX(unsigned int, ia, ib)); // wont get away with this either
//printf("%d\n", MAX(int32_t, ia, ib)); // wont get away with this either
return 0;
}
Run Code Online (Sandbox Code Playgroud)
Bre*_*ale 21
由于最近的发展,这是一个迟到的答案.由于OP接受了依赖于非便携式GCC(和clang)扩展的答案typeof- 或者__typeof__用于"干净"ISO C - 从gcc-4.9开始提供了更好的解决方案.
#define max(x,y) ( \
{ __auto_type __x = (x); __auto_type __y = (y); \
__x > __y ? __x : __y; })
Run Code Online (Sandbox Code Playgroud)
这个扩展的明显好处是每个宏参数只扩展一次,与__typeof__解决方案不同.
__auto_type是C++ 11的有限形式auto.它不能(或不应该?)在C++代码中使用,尽管auto在使用C++ 11时没有充分的理由不使用高级类型的推理功能.
也就是说,我认为当宏包含在extern "C" { ... }作用域中时,使用此语法没有问题; 例如,来自C头.AFAIK,这个扩展没有找到它的方式信息clang
Gab*_*les 20
@David Titarenco 把它钉在这里,但让我至少把它清理一下,让它看起来更好看,并同时显示两者min() , max()以便从这里复制和粘贴更容易。:)
2020 年 4 月 25 日更新:我还添加了第 3 节,以展示如何使用 C++ 模板来实现这一点,作为对那些同时学习 C 和 C++ 或从一种转换到另一种的人的有价值的比较。我已尽力做到彻底、真实和正确,使这个答案成为我可以一次又一次返回的规范参考,我希望你发现它和我一样有用。
这种技术被普遍使用的那些谁知道如何正确地使用它,做事情的“事实”的方式,并罚款的使用,如果使用得当,但备受推崇的越野车(想想:双评副作用),如果你曾经传递包括变量赋值在内的表达式以进行比较:
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN(a,b) ((a) < (b) ? (a) : (b))
Run Code Online (Sandbox Code Playgroud)
这种技术避免了上述“双重评估”的副作用和错误,因此被认为是更优越、更安全和“更现代”的GCC C 方法来做到这一点。期望它可以与 gcc 和 clang 编译器一起使用,因为 clang 在设计上与 gcc 兼容(请参阅本答案底部的 clang 注释)。
但是:仍然要注意“变量阴影”效果,因为语句表达式显然是内联的,因此没有自己的局部变量作用域!
#define max(a,b) \
({ \
__typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; \
})
#define min(a,b) \
({ \
__typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; \
})
Run Code Online (Sandbox Code Playgroud)
请注意,在 gcc 语句表达式中,代码块中的最后一个表达式是从表达式“返回”的内容,就好像它是从函数返回的一样。GCC 的文档是这样说的:
复合语句中的最后一件事应该是一个表达式,后跟一个分号;此子表达式的值用作整个构造的值。(如果您在大括号中最后使用某种其他类型的语句,则该构造的类型为 void,因此实际上没有值。)
C++ 注意:如果使用 C++,则可能建议将模板用于这种类型的构造,但我个人不喜欢模板,无论如何可能会在 C++ 中使用上述构造之一,因为我经常在嵌入式 C++ 中使用并更喜欢 C 样式。
本节于 2020 年 4 月 25 日添加:
在过去的几个月里,我一直在做大量的 C++,并且在 C++ 社区中选择模板而不是宏的压力非常大。结果,我在使用模板方面变得越来越好,并希望在此处放入 C++ 模板版本以确保完整性并使其成为更规范和更全面的答案。
以下是C++ 中的基本函数模板版本max()和min()可能的样子:
template <typename T>
T max(T a, T b)
{
return a > b ? a : b;
}
template <typename T>
T min(T a, T b)
{
return a < b ? a : b;
}
Run Code Online (Sandbox Code Playgroud)
在此处阅读有关 C++ 模板的更多信息:维基百科:模板 (C++)。
但是,max()和min()都已经是 C++ 标准库的一部分,位于<algorithm>头文件 ( #include <algorithm>) 中。在 C++ 标准库中,它们的定义与上面的略有不同。默认原型std::max<>()和std::min<>(),例如,在C ++ 14,看着他们在正上方cplusplus.com链接原型,主要有:
template <class T>
constexpr const T& max(const T& a, const T& b);
template <class T>
constexpr const T& min(const T& a, const T& b);
Run Code Online (Sandbox Code Playgroud)
请注意,关键字typename是一个别名class(所以它们的用法是相同的,你是否说<typename T>或<class T>),因为它的C ++模板的发明后来经过确认,该模板类型可能是一个普通类型(int,float,等),而不是只类类型。
在这里您可以看到输入类型和返回类型都是const T&,这意味着“对类型的常量引用T”。这意味着输入参数和返回值是通过引用传递而不是通过值传递。这就像传递指针一样,对于大型类型(例如类对象)更有效。constexpr函数的一部分修改了函数本身,并表明该函数必须能够在编译时被评估(至少如果提供了constexpr输入参数),但如果不能在编译时被评估,那么它默认回运行时评估,就像任何其他正常功能一样。
constexprC++ 函数的编译时方面使它有点像 C 宏,因为如果constexpr函数的编译时评估是可能的,它将在编译时完成,与 aMIN()或MAX()宏替换可能相同也可以在 C 或 C++ 编译时进行全面评估。有关此 C++ 模板信息的其他参考,请参见下文。
来自维基百科的Clang 注释:
[Clang] 旨在作为 GNU 编译器集合 (GCC) 的直接替代品,支持其大部分编译标志和非官方语言扩展。
dre*_*lax 19
我不认为它们是标准化的宏.有浮点已经标准化的功能,fmax并且fmin(和fmaxf对花车,和fmaxl长期双打).
只要您了解副作用/双重评估的问题,就可以将它们实现为宏.
#define MAX(a,b) ((a) > (b) ? a : b)
#define MIN(a,b) ((a) < (b) ? a : b)
Run Code Online (Sandbox Code Playgroud)
在大多数情况下,您可以将其留给编译器来确定您要执行的操作并尽可能地优化它.虽然这在使用时会引起问题MAX(i++, j++),但我怀疑一次性检查增量值的最大值是非常需要的.先增加,然后检查.
Mat*_*ner 11
我写了这个版本,对于MSVC,GCC,C和C++工程.
#if defined(__cplusplus) && !defined(__GNUC__)
# include <algorithm>
# define MIN std::min
# define MAX std::max
//# define TMIN(T, a, b) std::min<T>(a, b)
//# define TMAX(T, a, b) std::max<T>(a, b)
#else
# define _CHOOSE2(binoper, lexpr, lvar, rexpr, rvar) \
({ \
decltype(lexpr) lvar = (lexpr); \
decltype(rexpr) rvar = (rexpr); \
lvar binoper rvar ? lvar : rvar; \
})
# define _CHOOSE_VAR2(prefix, unique) prefix##unique
# define _CHOOSE_VAR(prefix, unique) _CHOOSE_VAR2(prefix, unique)
# define _CHOOSE(binoper, lexpr, rexpr) \
_CHOOSE2( \
binoper, \
lexpr, _CHOOSE_VAR(_left, __COUNTER__), \
rexpr, _CHOOSE_VAR(_right, __COUNTER__) \
)
# define MIN(a, b) _CHOOSE(<, a, b)
# define MAX(a, b) _CHOOSE(>, a, b)
#endif
Run Code Online (Sandbox Code Playgroud)
如果你需要min/max以避免昂贵的分支,你不应该使用三元运算符,因为它会编译成跳转.下面的链接描述了一种在没有分支的情况下实现最小/最大功能的有用方法.
http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
看起来Windef.h(a la #include <windows.h>)有max和min(小写)宏,它们也遭受“双重评估”困难,但它们是为那些不想重新推出自己的宏而存在的:)
值得指出的是,我认为如果您定义min并max使用三元运算,例如
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
Run Code Online (Sandbox Code Playgroud)
然后为特殊情况获得相同的结果fmin(-0.0,0.0),fmax(-0.0,0.0)您需要交换参数
fmax(a,b) = MAX(a,b)
fmin(a,b) = MIN(b,a)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
670996 次 |
| 最近记录: |