繁体   English   中英

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

[英]How to convert a signed integer to a corresponding unsigned integer in C?

我想定义一个C宏

#define TO_UNSIGNED(x) (...)

,它采用有符号整数x (可以是: signed charshortintlonglong long或其他任何东西,甚至长于long long东西),并将x转换为相同大小的相应无符号整数类型。

可以假设有符号整数使用二进制补码表示。 因此,要转换任何值(正或负),应采用其二进制补码表示,并且应将其解释为相同大小的无符号整数。

我假设使用了一个相当现代的优化编译器,它可以消除未使用的分支,例如,如果sizeof(X) < 4 ? f(Y) : g(Z) sizeof(X) < 4 ? f(Y) : g(Z)执行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;
}

宏使用三元比较运算符?:来模拟所有已知有符号整数大小的switch语句。 (这应该捕获适当的无符号整数和从<stdint.h>键入的typedef 。它适用于表达式。它也接受浮点数,虽然不像我期望的那样。)

稍微复杂的printf显示负数被扩展到源整数的原始大小。

编辑 :OP正在查找一个宏,该宏返回与源类型长度相同的无符号类型的表达式。 上面的宏没有这样做:因为三元比较的两个替代值被提升为一个公共类型,所以宏的结果总是最大的类型,即unsigned long long

可以使用纯宏解决方案实现不同类型的分支,这样在预处理之后,编译器只能看到一种类型,但是预处理器不知道类型,因此这里不能使用sizeof ,这排除了这样的宏。

但是对于我的(弱)防御,我会说如果将宏的无符号long long结果的值赋给适当的无符号类型(即简称unsigned short),则该值永远不应被截断,因此宏可能有一些用处。

编辑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)            \
    )

_Generic选择在编译时解析,并且没有在超大int类型中生成中间结果的开销。 (一个真实世界的宏应该包含自己的无符号类型以进行空转。另请注意,我必须显式包含signed char ,只是char不起作用,即使我的字符已签名。)

它需要一个最近的编译器来实现C11或至少它的_Generic关键字,这意味着这个解决方案不是非常便携,但请参见此处

你不需要宏。 转换会自动发生。 例如:

int x = -1;
unsigned int y;

y = x;

编辑

您似乎想要一个可以从其名称推断变量类型的宏。 那是不可能的。 宏在编译阶段运行,编译器没有可用的类型信息。 因此,无论变量的类型如何,宏都必须发出相同的代码。

在类型信息可用的阶段,编译器将坚持每个表达式都具有一致的类型。 但是你要求输入的代码不一致。

您可以期待的最好的方法是自己提供类型信息。 例如:

#define TO_UNSIGNED(type, name) (unsigned type(name))

好的,既然你打算使用这个宏来隐式地将负值转换为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

当然可能需要支持更长的长度,我现在离开> 64bit只返回x本身。

看起来没有支持所有可能大小的整数的通用解决方案。

对于硬编码的类型列表,我能够使用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 ++实现。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM