类型(int vs. float vs. char * vs. struct foo)只有在编译器分析您的源代码并将其转换为合适的机器码。这就是“[] 的操作数之一应具有指针类型,另一个应具有整数类型”和“一元 * 的操作数应具有指针类型”和“乘法运算符的操作数应具有算术类型”之类的规则"等,都被强制执行了。
汇编语言通常处理字节、字(2 个字节)、长字(4 个字节)等,尽管一些特殊用途的平台可能有奇怪的字长。操作码addb1 添加两个字节大小的实体的内容,addl 添加两个长字大小的实体的内容,等等。所以当编译器翻译你的代码时,它使用基于其声明类型的对象的正确操作码。因此,如果您将某些内容声明为short,编译器将(通常)使用用于字大小对象的操作码(addw、movw 等)。如果您将某些内容声明为int 或long,它将(通常)使用用于长字大小对象的操作码(addl、movl)。浮点类型通常使用一组不同的操作码和它们自己的一组寄存器来处理。
简而言之,汇编语言通过编译器指定的操作码“知道”事物的位置和大小。
简单示例 - 这是一些与 int 和 short 一起使用的 C 源代码:
#include <stdio.h>
int main( void )
{
int x;
short y;
printf( "Gimme an x: " );
scanf( "%d", &x );
y = 2 * x + 30;
printf( "x = %d, y = %hd\n", x, y );
return 0;
}
我使用-Wa,-aldh 选项和gcc 生成汇编代码列表,其中源代码交错,给我
GAS LISTING /tmp/cc3D25hf.s page 1
1 .file "simple.c"
2 .text
3 .Ltext0:
4 .section .rodata
5 .LC0:
6 0000 47696D6D .string "Gimme an x: "
6 6520616E
6 20783A20
6 00
7 .LC1:
8 000d 256400 .string "%d"
9 .LC2:
10 0010 78203D20 .string "x = %d, y = %hd\n"
10 25642C20
10 79203D20
10 2568640A
10 00
11 .text
12 .globl main
14 main:
15 .LFB0:
16 .file 1 "simple.c"
1:simple.c **** #include <stdio.h>
2:simple.c ****
3:simple.c **** int main( void )
4:simple.c **** {
17 .loc 1 4 0
18 .cfi_startproc
19 0000 55 pushq %rbp
20 .cfi_def_cfa_offset 16
21 .cfi_offset 6, -16
22 0001 4889E5 movq %rsp, %rbp
23 .cfi_def_cfa_register 6
24 0004 4883EC10 subq $16, %rsp
5:simple.c **** int x;
6:simple.c **** short y;
7:simple.c ****
8:simple.c **** printf( "Gimme an x: " );
25 .loc 1 8 0
26 0008 BF000000 movl $.LC0, %edi
26 00
27 000d B8000000 movl $0, %eax
27 00
28 0012 E8000000 call printf
28 00
9:simple.c **** scanf( "%d", &x );
29 .loc 1 9 0
30 0017 488D45F8 leaq -8(%rbp), %rax
31 001b 4889C6 movq %rax, %rsi
32 001e BF000000 movl $.LC1, %edi
32 00
33 0023 B8000000 movl $0, %eax
33 00
34 0028 E8000000 call __isoc99_scanf
34 00
10:simple.c ****
11:simple.c **** y = 2 * x + 30;
GAS LISTING /tmp/cc3D25hf.s page 2
35 .loc 1 11 0
36 002d 8B45F8 movl -8(%rbp), %eax
37 0030 83C00F addl $15, %eax
38 0033 01C0 addl %eax, %eax
39 0035 668945FE movw %ax, -2(%rbp)
12:simple.c ****
13:simple.c **** printf( "x = %d, y = %hd\n", x, y );
40 .loc 1 13 0
41 0039 0FBF55FE movswl -2(%rbp), %edx
42 003d 8B45F8 movl -8(%rbp), %eax
43 0040 89C6 movl %eax, %esi
44 0042 BF000000 movl $.LC2, %edi
44 00
45 0047 B8000000 movl $0, %eax
45 00
46 004c E8000000 call printf
46 00
14:simple.c **** return 0;
47 .loc 1 14 0
48 0051 B8000000 movl $0, %eax
48 00
15:simple.c **** }
49 .loc 1 15 0
50 0056 C9 leave
51 .cfi_def_cfa 7, 8
52 0057 C3 ret
53 .cfi_endproc
54 .LFE0:
56 .Letext0:
57 .file 2 "/usr/lib/gcc/x86_64-redhat-linux/7/include/stddef.h"
58 .file 3 "/usr/include/bits/types.h"
59 .file 4 "/usr/include/libio.h"
60 .file 5 "/usr/include/stdio.h"
如果你看线
36 002d 8B45F8 movl -8(%rbp), %eax
37 0030 83C00F addl $15, %eax
38 0033 01C0 addl %eax, %eax
39 0035 668945FE movw %ax, -2(%rbp)
这是机器码
y = 2 * x + 30;
当它处理x 时,它使用长字的操作码:
movl -8(%rbp), %eax ;; copy the value in x to the eax register
addl $15, %eax ;; add the literal value 15 to the value in eax
addl %eax, %eax ;; multiply the value in eax by 2
在处理y 时,它使用操作码来表示单词:
movw %ax, -2(%rbp) ;; save the value in the lower 2 bytes of eax to y
这就是它“知道”为给定对象读取多少字节的方式 - 所有这些信息都被烘焙到机器代码本身中。标量类型都有固定的已知大小,因此只需选择正确的操作码或要使用的操作码即可。
- 我使用的是 Intel 特定的助记符,但其他汇编程序的概念是相同的。