首页 > 解决方案 > 处理有符号整数数组,就好像它包含无符号值一样

问题描述

我继承了一些旧代码,假设 anint可以存储从 -2 31到 2^ 31 -1 的值,溢出只是环绕,并且符号位是高位。换句话说,该代码应该使用uint32_t,但事实并非如此。我想修复此代码以使用uint32_t.

困难在于代码是作为源代码分发的,我不允许更改外部接口。我有一个适用于数组的函数int。它在内部做的是自己的业务,但是int暴露在接口中。简而言之,接口是:

struct data {
    int a[10];
};
void frobnicate(struct data *param);

我想更改int a[10]uint32_t a[10],但我不允许修改 的定义struct data

我可以使代码在内部uint32_tunsigned内部工作:

struct internal_data {
    unsigned a[10];
};
void frobnicate(struct data *param) {
    struct internal_data *internal = (struct internal_data *)param;
    // ... work with internal ...
}

然而,这实际上不是正确的 C,因为它在指向不同类型的指针之间进行转换。

有没有办法可以添加编译时保护,这样对于那些不是“老派” 32 位的少数人来说int,代码不会构建?如果int小于 32 位,则代码无论如何都不会工作。对于绝大多数用户来说,代码应该构建,并且以一种告诉编译器不要做溢出int计算的“奇怪”事情的方式。

我分发源代码,人们可以将它与他们选择的任何编译器一起使用,因此特定于编译器的技巧不相关。

我至少要补充

#if INT_MIN + 1 != -0x7fffffff
#error "This code only works with 32-bit two's complement int"
#endif

有了这个守卫,上面的演员阵容会出什么问题?有没有一种可靠的方法来操作int数组,就像它的元素一样unsigned,而不复制数组?

总之:

标签: carrayscastingsigned

解决方案


  • 然而,这实际上不是正确的 C,因为它在指向不同类型的指针之间进行转换。

    实际上,您不能进行此类转换,因为这两种结构类型不兼容。但是,您可以使用以下解决方法:

    typedef union
     {
       struct   data;
       uint32_t array[10];
     } internal_t;
    
     ...
    
     void frobnicate(struct data *param) {
         internal_t* internal = (internal_t*)param;
         ...
    

    如果您可以更改原始结构声明但不能更改其成员名称,另一种选择是使用 C11 匿名联合:

     struct data {
       union {
         int        a[10];
         uint32_t u32[10];
       }
     };
    

    这意味着用户代码访问foo.a不会中断。但是您需要 C11 或更高版本。

    或者,您可以使用 a直接uint32_t*访问int[10]。这也是明确定义的,因为uint32_t在这种情况下是有效类型的无符号等价物int


  • 有没有办法可以添加编译时保护,这样对于 int 不是“老式”32 位的少数人来说,代码不会构建?

    显而易见的是static_assert(sizeof(int) == 4, "int is not 32 bits");,但这又需要 C11。如果需要与旧 C 的向后兼容性,您可以发明一些肮脏的“穷人的静态断言”:

    #define stat_assert(expr) typedef int dummy_t [expr]; 
    

  • #if INT_MIN != -0x80000000

    根据您的挑剔程度,这不是 100% 可移植的。int理论上可能是 64 位,但可能也不希望移植到这种虚构的系统。

    如果您不想拖动limits.h,您也可以将宏编写为

    #if (unsigned int)-1 != 0xFFFFFFFF
    

    无论如何,它是一个更好的宏,因为它没有任何隐藏的隐式提升宝石- 请注意,-0x80000000它始终 100% 等效0x80000000于 32 位系统。


推荐阅读