问题描述
如果我这样做
int a = 3,那么 3 将以 32 位二进制表示。
如果我这样做
char a = 3,那么 3 将以 8 位二进制表示。
我的问题是在对值进行初始化之前,3 用多少位表示?
(换句话说,等号右边的“3”有多少位)
解决方法
int
具有 32 位非常常见,但不能保证。它也可以是 16 或 64。或更高。
单个 3
是 int
类型的整数文字。
您可以使用 sizeof
运算符进行检查。它将为您提供以字节为单位的参数大小。只需尝试获取 int
、a
和 3
的大小。
#include <stdio.h>
int main()
{
int a = 3;
printf("%ld\n",sizeof(a)); // gives 4 bytes (32 bit) on my PC
printf("%ld\n",sizeof(int)); // gives 4 bytes (32 bit) on my PC
printf("%ld\n",sizeof(3)); // gives 4 bytes (32 bit) on my PC
return 0;
}
此外,3
的类型为 int
。所以它的大小等于 int
的大小。
int
类型的对象的大小是实现定义的标准仅要求 INT_MAX
不得小于 +32767
即 2 ^ 15 - 1
>
如果在您的系统中,int
类型的对象的大小等于 4,那么像 3
这样的整数常量将占用一个等于 4
字节的内存块。
注意例如字符整型常量也有int
类型。
所以在这两个声明中
char a = 3;
和
char a = '\3';
如果 3
等于 '\3'
,则类型为 int
的常量 4
和 sizeof( int )
占用 4
字节。
3
被称为整数常量,它的类型很像任何命名变量。如果输入的数字可以容纳在 int
内,则始终输入 int
。否则,如果它不适合,编译器将尝试将其装入 long
,然后是 long long
。
如何做到这一点有各种相当复杂的规则,我不会在这里提到所有肮脏的细节——有兴趣的人可以查看 C 标准 6.4.4.1 中的表格。对于普通程序员来说,知道我们还可以通过添加 U
后缀来强制整数常量无符号或通过添加 long
后缀将其强制为 L
可能就足够了。即 3U
或 3L
或组合 3UL
。 (小写 u
和 l
也可以。)
在现实世界的计算机上,int
总是 2 或 4 字节大。 long
为 4 或 8 字节大。来自具有 4 字节 int
和 8 字节 long
的 64 位 Linux 计算机的示例:
#include <stdio.h>
int main (void)
{
printf("%zu\n",sizeof(int)); // 4
printf("%zu\n",sizeof(3)); // 4
printf("%zu\n",sizeof(3L)); // 8
printf("%zu\n",sizeof(2147483647)); // 4,fits int
printf("%zu\n",sizeof(2147483648)); // 8,doesnt fit
}
,
问题“3 用多少位表示?”其实很棘手。如果我们能找到 3,那么我们就可以回答它。所以问题是:3在哪里?
真正发生的事情是:
int a = 3;
等同于:
int a;
a = 3;
编译器确保变量 a
有 4 个字节的空间(它在编译时执行此操作),然后它还会在程序中放置一条指令,将数字 3 存储在该空间中,当你运行程序。
我们可以使用这个有用的在线工具来编译程序并查看编译器实际输出的汇编/机器代码:https://godbolt.org/z/a9Pohn
在这种情况下,我进入了程序:
int main() {
int a;
a = 3;
}
并用“x86-64 gcc 10.2”编译它,没有优化。这是编译后的代码(汇编代码和机器代码):
main:
55 push rbp
48 89 e5 mov rbp,rsp
c7 45 fc 03 00 00 00 mov DWORD PTR [rbp-0x4],0x3
b8 00 00 00 00 mov eax,0x0
5d pop rbp
c3 ret
如果我们可以读取汇编,我们可以看到编译器选择插入到程序中以初始化变量 a
的指令是 mov DWORD PTR [rbp-0x4],0x3
。在机器代码中,它写为 c7 45 fc 03 00 00 00
。指令是数字 3 的来源。
该指令长 7 个字节。 c7 45
告诉 CPU 这是什么类型的指令(“在堆栈帧的特定位置放置一个特定的数字”)。 fc
是堆栈帧中的位置。而 03 00 00 00
是它放在那里的特定数字(以小端格式)。这是源代码中的数字 3。所以在这种情况下,它占用了 4 个字节。
请注意,它并不总是相同的。如果我们为 ARM CPU 而不是 x86-64 编译,那么这些是相关指令
mov r3,#3
str r3,[fp,#-8]
不幸的是,godbolt 不会向我们展示机器代码,但是 we can look up the MOV # instruction in the ARM manual 告诉我们指令有 4 个字节长,并且被移动的数字只占用其中的 2 个字节!其他位自动为零。如果您使用的数字不适合 2 个字节,显然它使用了不同的指令。
通常我们不讨论指令的大小,因为它们的变化远大于数据大小。 int a;
始终保留 4 个字节(如果您系统的 int
为 4 个字节),但将特定位放入该空间的指令可能具有不同的大小。
即使在 x86-64 上,数字也会占用不同数量的空间。如果我执行 return 0;
,编译器会将其转换为 xor eax,eax
(31 c0
)。该指令中根本没有数字 0
! (31 c0
是所有类型的指令,没有数据)