types安全在C
有没有办法使C更加了解types并确保types安全?
考虑一下:
typedef unsigned cent_t; typedef unsigned dollar_t; #define DOLLAR_2_CENT(dollar) ((cent_t)(100*(dollar))) void calc(cent_t amount) { // expecting 'amount' to semantically represents cents... } int main(int argc, char* argv[]) { dollar_t amount = 50; calc(DOLLAR_2_CENT(amount)); // ok calc(amount); // raise warning return 0; }
有没有办法让上面的代码至less提醒gcc的警告?
我知道我可以使用C结构来包装unsigned
s,并达到预期的结果,我只是想知道是否有一个更优雅的方式来做到这一点。
它可以比这更多一点吗?
您需要在构build过程中使用静态分析工具来实现此目的。
例如,如果您在代码上运行PCLint,则会提供以下输出:
[Warning 632] Assignment to strong type 'cent_t' in context: arg. no. 1 [Warning 633] Assignment from a strong type 'dollar_t' in context: arg. no. 1
问题是C不会把你的两个typedefs当作独特的types,因为它们都是unsigned
types的。
有各种各样的技巧闪避这一点。 有一件事是将你的types改为枚举。 良好的编译器会对从某个枚举types到其他types的隐式转换实施更强的types警告。
即使你没有一个好的编译器,使用枚举你可以这样做:
typedef enum { FOO_CENT } cent_t; typedef enum { FOO_DOLLAR} dollar_t; #define DOLLAR_2_CENT(dollar) ((cent_t)(100*(dollar))) void calc(cent_t amount) { // expecting 'amount' to semantically represents cents... } #define type_safe_calc(amount) _Generic(amount, cent_t: calc(amount)) int main(int argc, char* argv[]) { dollar_t amount = 50; type_safe_calc(DOLLAR_2_CENT(amount)); // ok type_safe_calc(amount); // raise warning return 0; }
一个更传统的/传统的技巧是使用一个通用的结构包装器,在那里你使用“票”枚举来标记types。 例:
typedef struct { type_t type; void* data; } wrapper_t; ... cent_t my_2_cents; wrapper_t wrapper = {CENT_T, &my_2_cents}; ... switch(wrapper.type) { case CENT_T: calc(wrapper.data) ... }
好处是它可以与任何C版本一起使用。 缺点是代码和内存开销,它只允许运行时检查。
C中的别名有一个非常狭义的含义,这不是你想到的。 你可能想说“typedefing”。
答案是否定的,你不能。 无论如何,没有一个优雅的方式。 你可以为每个数字types使用一个结构体,并使用一组独立的函数来对每一个进行math运算。 除了乘法,你运气不好。 为了乘以磅英尺,你需要第三种types。 你还需要脚的types,脚的立方,秒的力量减去两个和其他types的无限数量。
如果这就是你所追求的,那么C就不是正确的语言。
编辑:这里的替代工程,即使在C89,如果你的编译器不支持_Generic
select器(很多编译器不经常,你经常被困在你的机器上安装的东西)。
您可以使用macros来简化struct
包装器的使用。
#define NEWTYPE(nty,oty) typedef struct { oty v; } nty #define FROM_NT(ntv) ((ntv).v) #define TO_NT(nty,val) ((nty){(val)}) /* or better ((nty){ .v=(val)}) if C99 */ NEWTYPE(cent_t, unsigned); NEWTYPE(dollar_t, unsigned); #define DOLLAR_2_CENT(dollar) (TO_NT(cent_t, 100*FROM_NT(dollar))) void calc(cent_t amount) { // expecting 'amount' to semantically represents cents... } int main(int argc, char* argv[]) { dollar_t amount = TO_NT(dollar_t, 50); // or alternatively {50}; calc(DOLLAR_2_CENT(amount)); // ok calc(amount); // raise warning return 0; }
你甚至比警告更强。 这是gcc 5.1的编译结果
$ gcc -O3 -Wall Edit1.c Edit1.c:在函数'main'中: Edit1.c:17:10: error :'calc'的参数1的不兼容types 计算值(量); //提出警告 ^ Edit1.c:10:6: note :expected'cent_t {aka struct}'但是参数的types是'dollar_t {aka struct}' void calc(cent_t amount); // {
这里的结果与海湾合作委员会3.4
$ gcc -O3 -Wall Edit1.c Edit1.c:在函数'main'中: Edit1.c:17:error:'calc'的参数1的不兼容types