# Chapter 8 一個或者多個字的返回值
X86架構下通常返回EAX寄存器的值,如果是單字節char,則只使用EAX的低8位AL。如果返回float類型則使用FPU寄存器ST(0)。ARM架構下通常返回寄存器R0。
假如main()函數的返回值是void而不是int會怎么樣?
通常啟動函數調用main()為:
```
#!bash
push envp
push argv
push argc
call main
push eax
call exit
```
換句話說為
```
#!cpp
exit(main(argc,argv,envp));
```
如果main()聲明為void類型并且函數沒有明確返回狀態值,通常在main()結束時EAX寄存器的值被返回,然后作為exit()的參數。大多數情況下函數返回的是隨機值。這種情況下程序的退出代碼為偽隨機的。
我們看一個實例,注意main()是void類型:
```
#!cpp
#include <stdio.h>
void main()
{
printf ("Hello, world!
");
};
```
我們在linux下編譯。
GCC 4.8.1會使用puts()替代printf()(看前面章節2.3.3),沒有關系,因為puts()會返回打印的字符數,就行printf()一樣。請注意,main()結束時EAX寄存器的值是非0的,這意味著main()結束時保留puts()返回時EAX的值。
Listing 8.1: GCC 4.8.1
```
#!bash
.LC0:
.string "Hello, world!"
main:
push ebp
mov ebp, esp
and esp, -16
sub esp, 16
mov DWORD PTR [esp], OFFSET FLAT:.LC0
call puts
leave
ret
```
我們寫bash腳本來看退出狀態:
Listing 8.2: tst.sh
```
#!bash
#!/bin/sh
./hello_world
echo $?
```
運行:
```
#!bash
$ tst.sh
Hello, world!
14
```
14為打印的字符數。
回到返回值是EAX寄存器值的事實,這也就是為什么老的C編譯器不能夠創建返回信息無法擬合到一個寄存器(通常是int型)的函數。如果必須這樣,應該通過指針來傳遞。現在可以這樣,比如返回整個結構體,這種情況應該避免。如果必須要返回大的結構體,調用者必須開辟存儲空間,并通過第一個參數傳遞指針,整個過程對程序是透明的。像手動通過第一個參數傳遞指針一樣,只是編譯器隱藏了這個過程。
小例子:
```
#!cpp
struct s
{
int a;
int b;
int c;
};
struct s get_some_values (int a)
{
struct s rt;
rt.a=a+1;
rt.b=a+2;
rt.c=a+3;
return rt;
};
```
…我們可以得到(MSVC 2010 /Ox):
```
#!bash
$T3853 = 8 ; size = 4
_a$ = 12 ; size = 4
?get_some_values@@YA?AUs@@H@Z PROC ; get_some_values
mov ecx, DWORD PTR _a$[esp-4]
mov eax, DWORD PTR $T3853[esp-4]
lea edx, DWORD PTR [ecx+1]
mov DWORD PTR [eax], edx
lea edx, DWORD PTR [ecx+2]
add ecx, 3
mov DWORD PTR [eax+4], edx
mov DWORD PTR [eax+8], ecx
ret 0
?get_some_values@@YA?AUs@@H@Z ENDP ; get_some_values
```
內部變量傳遞指針到結構體的宏為$T3853。
這個例子可以用C99語言擴展來重寫:
```
#!bash
struct s
{
int a;
int b;
int c;
};
struct s get_some_values (int a)
{
return (struct s){.a=a+1, .b=a+2, .c=a+3};
};
```
Listing 8.3: GCC 4.8.1
```
#!bash
_get_some_values proc near
ptr_to_struct = dword ptr 4
a = dword ptr 8
mov edx, [esp+a]
mov eax, [esp+ptr_to_struct]
lea ecx, [edx+1]
mov [eax], ecx
lea ecx, [edx+2]
add edx, 3
mov [eax+4], ecx
mov [eax+8], edx
retn
_get_some_values endp
```
我們可以看到,函數僅僅填充調用者申請的結構體空間的相應字段。因此沒有性能缺陷。
- 第一章 CPU簡介
- 第二章 Hello,world!
- 第三章? 函數開始和結束
- 第四章 棧
- Chapter 5 printf() 與參數處理
- Chapter 6 scanf()
- CHAPER7 訪問傳遞參數
- Chapter 8 一個或者多個字的返回值
- Chapter 9 指針
- Chapter 10 條件跳轉
- 第11章 選擇結構switch()/case/default
- 第12章 循環結構
- 第13章 strlen()
- Chapter 14 Division by 9
- chapter 15 用FPU工作
- Chapter 16 數組
- Chapter 17 位域
- 第18章 結構體
- 19章 聯合體
- 第二十章 函數指針
- 第21章 在32位環境中的64位值
- 第二十二章 SIMD
- 23章 64位化
- 24章 使用x64下的SIMD來處理浮點數
- 25章 溫度轉換
- 26章 C99的限制
- 27章 內聯函數
- 第28章 得到不正確反匯編結果
- 第29章 花指令
- 第30章 16位Windows
- 第31章 類
- 三十二 ostream