警告只有一个位域,而不是两个?
Warning about only one bitfield, instead of both?
受此启发,我有:
#include<stdio.h>
struct st
{
int a:1;
int b:2;
};
int main()
{
struct st obj={1, 2};
printf("a = %d\nb = %d\n",obj.a,obj.b);
}
我得到:
Georgioss-MacBook-Pro:~ gsamaras$ gcc -Wall main.c
main.c:10:26: warning: implicit truncation from 'int' to bitfield changes value
from 2 to -2 [-Wbitfield-constant-conversion]
struct st obj={1, 2};
^
1 warning generated.
Georgioss-MacBook-Pro:~ gsamaras$ ./a.out
a = -1
b = -2
我想我明白为什么两个位域都无法保持它们的值(根据这个 answer),但我不明白为什么编译器只警告 2
,而不是 1
太!有什么想法吗?
我在 Mac 中使用:
Georgioss-MacBook-Pro:~ gsamaras$ gcc -v
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 8.1.0 (clang-802.0.38)
Target: x86_64-apple-darwin16.3.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
在旧的 Linux 系统中,gcc 版本 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5),我没有收到相关警告。
在 Debian 安装中,gcc 版本 4.9.2 (Debian 4.9.2-10),我没有收到相关警告!
int b:2;
是 2 位宽的有符号整数。数字 2(无符号)在二进制中表示为 10
,并且因为源 int 是有符号的,所以您实际上将带符号的数字保存到内存中。
这就是您收到此警告的原因。
可以保存为 2 位有符号整数的值是:(-2 = 11, -1 = 10, 0 = 00, 1 = 01)
你在这两种情况下都没有得到的原因是 int a:1
只有 1 位并且可以容纳数字 1
或 0
,没有负数。
https://en.wikipedia.org/wiki/Two%27s_complement
可能会发生一些事情,也许一些实验可以提供帮助。
首先,可能是 gcc 足够聪明,知道单个位不能真正为正或负,因为它只是单个位。
另一种可能性是 gcc 计算表达式的顺序与您的想法相反。一些编译器从右到左计算,在你的情况下,踢出错误并停止。
要进行测试,请更正 b 的位域以在不翻转符号位的情况下保存“2”的带符号整数(3 位应该有效)。如果在修复 'b' 时您为 'a' 生成了错误,那么您知道这只是编译器评估顺序。
如果修复 'b' 不会导致 'a' 发出警告,则 gcc 正在对单个位字段执行一些内部优化。
此外,将您的位域类型更改为 'uint' 也应该修复警告,在这种情况下,它只是表示的符号位被翻转了。
编码愉快。
编辑
相关源码:https://github.com/llvm-mirror/clang/blob/master/lib/Sema/SemaChecking.cpp#L8812
受此启发
#include<stdio.h>
struct st
{
int a:1;
int b:2;
};
int main()
{
struct st obj={1, 2};
printf("a = %d\nb = %d\n",obj.a,obj.b);
}
我得到:
Georgioss-MacBook-Pro:~ gsamaras$ gcc -Wall main.c
main.c:10:26: warning: implicit truncation from 'int' to bitfield changes value
from 2 to -2 [-Wbitfield-constant-conversion]
struct st obj={1, 2};
^
1 warning generated.
Georgioss-MacBook-Pro:~ gsamaras$ ./a.out
a = -1
b = -2
我想我明白为什么两个位域都无法保持它们的值(根据这个 answer),但我不明白为什么编译器只警告 2
,而不是 1
太!有什么想法吗?
我在 Mac 中使用:
Georgioss-MacBook-Pro:~ gsamaras$ gcc -v
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 8.1.0 (clang-802.0.38)
Target: x86_64-apple-darwin16.3.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
在旧的 Linux 系统中,gcc 版本 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5),我没有收到相关警告。
在 Debian 安装中,gcc 版本 4.9.2 (Debian 4.9.2-10),我没有收到相关警告!
int b:2;
是 2 位宽的有符号整数。数字 2(无符号)在二进制中表示为 10
,并且因为源 int 是有符号的,所以您实际上将带符号的数字保存到内存中。
这就是您收到此警告的原因。
可以保存为 2 位有符号整数的值是:(-2 = 11, -1 = 10, 0 = 00, 1 = 01)
你在这两种情况下都没有得到的原因是 int a:1
只有 1 位并且可以容纳数字 1
或 0
,没有负数。
https://en.wikipedia.org/wiki/Two%27s_complement
可能会发生一些事情,也许一些实验可以提供帮助。
首先,可能是 gcc 足够聪明,知道单个位不能真正为正或负,因为它只是单个位。
另一种可能性是 gcc 计算表达式的顺序与您的想法相反。一些编译器从右到左计算,在你的情况下,踢出错误并停止。
要进行测试,请更正 b 的位域以在不翻转符号位的情况下保存“2”的带符号整数(3 位应该有效)。如果在修复 'b' 时您为 'a' 生成了错误,那么您知道这只是编译器评估顺序。
如果修复 'b' 不会导致 'a' 发出警告,则 gcc 正在对单个位字段执行一些内部优化。
此外,将您的位域类型更改为 'uint' 也应该修复警告,在这种情况下,它只是表示的符号位被翻转了。
编码愉快。
编辑 相关源码:https://github.com/llvm-mirror/clang/blob/master/lib/Sema/SemaChecking.cpp#L8812