[英]How to convert a signed integer to a corresponding unsigned integer in C?
I'd like to define a C macro 我想定义一个C宏
#define TO_UNSIGNED(x) (...)
, which takes a signed integer x
(can be: signed char
, short
, int
, long
, long long
, or anything else, even something longer than a long long
), and it converts x
to the corresponding unsigned integer type of the same size. ,它采用有符号整数x
(可以是: signed char
, short
, int
, long
, long long
或其他任何东西,甚至长于long long
东西),并将x
转换为相同大小的相应无符号整数类型。
It's OK to assume that signed integers use the two's complement representation. 可以假设有符号整数使用二进制补码表示。 So to convert any value (positive or negative), its two's complement binary representation should be taken, and that should be interpreted as an unsigned integer of the same size. 因此,要转换任何值(正或负),应采用其二进制补码表示,并且应将其解释为相同大小的无符号整数。
I'm assuming that a reasonably modern, optimizing compiler is used which can eliminate unused branches, eg if sizeof(X) < 4 ? f(Y) : g(Z)
我假设使用了一个相当现代的优化编译器,它可以消除未使用的分支,例如,如果sizeof(X) < 4 ? f(Y) : g(Z)
sizeof(X) < 4 ? f(Y) : g(Z)
is executed, then X
is not evaluated, and only one of f(Y)
or g(Z)
is generated and evaluated. sizeof(X) < 4 ? f(Y) : g(Z)
执行sizeof(X) < 4 ? f(Y) : g(Z)
,然后不评估X
,并且仅生成并评估f(Y)
或g(Z)
中的一个。
I'll bite, but I have to say it's more in the spirit of macro hacking, not because I think such a macro is useful. 我会咬人,但我不得不说这更像是宏观黑客的精神,而不是因为我认为这样的宏是有用的。 Here goes: 开始:
#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;
}
The macro uses the ternary comparison operator ?:
to emulate a switch statement for all known signed integer sizes. 宏使用三元比较运算符?:
来模拟所有已知有符号整数大小的switch语句。 (This should catch the appropriate unsigned integers and the typedef
'd typed from <stdint.h>
, too. It works with expressions. It also accepts floats, although not quite as I'd expect.) (这应该捕获适当的无符号整数和从<stdint.h>
键入的typedef
。它适用于表达式。它也接受浮点数,虽然不像我期望的那样。)
The somewhat convoluted printf
s show that the negative numbers are expanded to the native size of the source integer. 稍微复杂的printf
显示负数被扩展到源整数的原始大小。
Edit : The OP is looking for a macro that returns an expression of the unsigned type of the same length as the source type. 编辑 :OP正在查找一个宏,该宏返回与源类型长度相同的无符号类型的表达式。 The above macro doesn't do that: Because the two alternative values of the ternary comparison are promoted to a common type, the result of the macro will always be the type of the greatest size, which is unsigned long long
. 上面的宏没有这样做:因为三元比较的两个替代值被提升为一个公共类型,所以宏的结果总是最大的类型,即unsigned long long
。
Branches of different types could probably be achieved with a pure macro solution, such that after preprocessing, the compiler only sees one type, but the preprocessor doesn't know about types, so sizeof
cannot be used here, which rules out such a macro. 可以使用纯宏解决方案实现不同类型的分支,这样在预处理之后,编译器只能看到一种类型,但是预处理器不知道类型,因此这里不能使用sizeof
,这排除了这样的宏。
But to my (weak) defense, I'll say that if the value of the unsigned long long result of the macro is assigned to the appropriate unsigned type (ie unsigned short for short), the value should never be truncated, so the macro might have some use. 但是对于我的(弱)防御,我会说如果将宏的无符号long long结果的值赋给适当的无符号类型(即简称unsigned short),则该值永远不应被截断,因此宏可能有一些用处。
Edit II : Now that I've stumbled upon the C11 _Generic
keyword in another question (and have installed a compiler that supports it), I can present a working solution: The following macro really returns the correct value with the correct type: 编辑II :既然我在另一个问题中偶然发现了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) \
)
The _Generic
selection is resolved at compile time and doesn't have the overhead of producing intermediate results in an oversized int type. _Generic
选择在编译时解析,并且没有在超大int类型中生成中间结果的开销。 (A real-world macro should probably include the unsigned types themself for a null-cast. Also note that I had to include signed char
explicitly, just char
didn't work, even though my chars are signed.) (一个真实世界的宏应该包含自己的无符号类型以进行空转。另请注意,我必须显式包含signed char
,只是char
不起作用,即使我的字符已签名。)
It requires a recent compiler that implements C11 or at least its _Generic
keyword, which means this solution is not very portable, though, see here . 它需要一个最近的编译器来实现C11或至少它的_Generic
关键字,这意味着这个解决方案不是非常便携,但请参见此处 。
You don't need a macro. 你不需要宏。 The conversion happens automatically. 转换会自动发生。 Eg: 例如:
int x = -1;
unsigned int y;
y = x;
EDIT 编辑
You seem to want a macro that can infer the type of a variable from its name. 您似乎想要一个可以从其名称推断变量类型的宏。 That is impossible. 那是不可能的。 Macros are run at a stage of compilation where the compiler doesn't have the type information available. 宏在编译阶段运行,编译器没有可用的类型信息。 So the macro must emit the same code regardless of the variable's type. 因此,无论变量的类型如何,宏都必须发出相同的代码。
At the stage when type information becomes available, the compiler will insist that every expression has a consistent type. 在类型信息可用的阶段,编译器将坚持每个表达式都具有一致的类型。 But you're asking for code that is inconsistently typed. 但是你要求输入的代码不一致。
The best you can hope for is to supply the type information yourself. 您可以期待的最好的方法是自己提供类型信息。 Eg: 例如:
#define TO_UNSIGNED(type, name) (unsigned type(name))
Ok, since you intend to use this macro to implicitly convert negative values to their 2's complement counterparts, I think we can address it the following way: 好的,既然你打算使用这个宏来隐式地将负值转换为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;
}
Output: 输出:
TO_UNSIGNED(a) = 252
TO_UNSIGNED(b) = 4294967292
Of course support for further lengths may be required, I left the > 64bit to just return x
itself for now. 当然可能需要支持更长的长度,我现在离开> 64bit只返回x
本身。
It looks like there is no generic solution which supports integers of all possible sizes. 看起来没有支持所有可能大小的整数的通用解决方案。
For a hardcoded list of types, I was able to make it work using __builtin_choose_expr
in C and overloaded function in C++. 对于硬编码的类型列表,我能够使用C中的__builtin_choose_expr
和C ++中的重载函数使其工作。 Here is the solution: https://github.com/pts/to-unsigned/blob/master/to_unsigned.h 这是解决方案: https : //github.com/pts/to-unsigned/blob/master/to_unsigned.h
The relevant C code looks like this: 相关的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))))))))))))))))
Instead of __builtin_choose_expr
+ __builtin_types_compatible_p
, the equivalent _Generic
construct can also be used with compilers that support it, starting from C11. 而不是__builtin_choose_expr
+ __builtin_types_compatible_p
,等效的_Generic
构造也可以与支持它的编译器一起使用,从C11开始。
C++11 has std::make_unsigned , and its implementation in libstdc++ explicitly enumerates the integer types it knows about, similar to how my C++ implementation of TO_UNSIGNED
does. C ++ 11有std :: make_unsigned ,它在libstdc ++中的实现显式枚举了它所知道的整数类型,类似于我的TO_UNSIGNED
C ++实现。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.