1. 程式人生 > >C程式執行過程

C程式執行過程

在C程式執行時,記憶體被劃分為了三個區:1、程式碼區;2、靜態資料區;3、動態資料區。

C程式經過編譯連結之後,在執行執行程式時,程式的一系列指令就被讀取到所連結的記憶體地址上,然後通過eip暫存器來指向要執行的下一條指令;而靜態資料區則是用於存放全域性變數和靜態變數的地方,在程式開始執行前就已經存在初始化的資料了;最後的動態資料區則是在程式執行後才會產生資料,而每個函式的壓棧和清棧就是在這一區域進行的,ebp暫存器指向棧底,esp暫存器指向棧頂。

接下來通過一段程式碼來進行說明,如下所示:

#include <stdio.h>

void fun(int a, int b);

int
main() { fun(1, 2); printf("scv!\n"); } void fun(int a, int b) { int i = a + b; }

在執行此程式時,會首先進入main函式中進行。在進入main函式時,會將ebp暫存器的值進行壓棧處理,並建立main函式的棧區,也就是將esp暫存器(棧頂)值傳遞給ebp暫存器。而eip暫存器的值將會隨著程式的執行而遞增。
當main函式中對fun函式進行呼叫時,在進入fun函式時,同樣會將ebp暫存器的值進行壓棧處理,並建立fun函式的棧區,也就是將esp暫存器(棧頂)值傳遞給ebp暫存器。如下圖所示:
這裡寫圖片描述

而從上述C程式的彙編程式碼中,我們可以印證此說法,如下是此C程式的彙編程式碼:

    .file   "a.i"
    .section    .rodata
.LC0:
    .string "scv!"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
andl $-16, %esp subl $16, %esp movl $2, 4(%esp) movl $1, (%esp) call fun movl $.LC0, (%esp) call puts leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc .LFE0: .size main, .-main .globl fun .type fun, @function fun: .LFB1: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 subl $16, %esp movl 12(%ebp), %eax movl 8(%ebp), %edx addl %edx, %eax movl %eax, -4(%ebp) leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc .LFE1: .size fun, .-fun .ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3" .section .note.GNU-stack,"",@progbits

在上述程式碼中,我們可以看到main和fun函式中都存在 pushl %ebpmovl %esp, %ebp 語句。這就證明了在呼叫這兩個函式時,都進行了壓棧和新建棧區。

且經過實驗,在fun函式中不使用return語句,在fun函式執行完成後,也同樣會跳轉到呼叫函式繼續往下執行。