我想定义一个C宏
#define TO_UNSIGNED(x) (...)
,带有符号整数x
(可以是:signed char
,short
,int
,long
,long long
或其他任何内容否则,甚至比long long
更长的东西),它会将x
转换为相同大小的相应无符号整数类型。
可以假设有符号整数使用二进制补码表示。因此,要转换任何值(正数或负数),应采用其二进制补码二进制表示,并且应将其解释为相同大小的无符号整数。
我假设使用了一个相当现代的优化编译器,它可以消除未使用的分支,例如如果执行sizeof(X) < 4 ? f(Y) : g(Z)
,则不会评估X
,并且只生成并评估f(Y)
或g(Z)
中的一个。
答案 0 :(得分:7)
我会咬人,但我不得不说这更符合宏观黑客的精神,而不是因为我认为这样的宏是有用的。这是:
#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;
}
宏使用三元比较运算符?:
来模拟所有已知有符号整数大小的switch语句。 (这应该捕获适当的无符号整数,并且typedef
'也可以从<stdint.h>
输入。它适用于表达式。它也接受浮点数,尽管不像我期望的那样。)
有些错综复杂的printf
表明负数被扩展为源整数的原始大小。
编辑:OP正在寻找一个宏,该宏返回与源类型长度相同的无符号类型的表达式。上面的宏没有这样做:因为三元比较的两个替代值被提升为一个公共类型,宏的结果将始终是最大大小的类型,即unsigned long long
。
使用纯宏解决方案可能可以实现不同类型的分支,这样在预处理之后,编译器只能看到一种类型,但预处理器不知道类型,因此sizeof
不能在这里使用,这排除了这样一个宏。
但是对于我的(弱)防御,我会说如果将宏的无符号long long结果的值赋给适当的无符号类型(即简称unsigned short),则该值永远不应被截断,所以宏可能会一些使用。
编辑II :现在我偶然发现了another question中的C11 _Generic
关键字(并安装了支持它的编译器),我可以提供一个工作解决方案:以下宏确实返回正确的值,其类型正确:
#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) \
)
_Generic
选择在编译时解析,并且没有在超大int类型中生成中间结果的开销。 (一个真实世界的宏应该包括无符号类型自己进行空转。另请注意,我必须明确包含signed char
,只是char
不起作用,即使我的字符已签名。)
它需要最近的编译器实现C11或至少它的_Generic
关键字,这意味着此解决方案不是非常便携,但see here。
答案 1 :(得分:3)
您不需要宏。转换会自动发生。 E.g:
int x = -1;
unsigned int y;
y = x;
修改强>
您似乎想要一个可以从其名称推断变量类型的宏。那是不可能的。宏在编译阶段运行,编译器没有可用的类型信息。因此,无论变量的类型如何,宏都必须发出相同的代码。
在类型信息可用的阶段,编译器将坚持每个表达式都具有一致的类型。但是你要求输入的代码不一致。
您最希望的是自己提供类型信息。 E.g:
#define TO_UNSIGNED(type, name) (unsigned type(name))
答案 2 :(得分:2)
好的,既然你打算使用这个宏来隐式地将负值转换为2的补码,我想我们可以通过以下方式解决它:
#include "stdio.h"
#include "stdint.h"
#define TO_UNSIGNED(x) ( \
(sizeof(x) == 1 ? (uint8_t)x : \
(sizeof(x) <= 2 ? (uint16_t)x : \
(sizeof(x) <= 4 ? (uint32_t)x : \
(sizeof(x) <= 8 ? (uint64_t)x : \
x \
)))))
int main () {
char a = -4;
int b = -4;
printf ("TO_UNSIGNED(a) = %u\n", TO_UNSIGNED(a));
printf ("TO_UNSIGNED(b) = %u\n", TO_UNSIGNED(b));
return 0;
}
输出:
TO_UNSIGNED(a) = 252
TO_UNSIGNED(b) = 4294967292
当然可能需要支持更长的长度,我离开了&gt; 64位现在只返回x
。
答案 3 :(得分:0)
看起来没有支持所有可能大小的整数的通用解决方案。
对于硬编码的类型列表,我能够使用C中的__builtin_choose_expr
和C ++中的重载函数使其工作。以下是解决方案:https://github.com/pts/to-unsigned/blob/master/to_unsigned.h
相关的C代码如下所示:
#define TO_UNSIGNED(x) ( \
__builtin_choose_expr(__builtin_types_compatible_p(__typeof(x), unsigned char), (unsigned char)(x), \
__builtin_choose_expr(__builtin_types_compatible_p(__typeof(x), char), (unsigned char)(x), \
__builtin_choose_expr(sizeof(x) == sizeof(char), (unsigned char)(x), \
__builtin_choose_expr(__builtin_types_compatible_p(__typeof(x), unsigned short), (unsigned short)(x), \
__builtin_choose_expr(__builtin_types_compatible_p(__typeof(x), short), (unsigned short)(x), \
__builtin_choose_expr(sizeof(x) == sizeof(short), (unsigned short)(x), \
__builtin_choose_expr(__builtin_types_compatible_p(__typeof(x), unsigned), (unsigned)(x), \
__builtin_choose_expr(__builtin_types_compatible_p(__typeof(x), int), (unsigned)(x), \
__builtin_choose_expr(sizeof(x) == sizeof(int), (unsigned)(x), \
__builtin_choose_expr(__builtin_types_compatible_p(__typeof(x), unsigned long), (unsigned long)(x), \
__builtin_choose_expr(__builtin_types_compatible_p(__typeof(x), long), (unsigned long)(x), \
__builtin_choose_expr(sizeof(x) == sizeof(long), (unsigned long)(x), \
__extension__ __builtin_choose_expr(__builtin_types_compatible_p(__typeof(x), unsigned long long), (unsigned long long)(x), \
__extension__ __builtin_choose_expr(__builtin_types_compatible_p(__typeof(x), long long), (unsigned long long)(x), \
__extension__ __builtin_choose_expr(sizeof(x) == sizeof(long long), (unsigned long)(x), \
(void)0))))))))))))))))
而不是__builtin_choose_expr
+ __builtin_types_compatible_p
,等效的_Generic
构造也可以与支持它的编译器一起使用,从C11开始。
C ++ 11有std::make_unsigned,它在libstdc ++中的实现显式枚举了它所知道的整数类型,类似于TO_UNSIGNED
的C ++实现。