堆栈如何区分不同的数字类型?

我正在尝试学习汇编,但在理解堆栈上的内存分配/检索时遇到了一些麻烦。

当字符串在堆栈上分配时,程序知道当它到达空终止字符时停止读取字符串/x00。然而,对于数字,没有这样的事情。程序如何知道堆栈上分配的数字的结尾,以及如何区分不同的数字类型(short、long、int)?(我对此有点陌生,所以请纠正我可能误解的任何内容!)

回答

类型intvs. floatvs. char *vs. struct foo)仅在翻译过程中真正重要,当编译器正在分析您的源代码并将其转换为适当的机器代码时。这就是“一个操作数[]应该是指针类型,另一个应该是整数类型”和“一元的操作数*应该是指针类型”和“乘法运算符的操作数应该是算术类型”等规则时,强制执行。

汇编语言通常处理字节、字(2 个字节)、长字(4 个字节)等,尽管一些特殊用途的平台可能有奇怪的字长。操作码addb1添加了两个字节大小的实体addl的内容,添加了两个长字大小的实体的内容等。因此,当编译器翻译您的代码时,它会根据对象声明的类型为对象使用正确的操作码。所以,如果你的东西申报的short,编译器会(典型值)的使用操作码用于字大小的物体(addwmovw,等)。如果您将某些内容声明为intor long,它将(通常)使用用于长字大小对象 ( ) 的操作码。浮点类型通常用一组不同的操作码和它们自己的一组寄存器来处理。addl ,movl

简而言之,汇编语言凭借编译器指定的操作码“知道”事物的位置和大小。

简单示例 - 这是一些与 anint和 a一起使用的 C 源代码short

#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,-aldhwith 选项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

这就是它“知道”为给定对象读取多少字节的方式——所有这些信息都被烘焙到机器代码本身中。标量类型都有固定的、已知的大小,因此只需选择要使用的正确操作码或操作码即可。


  1. 我正在使用 Intel 特定的助记符,但其他汇编程序的概念是相同的。


以上是堆栈如何区分不同的数字类型?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>