Итак, во-первых, у вас есть ошибка:
$ cc -O2 -S test.c
test.c: In function ‘add’:
test.c:7:12: warning: function returns address of local variable
Возврат адреса локальной переменной имеет неопределенное поведение, тогда и только тогда, когда вызывающий использует это значение; вот почему ваш компилятор сгенерировал код, который возвратил нулевой указатель, который sh сканирует программу, если используется, но безвреден в противном случае. Фактически, моя копия G CC генерирует только это для add
:
add:
xorl %eax, %eax
ret
, потому что такая обработка возвращаемого значения делает другие операции в add
мертвым кодом.
(Ограничение «только если используется» также является причиной того, что мой компилятор выдает предупреждение, а не серьезную ошибку.)
Теперь, если я изменю вашу программу, чтобы она имела четко определенное поведение, например
#include <stdio.h>
#include <string.h>
void add(int *sum, int a, int b)
{
int result = a+b;
memcpy(sum, &result, sizeof(int));
}
int main(void)
{
int a;
add(&a, 1, 2);
printf("%i\n",a);
return 0;
}
тогда я действительно вижу ассемблерный код, в котором вызов memcpy
заменен встроенным кодом:
add:
addl %edx, %esi
movl %esi, (%rdi)
ret
Это особенность многих современных компиляторов C: они знают, что некоторые из функций библиотеки C это делают и могут встраивать их, когда это имеет смысл. (Вы можете видеть, что в этом случае сгенерированный код меньше и быстрее, чем был бы при фактическом вызове memcpy
.)
G CC позволяет мне отключить эту функцию с помощью команды -line option:
$ gcc -O2 -ffreestanding test.c
$ sed -ne '/^add:/,/cfi_endproc/{; /^\.LF[BE]/d; /\.cfi_/d; p; }' test.s
add:
subq $24, %rsp
addl %edx, %esi
movl $4, %edx
movl %esi, 12(%rsp)
leaq 12(%rsp), %rsi
call memcpy@PLT
addq $24, %rsp
ret
В этом режиме вызов memcpy
в add
обрабатывается так же, как вызов printf
в main
. В вашем компиляторе могут быть похожие параметры.