无_Generic能否区分表示相同的整数类型?C11前的检测方案
区分内存表示相同的不同整数类型
背景
在标准C中,不同的整数类型可能拥有完全相同的大小、对齐要求和内存表示,但它们仍属于不同类型,转换优先级也有区别。典型例子是x86_64-linux平台上的unsigned long和unsigned long long,二者的内存特性完全一致,但属于不同类型。
C11标准下:用*_Generic*区分类型
C11引入的*_Generic特性可以直接区分这类“内存相同但类型不同”的整数类型,哪怕是typedef*定义的类型也能识别。示例代码如下:
#if 0 /* valid shell script */ ${CC-cc} -std=c11 -Wall -o "${0%.c}" "$0" && exec "${0%.c}" #endif #include <assert.h> #include <limits.h> #include <stddef.h> #if !defined __linux__ || !defined __LP64__ #error wrong platform #endif int main(void) { size_t n; assert((size_t)-1 == ULONG_MAX && (size_t)-1 == ULLONG_MAX); assert(sizeof(size_t) == sizeof(unsigned long) && sizeof(size_t) == sizeof(unsigned long long)); assert(_Alignof(size_t) == _Alignof(unsigned long) && _Alignof(size_t) == _Alignof(unsigned long long)); assert(_Generic(n, unsigned long long: !"should not be chosen", unsigned long: "should be chosen")); return 0; }
这段代码通过*_Generic*直接匹配变量的实际类型,即便内存特性完全一致,也能准确区分。
通过编译失败判断类型差异(适用于configure脚本)
还有一种方法是利用C语言的类型规则:不同类型的指针进行减法操作会触发编译错误。可以用这个特性编写检测代码,一旦编译失败就说明两个类型不同,这种方式适合在configure脚本中使用:
#include <stddef.h> int main(void) { (void)sizeof((size_t *)0 - (unsigned long long *)0); return 0; }
如果size_t和unsigned long long是不同类型,这段代码会因指针类型不兼容编译失败;若类型相同则编译通过。
技术问题解答
1. 不使用*_Generic*时能否区分表示形式相同的整数类型?
可以,但只能通过触发编译失败的方式实现——就像上面提到的指针减法检测法:利用不同类型的指针运算不兼容的规则,通过编译是否报错来判断类型是否不同。如果要求不触发编译失败,仅通过运行时或编译时无错误的方式检测,_Generic出现前没有标准方法。
2. C11标准之前,不导致编译失败且不依赖编译器特定特性的情况下,能否检测此类类型差异?
不能。在C11之前的标准里,没有提供任何可移植的、不触发编译错误的手段来区分内存表示完全相同的不同整数类型。这类类型在运行时的行为完全一致,编译阶段也没有标准的语法或特性可以在不报错的前提下识别它们的类型差异。
内容的提问来源于stack exchange,提问作者Alex Shpilkin
相关产品推荐
相关产品推荐

