pts*_*pts 3 c sign type-conversion
我想定义一个C宏
#define TO_UNSIGNED(x) (...)
Run Code Online (Sandbox Code Playgroud)
,这需要一个有符号整数x(可以是:signed char,short,int,long,long long,或其他任何东西,甚至是一种比长long long),并将其转换x为相应的无符号整数类型的大小相同.
可以假设有符号整数使用二进制补码表示.因此,要转换任何值(正或负),应采用其二进制补码表示,并且应将其解释为相同大小的无符号整数.
我假设使用了一个相当现代的优化编译器,它可以消除未使用的分支,例如,如果sizeof(X) < 4 ? f(Y) : g(Z)执行,X则不进行评估,只有一个f(Y)或者g(Z)生成和评估.
我会咬人,但我不得不说这更像是宏观黑客的精神,而不是因为我认为这样的宏是有用的.开始:
#include <stdlib.h>
#include <stdio.h>
#define TO_UNSIGNED(x) ( \
(sizeof(x) == 1) ? (unsigned char) (x) : \
(sizeof(x) == sizeof(short)) ? (unsigned short) (x) : \
(sizeof(x) == sizeof(int)) ? (unsigned int) (x) : \
(sizeof(x) == sizeof(long)) ? (unsigned long) (x) : \
(unsigned long long) (x) \
)
// Now put the macro to use ...
short minus_one_s()
{
return -1;
}
long long minus_one_ll()
{
return -1LL;
}
int main()
{
signed char c = -1;
short s = -1;
int i = -1;
long int l = -1L;
long long int ll = -1LL;
printf("%llx\n", (unsigned long long) TO_UNSIGNED(c));
printf("%llx\n", (unsigned long long) TO_UNSIGNED(s));
printf("%llx\n", (unsigned long long) TO_UNSIGNED(i));
printf("%llx\n", (unsigned long long) TO_UNSIGNED(l));
printf("%llx\n", (unsigned long long) TO_UNSIGNED(ll));
printf("%llx\n", (unsigned long long) TO_UNSIGNED(minus_one_s()));
printf("%llx\n", (unsigned long long) TO_UNSIGNED(minus_one_ll()));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
宏使用三元比较运算符?:来模拟所有已知有符号整数大小的switch语句.(这应该捕获适当的无符号整数和typedef'd typed from <stdint.h>.它适用于表达式.它也接受浮点数,虽然不像我期望的那样.)
稍微复杂的printfs表明负数被扩展到源整数的原始大小.
编辑:OP正在查找一个宏,该宏返回与源类型长度相同的无符号类型的表达式.上面的宏没有这样做:因为三元比较的两个替代值被提升为一个公共类型,所以宏的结果总是最大的类型,即unsigned long long.
可以使用纯宏解决方案实现不同类型的分支,这样在预处理之后,编译器只能看到一种类型,但是预处理器不知道类型,因此sizeof不能在这里使用,这排除了这样的宏.
但是对于我的(弱)防御,我会说如果将宏的无符号long long结果的值赋给适当的无符号类型(即简称unsigned short),则该值永远不应被截断,因此宏可能有一些用处.
编辑II:既然我_Generic在另一个问题中偶然发现了C11 关键字(并且安装了支持它的编译器),我可以提出一个可行的解决方案:以下宏实际上 返回了正确的类型正确的值:
#define TO_UNSIGNED(x) _Generic((x), \
char: (unsigned char) (x), \
signed char: (unsigned char) (x), \
short: (unsigned short) (x), \
int: (unsigned int) (x), \
long: (unsigned long) (x), \
long long: (unsigned long long) (x), \
default: (unsigned int) (x) \
)
Run Code Online (Sandbox Code Playgroud)
该_Generic选择在编译时被解决,并且不具有在一个超大整型产生中间结果的开销.(一个真实世界的宏应该包括无符号类型自己进行空转.还要注意我必须signed char明确包含,只是char没有工作,即使我的字符已签名.)
它需要一个最近的编译器来实现C11或至少它的_Generic关键字,这意味着这个解决方案不是非常便携,但请参阅此处.