返回介绍

Chapter 8 一个或者多个字的返回值

发布于 2025-02-22 14:00:43 字数 3005 浏览 0 评论 0 收藏 0

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

我们可以看到,函数仅仅填充调用者申请的结构体空间的相应字段。因此没有性能缺陷。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文