C编程中位数的问题

Question in number of bits in C programming

如果我这样做

int a = 3,则3用32位二进制表示

如果我这样做

char a = 3,则3用8位二进制表示

我的问题是在使用值进行初始化之前,3 代表了多少位?

(也就是说等号右边的“3”有多少位)

很常见 int 有 32 位,但不能保证。它也可以是 16 或 64。或更高。

单个 3int.

类型的整数文字

您可以使用 sizeof 运算符进行检查。它会给你参数的大小(以字节为单位)。只需尝试获取 inta3.

的大小
#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 不得小于 +327672 ^ 15 - 1

如果在您的系统中 int 类型的对象的大小等于 4,那么像 3 这样的整数常量将占用等于 4 字节的内存块.

注意,例如字符整型常量也是int.

类型

所以在这两个声明中

char a = 3;

char a = '';

如果 sizeof( int ) 等于 4int 类型的常量 3'' 占用 4 个字节。

3 称为 整数常量,它的类型与任何命名变量非常相似。如果键入的数字可以放在 int 中,则它总是键入 int。否则,如果放不下,编译器会尝试将其放入 long,然后 long long.

关于如何做到这一点有各种相当复杂的规则,我不会在这里提及所有肮脏的细节 - 对此感兴趣的人可以查看 C 标准 6.4.4.1 中的表格。对于普通程序员来说,知道我们还可以通过添加 U 后缀强制整数常量无符号或通过添加 L 后缀强制其为 long 可能就足够了。即 3U3L 或组合 3UL。 (小写 ul 也适用。)

在实际计算机上,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
}

https://godbolt.org/z/3675zv

问题“3 用多少位来表示?”实际上很棘手。如果我们能找到 3,那么我们就可以回答它。所以问题是:3在哪里?

真正发生的是:

int a = 3;

等同于:

int a;
a = 3;

编译器确保变量 a 有 4 个字节的 space(它在编译时执行此操作),然后它还在程序中放入一条指令来存储当你 运行 程序时 space 中的第 3 号。

我们可以使用这个有用的在线工具编译程序,看看编译器实际输出的 assembly/machine 代码是什么: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 个字节)但是将特定位放入 space 的指令可以有不同的大小。


即使在 x86-64 上,数字也会占用不同数量的 space。如果我执行 return 0;,编译器会将其转换为 xor eax, eax (31 c0)。该指令中根本没有数字 0 ! (31 c0是所有类型的指令,没有数据)