假设我的项目包含一个来自第三方库的头文件,其中包含以下内容:
struct foo {
signed int x:4;
};
如何在不假设位字段始终具有宽度4以及不依赖于实现定义行为的情况下,确定成员x
中可以存储的最大值?
struct foo {
signed int x:4;
};
如何在不假设位字段始终具有宽度4以及不依赖于实现定义行为的情况下,确定成员x
中可以存储的最大值?
由于位域的大小无法使用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
)
BIT_MASK(++i)
超过2^3-1,该值就超出了可表示值的范围。C99 6.3.1.3p3指出,如果新类型是有符号的且该值无法在其中表示,则结果是实现定义的或引发实现定义的信号。因此,我不能可移植地依赖于该值变为负数(它理论上可能会崩溃,尽管我不知道任何会这样做的实现)。 - Richard Hansensizeof(f)
可能大于sizeof(x)
(参见C99 6.7.2.1p15),f
中的其他位可能不为0(C99 6.2.6.1p6),我找不到标准关于保存位域的字节中剩余位的值的规定,位域表示中位的顺序是实现定义的(例如,符号位可能在值位的中间),并且分配给包含字节的位的顺序是实现定义的(C99 6.7.2.1p11)。 - Richard Hansen#pragma pack
在这里有帮助吗?希望你能找到解决方案。 - David Ranieri#pragma pack
的行为是实现定义的。 - Richard HansenINT_MAX
或INT_MIN
的问题相同:因为你无法自己确定这些值,编译器实现必须提供这些值。(你可以采取的所有措施来确定符号位的位置都具有实现定义(甚至是未定义)行为:溢出、移位操作)signed
的原因之一。位域用于位操作,没有其他作用,因此符号位就被浪费了。unsigned
,事情就容易了。你只需在其中存储-1
,就可以得到全部为1的值,即“类型”的最大值。如果我要这样做,我会尝试将-1加载到它上面,如果它是二进制补码表示,则为可存储的最大正值。 以上情况
foo.x = -1; unsigned int max_size = (unsigned int)(foo.x);
其中一半将是无符号最大值,另一半加1将是有符号最大值
1111
。 - Grijesh Chauhan不要假设位域始终具有宽度4
*,但任何编译器都会始终具有4位宽... 我是不是漏了什么? - Grijesh Chauhan