用汇编看C语言

Author Avatar
ciaoly 2019年11月05日
  • 在其它设备中阅读本文章

以下内容解答三个问题:

  1. 对一个 unsigned char 取负, 结果会是什么?
  2. sizeof() 如何计算?
  3. (静态)数组保存在哪里?

1. 问题1

使用编译器: "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5.1) 4.4.5"
CPU信息: AMD A10 PRO-7800B R7
使用 32 位操作系统
编译选项: $GCC -O0 -S

#include <stdlib.h>
#include <stdio.h>

typedef unsigned char uchar; 

int main() {
    uchar a=0xff, b=0x01, c;
    b = b << 4;
    c = -(a&b);
    printf("%d", c);
    return 0;
} 
    .file   "test.c"
    .section    .rodata
.LC0:
    .string "%d"
    .text
.globl main
    .type   main, @function
main:
    pushl   %ebp                ; 
    movl    %esp, %ebp          ; 
    andl    $-16, %esp          ; 
    subl    $32, %esp           ; 32 位栈空间
    movb    $-1, 31(%esp)       ; 31(%esp) = -1 // -1 的补码(32bit)是 0xFFFFFFFF, 低八位赋值给a正好是 0xff
    movb    $1, 30(%esp)        ; 30(%esp) = 1 // 30(%esp)是变量 b
    salb    $4, 30(%esp)        ; 30(%esp) = 4 // b <<= 4
    movzbl  30(%esp), %eax    ; 高位零填充 b // b在此变为 32 位有符号 
    movzbl  31(%esp), %edx    ; 高位零填充 a // a在此变为 32 位有符号
    andl    %edx, %eax          ; b = b & a
    negl    %eax                ; b 取负数 (补码负数) // b 为32位有符号
    movb    %al, 29(%esp)       ; b 取低八位 // 这里就是 c 的值
    movzbl  29(%esp), %edx    ; 高位零填充, 准备打印的数据
    movl    $.LC0, %eax         ; 
    movl    %edx, 4(%esp)       ; 
    movl    %eax, (%esp)        ; 
    call    printf              ; 
    movl    $0, %eax
    leave
    ret
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5.1) 4.4.5"
    .section    .note.GNU-stack,"",@progbits

2. 问题2

使用编译器: "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5.1) 4.4.5"
CPU信息: AMD A10 PRO-7800B R7
使用 32 位操作系统
编译选项: $GCC -O0 -S

#include <stdlib.h>
#include <stdio.h>

typedef unsigned char uchar; 

int main() {
    uchar *str="abcxyz", *cha, str2[]="abcxyz";
    uchar arr[] = {'i', 'j', 'k', 'l', 'm', 'n', 0};
    cha = arr;
    printf("%d\n", sizeof(str)); // 4
    printf("%d\n", sizeof(str2)); // 7
    printf("%d\n", sizeof(arr)); // 7
    printf("%d\n", sizeof(cha)); // 4
    printf("%c\n", arr[2]);
    printf("%s\n", arr); 
    return 0;
} 
    .file   "test2.c"
    .section    .rodata
.LC0:
    .string "abcxyz"
.LC1:
    .string "%d\n"
.LC2:
    .string "%c\n"
    .text
.globl main
    .type   main, @function
main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $48, %esp                ; 栈空间48字节
    movl    $.LC0, 44(%esp)          ; 赋值字符串 abcxyz // 44(%esp)是 数组str2, 占用栈空间7字节
    movl    $2019779169, 33(%esp)    ; 33(%esp) 是
    movw    $31353, 37(%esp)         ; 37(%esp) 是
    movb    $0, 39(%esp)             ; 39(%esp) 是
    movb    $105, 26(%esp)           ; 数组赋值 'i' // 26(%esp) 是 数组arr的头
    movb    $106, 27(%esp)           ; 'j'
    movb    $107, 28(%esp)           ; 'k'
    movb    $108, 29(%esp)           ; 'l'
    movb    $109, 30(%esp)           ; 'm'
    movb    $110, 31(%esp)           ; 'n'
    movb    $0, 32(%esp)             ; '\0'
    leal    26(%esp), %eax           ; %eax = arr
    movl    %eax, 40(%esp)           ; 40(%esp) = arr
    movl    $.LC1, %eax              ; %eax = "%d\n" //
    movl    $4, 4(%esp)              ; 4(%esp) = 4 // 由此可见, sizeof()由编译器自动执行
    movl    %eax, (%esp)             ; %esp = "%d\n" // 但是在64位机中, 这里的传参应该是用寄存器 (%edi) 和 (%esi) 完成才对, 这里是32位机, 看起来好像是用了栈内存
    call    printf                   ; printf
    movl    $.LC1, %eax              ; %eax = "%d\n" //
    movl    $7, 4(%esp)              ; 4(%esp) = 7 // 由此可见, sizeof()由编译器自动执行
    movl    %eax, (%esp)             ; %esp = %eax
    call    printf
    movl    $.LC1, %eax
    movl    $7, 4(%esp)
    movl    %eax, (%esp)
    call    printf
    movl    $.LC1, %eax
    movl    $4, 4(%esp)
    movl    %eax, (%esp)
    call    printf                   ; 下面是数组取值与打印
    movzbl  28(%esp), %eax         ; 
    movzbl  %al, %edx              ; 
    movl    $.LC2, %eax              ; 
    movl    %edx, 4(%esp)            ; 栈内存的"第二个" 32 位空间为第二个参数
    movl    %eax, (%esp)             ; 栈内存顶为第一个参数
    call    printf                   ; 下面是打印字符串 "%s"
    leal    26(%esp), %eax           ; 
    movl    %eax, (%esp)             ;
    call    puts                     ; 竟然是调用了 puts 函数
    movl    $0, %eax
    leave
    ret
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5.1) 4.4.5"
    .section    .note.GNU-stack,"",@progbits

3. 问题3

问题二