【问题标题】:How to calculate the maximum supported value of a signed bit-field?如何计算有符号位域的最大支持值?
【发布时间】:2013-07-20 07:00:33
【问题描述】:

假设我的项目包含来自第 3 方库的标头,其中包含以下内容:

struct foo {
    signed int x:4;
};

如果不假设位字段的宽度始终为 4,并且不依赖实现定义的行为,我如何确定可以存储在成员 x 中的最大值?

【问题讨论】:

  • 可靠地你只能存储1111
  • @GrijeshChauhan:“不假设位字段的宽度始终为 4。”第 3 方开发者可能会在下一个版本中将宽度更改为 5。或者 13. 或者别的什么。当位域的宽度发生变化时,我不希望我的代码中断。
  • 我没有得到 :( , Without assuming that the bit-field will always have width 4 它总是有 4-with 任何编译器......我错过了什么吗?
  • @GrijeshChauhan:在 3rd 方库的下一版本中,4 可能会更改为 5 或其他内容。
  • 好的,知道了!现在..好问题。但是:(不知道,我觉得只有你可以从第三方库文档中知道这一点。但是如何构建稳定的代码??仍然是一个问题......

标签: c c99 bit-fields c11


【解决方案1】:

由于无法使用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

【讨论】:

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

基本上你不能。问题与确定INT_MAXINT_MIN 相同:因为您无法自己确定这些,编译器实现必须提供这些值。 (确定符号位位置的所有操作都具有实现定义(甚至未定义)的行为:溢出、移位运算符)

对于位域,实现不能提供这些值,所以你被卡住了。

这是位域真的不应该是signed 的原因之一。位域用于位操作,仅此而已,符号位就被浪费了。

如果你的字段是unsigned,事情就简单了。您只需将-1 存储在其中,您将获得所有值,即“类型”的最大值。

【讨论】:

    【解决方案3】:

    如果我要这样做,我会尝试将 -1 加载到它,如果它是 2 的补码表示,那么它将是可以存储的最大正值。 以上案例

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

    一半是无符号最大值,一半+1是有符号最大值

    【讨论】:

      猜你喜欢
      • 2010-09-14
      • 2011-06-18
      • 2019-04-14
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      • 2019-07-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多