简体   繁体   English

如何将有符号整数转换为C中对应的无符号整数?

[英]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 charshortintlonglong 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.

相关问题 C-有符号和无符号整数 - C - Signed and Unsigned integer C:如何将2 32位无符号整数的结果保存为有符号整数 - C: How to save the result of 2 32 bit unsigned integer into a signed integer 有符号整数类型及其对应的无符号整数类型 - Signed integer type and its corresponding unsigned integer type 如何使用Neon SIMD将unsigned char转换为有符号整数 - How to convert unsigned char to signed integer using Neon SIMD 有符号整数类型的大小可以与C / C ++中对应的无符号类型的大小不同吗? - Can the size of a signed integer type differ from that of its corresponding unsigned type in C/C++? 如何在 C 中使用有符号或无符号整数进行位映射? - How to use signed or unsigned integer for bit mapping in C? c 编译器如何处理无符号和有符号整数? 为什么无符号和有符号算术运算的汇编代码相同? - how does c compiler handle unsigned and signed integer? Why the assembly code for unsigned and signed arithmetic operation are the same? Tilde C unsigned vs signed integer - Tilde C unsigned vs signed integer 有符号和无符号整数乘法 - Signed & unsigned integer multiplication 将 8 位有符号整数转换为无符号整数,然后转换为 int32 - Convert 8 bit signed integer to unsigned and then convert to int32
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM