Как C возвращает структуру? - PullRequest
4 голосов
/ 18 июля 2011
(gdb) disas func
Dump of assembler code for function func:
0x00000000004004b8 <func+0>:    push   %rbp
0x00000000004004b9 <func+1>:    mov    %rsp,%rbp
0x00000000004004bc <func+4>:    movl   $0x64,0xfffffffffffffff0(%rbp)
0x00000000004004c3 <func+11>:   movb   $0x61,0xfffffffffffffff4(%rbp)
0x00000000004004c7 <func+15>:   mov    0xfffffffffffffff0(%rbp),%rax
0x00000000004004cb <func+19>:   leaveq
0x00000000004004cc <func+20>:   retq
End of assembler dump.


t_test func()
{
    t_test t;
    t.i = 100;
    t.c = 'a';
    return t;
}

Так что кажется, что он возвращает локальную переменную t, но гарантированно ли работает этот вид работы, разве он не должен ссылаться на какие-либо локальные переменные при возврате ??

Ответы [ 5 ]

5 голосов
/ 18 июля 2011

По моему опыту, не существует стандартного способа, которым C возвращает структуру. Чтобы иметь возможность передавать структуру, компилятор обычно (незаметно для пользователя) передает указатель на структуру, в которую функция может копировать содержимое. Способ передачи этого указателя (первый или последний в стеке) зависит от реализации. Некоторые компиляторы, такие как 32-битный MSVC ++, возвращают небольшие структуры в регистрах, таких как EAX и EDX. По-видимому, GCC возвращает такую ​​структуру в RAX в 64-битном режиме.

Но, опять же, не существует стандартного способа, как это сделать. Это не проблема, когда остальная часть кода, использующего функцию, также компилируется тем же компилятором, но это проблема, если функция является экспортированной функцией DLL или библиотеки. Я был укушен этим несколько раз, когда использовал такие функции из другого языка (Delphi) или из C с другим компилятором. Смотрите эту ссылку тоже.

4 голосов
/ 18 июля 2011

возможно, rax достаточно велик, чтобы вместить всю структуру. На 0x00000000004004c7 вы получаете всю структуру (с помощью mov), а не ее адрес (вместо этого вы будете использовать lea)

3 голосов
/ 18 июля 2011

Это не совсем стандартно, как все возвращается, но обычно это в RAX.В вашем примере, предполагая, что t_test :: i и t_test :: c являются единственными членами t_test и имеют максимум 32 бита каждый, вся структура может помещаться в 64-битный регистр, поэтому она просто возвращает значения напрямую через RAXи обычно вещи, которые могут поместиться в 2 регистра, возвращаются в RAX: RDX (или RDX: RAX, я забыл общий порядок).

Для более чем двух регистров обычно требуется передача скрытого параметра указателяв качестве первого параметра, который указывает на объект в вызывающей функции (обычно тот, которому непосредственно присваивается возвращаемое значение).Затем этот объект записывается перед возвратом из вызываемой функции (обычно копируется из локальной структуры, используемой в вызываемой функции), и обычно тот же самый указатель, который был передан, возвращается в RAX.

EAX / EDX может бытьвместо RAX / RDX в 32-битных системах x86.

С соглашениями, которые передают указатель "this" в стеке (как стандартные соглашения x86 GCC), указатель возвращаемого значения обычно передается как скрытая секунда.параметр вместо первого.

1 голос
/ 18 июля 2011

Ваш исходный код возвращает копию структуры, созданной в функции - потому что вы возвращаете тип структуры, а не указатель на структуру.На что это похоже, так это то, что вся структура передается по значению с rax.Вообще говоря, для этого компилятор может создавать различные коды ассемблера, в зависимости от поведения вызывающего и вызываемого абонентов и соглашения о вызовах.

Правильный способ обработки структуры - использовать их в качестве параметров:

void func(t_test* t)
{
    t->i = 100;
    t->c = 'a';
}
0 голосов
/ 18 июля 2011

Указатель стека не изменяется в начале функции, поэтому распределение t_test не выполняется внутри функции и, следовательно, не освобождается функцией. Как это обрабатывается, зависит от используемого соглашения о вызовах. Если вы посмотрите, как вызывается функция, вам будет легче увидеть, как это делается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...