如何计算有符号位域的最大支持值?

3
假设我的项目包含一个来自第三方库的头文件,其中包含以下内容:
struct foo {
    signed int x:4;
};

如何在不假设位字段始终具有宽度4以及不依赖于实现定义行为的情况下,确定成员x中可以存储的最大值?


可靠地存储只能是 1111 - Grijesh Chauhan
1
@GrijeshChauhan:“不要假设位域总是具有宽度4。”第三方开发人员可能会在下一个版本中将其宽度更改为5,或者13,或其他某些值。我不希望我的代码在位域的宽度更改时出现问题。 - Richard Hansen
我没明白 :( ,*不要假设位域始终具有宽度4*,但任何编译器都会始终具有4位宽... 我是不是漏了什么? - Grijesh Chauhan
@GrijeshChauhan:在第三方库的下一个版本中,那个4可能会改变为5或其他内容。 - Richard Hansen
好的,我明白了!现在……好问题。但是:(没有头绪,我觉得只有通过第三方库文档你才能知道这个问题的答案。但是如何构建稳定的代码?仍然是一个问题…… - Grijesh Chauhan
3个回答

5

由于位域的大小无法使用sizeof计算,因此可以用以下方法:

#include <stdio.h>

struct foo {
    signed int x: 4;
};

#define BIT(n) (1l << (n))
#define BIT_MASK(len) (BIT(len) - 1)

int main(void)
{
    struct foo f = {0};
    long i = 0;

    while (f.x >= 0) {
        f.x = BIT_MASK(++i);
    }
    printf("bits=%ld max=%ld\n", i, BIT_MASK(i));
    return 0;
}

将左移位直到f.x为负。

编辑:

上面的代码是实现定义的,请检查下面的代码,我认为这个代码会运行(但你必须处理BIG ENDIAN):

#include <stdio.h>
#include <string.h>

struct foo {
    signed int x: 4;
};

#define BIT(n) (1l << (n))
#define BIT_MASK(len) (BIT(len) - 1)

int main(void)
{
    long x, i = 0;
    struct foo f;

    f.x = -1;
    memcpy(&x, &f, sizeof(f));
    while (1) {
        if (!(x & BIT(++i))) break; 
    }
    printf("bits=%ld max=%ld\n", i, BIT_MASK(i));
    return 0;
}

现在可以看到它不会超过2^(n-1)-1 (适用于long)


2
这是非常好的答案!David RF,你应该添加一些注释和解释。 - Grijesh Chauhan
1
OP (@Richard Hansen):检查一下这段代码是否可行 @codepad - Grijesh Chauhan
1
我认为这取决于实现定义的行为。一旦BIT_MASK(++i)超过2^3-1,该值就超出了可表示值的范围。C99 6.3.1.3p3指出,如果新类型是有符号的且该值无法在其中表示,则结果是实现定义的或引发实现定义的信号。因此,我不能可移植地依赖于该值变为负数(它理论上可能会崩溃,尽管我不知道任何会这样做的实现)。 - Richard Hansen
@DavidRF: 很不幸,仍然存在实现定义(或未定义)的行为:sizeof(f)可能大于sizeof(x)(参见C99 6.7.2.1p15),f中的其他位可能不为0(C99 6.2.6.1p6),我找不到标准关于保存位域的字节中剩余位的值的规定,位域表示中位的顺序是实现定义的(例如,符号位可能在值位的中间),并且分配给包含字节的位的顺序是实现定义的(C99 6.7.2.1p11)。 - Richard Hansen
@RichardHansen,#pragma pack 在这里有帮助吗?希望你能找到解决方案。 - David Ranieri
不,#pragma pack 的行为是实现定义的。 - Richard Hansen

1
基本上你做不到。问题与确定INT_MAXINT_MIN的问题相同:因为你无法自己确定这些值,编译器实现必须提供这些值。(你可以采取的所有措施来确定符号位的位置都具有实现定义(甚至是未定义)行为:溢出、移位操作)
对于位域,实现无法提供这些值,所以你卡住了。
这是位域确实不应该是signed的原因之一。位域用于位操作,没有其他作用,因此符号位就被浪费了。
如果你的字段是unsigned,事情就容易了。你只需在其中存储-1,就可以得到全部为1的值,即“类型”的最大值。

0

如果我要这样做,我会尝试将-1加载到它上面,如果它是二进制补码表示,则为可存储的最大正值。 以上情况

foo.x = -1; unsigned int max_size = (unsigned int)(foo.x);

其中一半将是无符号最大值,另一半加1将是有符号最大值


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接