在C中,有符号整数和无符号整数在内存中的存储方式不同。当在运行时清除类型时,C也会隐式转换带符号整数和无符号整数。但是,当我尝试以下代码段时,
#include <stdio.h>
int main() {
unsigned int a = 5;
signed int b = a;
signed int c = *(unsigned int*)&a;
signed int d = *(signed int*)&a;
printf("%u\n", a);
printf("%i\n", b);
printf("%i\n", c);
printf("%i\n", d);
return 0;
}
预期输出为:
5
5 //Implicit conversion occurs
5 //Implicit conversion occurs, because it knows that *(unsigned int*)&a is an unsigned int
[some crazy number] //a is casted directly to signed int without conversion
但是,实际上它会输出
5
5
5
5
为什么?
答案 0 :(得分:6)
您声称...
在C语言中,有符号整数和无符号整数在内存中的存储方式不同
...在很大程度上是错误的。该标准改为specifies:
对于有符号整数类型,对象表示的位应分为三组:值位,填充位和符号位。不需要任何填充位;带符号的字符不得有任何填充位。恰好有一个符号位。 作为值位的每个位应与对应的无符号类型的对象表示形式中的同一位具有相同的值 (如果在有符号类型中有M个值位) N为无符号类型,则M <= N)。如果符号位为零,则不会影响结果值。
(C2011 6.2.6.2/2;已添加重点)
因此,尽管有符号整数类型的表示形式及其对应的无符号整数类型(具有相同的大小)必须至少在以下方面有所不同:前者具有符号位,而后者没有符号位,但实际上大多数表示形式完全对应。该标准要求它。较小的(ish)非负整数将以相应的有符号和无符号整数类型相同地表示。
此外,一些评论提出了“严格别名规则”(paragraph 6.5/7 of the standard)的问题。就像您的代码一样,它禁止通过不同类型的左值访问一种类型的对象,但是它允许一些显着的异常。例外之一是您可以通过类型为
的左值访问对象
- 一种类型,它是与对象的有效类型相对应的有符号或无符号类型,
这实际上就是您的代码所执行的操作,因此那里没有严格的混叠违规。
答案 1 :(得分:0)
与注释部分中的解释相反,我仍然想证明您的整数全部以相同的方式存储在内存中。我很高兴修改我的答案,但是目前我仍然不相信unsigned / signed int在内存中的存储方式有所不同[实际上,我知道它^^]。
测试程序:
#include <iostream>
int main() {
unsigned int a = 5;
signed int b = a;
signed int c = *(unsigned int*)&a;
signed int d = *(signed int*)&a;
printf("%u\n", a);
printf("%i\n", b);
printf("%i\n", c);
printf("%i\n", d);
std::terminate();
return 0;
}
使用以下命令进行编译: g ++ -O0 -g test.cpp
在GDB中运行它: gdb ./a.out
一旦调用std :: terminate,我们就可以检查原始内存:
(gdb) print/t main::a
$9 = 101
(gdb) print/t main::b
$10 = 101
(gdb) print/t main::c
$11 = 101
(gdb) print/t main::d
$12 = 101
(gdb)
所有整数都以相同的方式存储,无符号或有符号整数。唯一的区别是,一旦SIGNED_INT_MAX上的无符号int转换为有符号int,它们的解释方式就会不同。但是,这种强制转换也不会完全改变内存。