Подтвердить что ты не робот

Как функция ACTUALLY возвращает переменную struct в C?

Как функция возвращает значение функции для меня, просто для запуска:

int f()
{
  int a = 2;
  return a;
}

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

Давайте вернемся к C для ситуации, когда я хочу вернуть переменную struct not pointer:

struct inventory
{
    char name[20];
    int number;
};
struct inventory function();

int main()
{
    struct inventory items;
    items=function();
    printf("\nam in main\n");
    printf("\n%s\t",items.name);
    printf(" %d\t",items.number); 
    getch();
    return 0;
}

struct inventory function()
{
    struct inventory items;
    printf(" enter the item name\n ");
    scanf(" %s ",&items.name );
    printf(" enter the number of items\n ");
    scanf("%d",&items.number );
    return items;
}

Код forked from: qaru.site/info/160866/...

Вот сделка,

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

Вопрос: Хотя он работает, но как function() фактически вернул переменную struct и скопировал в items переменную в main когда он должен умереть с возвратом function().

Я, конечно, пропустил важную вещь, подробное объяснение поможет.:)

EDIT: Другие ответы на отзывы:

4b9b3361

Ответ 1

Детали широко варьируются, вызывая соглашение. Некоторые ABI не имеют соглашения о вызовах для передачи целых структур, и в этом случае компилятор может свободно делать то, что, по его мнению, имеет смысл.

Примеры включают:

  • Передача и возврат всей структуры как серии последовательных регистров (часто используется с "малыми" структурами)
  • Размещение всей структуры как блока аргументов в стеке
  • Выделение пустого аргумента, достаточно большого для хранения структуры, для заполнения возвращаемым значением
  • Передача (стека) адреса структуры в качестве аргумента (как если бы функция была объявлена ​​ void function(struct inventory *))

Любая из этих реализаций может соответствовать спецификации C здесь. Но посмотрим на конкретную реализацию: вывод моего кросс-компилятора GCC ARM.

Компиляция кода, который вы дали, дает мне следующее:

main:
    stmfd   sp!, {fp, lr}
    add fp, sp, #4
    sub sp, sp, #48
    sub r3, fp, #52
    mov r0, r3
    bl  function(PLT)

Операнды назначения всегда слева. Вы можете видеть, что программа резервирует пространство стека, а затем передает адрес пространства стека как r0 (первый аргумент в вызове вызова ARM EABI). function не принимает аргументов, поэтому этот аргумент явно является искусственным аргументом, добавленным нашим компилятором.

function выглядит следующим образом:

function:
    stmfd   sp!, {r4, fp, lr}
    add fp, sp, #8
    sub sp, sp, #36
    str r0, [fp, #-40]
    ldr r3, .L6

        ...
    add r2, pc, r2
    mov r0, r2
    mov r1, r3
    bl  scanf(PLT)
    ldr r3, [fp, #-40]
    mov ip, r3
    sub r4, fp, #36
    ldmia   r4!, {r0, r1, r2, r3}
    stmia   ip!, {r0, r1, r2, r3}
    ldmia   r4, {r0, r1}
    stmia   ip, {r0, r1}
    ldr r0, [fp, #-40]
    sub sp, fp, #8
    ldmfd   sp!, {r4, fp, pc}

Этот код в основном фиксирует единственный аргумент в [fp, #-40], а затем загружает его и начинает записывать данные по адресу, на который он указывает. В конце он снова возвращает это значение указателя в r0. Фактически, компилятор сделал подпись функции в

struct inventory *function(struct inventory *)

где возвращенная структура распределяется в стеке вызывающим, передается и возвращается.

Ответ 2

Вам не хватает самой очевидной вещи, которая есть для способа передачи/возвращения: C все передается arounbd по значению или по крайней мере: он ведет себя таким образом.

То есть:

struct foo some_f( void )
{
    struct foo local = {
       .member = 123,
       .bar = 2.0
    };
    //some awsome code
    return local;
}

Будет работать, просто отлично. Если структура мала, то возможно, что этот код создаст локальную структурную переменную и вернет копию этой структуры вызывающему.
В других случаях, однако, этот код будет примерно переведен на:

void caller()
{
    struct foo hidden_stack_space;
    struct foo your_var = *(some_f(&hidden_stack_space));
}
//and the some_f function will behave as:
struct foo * some_f(struct foo * local)
{
    //works on local and
    return local;
}

Ну, это не совсем то, что происходит все время, но это сводится к этому, более или менее. Результат будет таким же, но компиляторы могут вести себя по-другому в этом случае.

Нижняя строка: C возвращает значение, поэтому ваш код работает нормально. Однако есть подводные камни:

struct foo
{
    int member1;
    char *str;
};
struct foo some_f()
{
    char bar[] = "foobar";
    struct foo local = {
        .member1 = 123,
        .str = &bar[0]
    };
    return local;
}

Опасно: указатель, присвоенный local.str, указывает на память, которая будет выпущена после возврата структуры. В этом случае проблемы, которые вы ожидали с помощью этого кода, верны: эта память больше не является (или больше не действительна).
Просто потому, что указатель - это переменная, значение которой является адресом mem, и это значение возвращается/назначается.

Ответ 3

Структура, по крайней мере большая, будет выделена и возвращена в стек и будет выскользнуть из стека (если вообще) вызывающим. Компилятор попытается выделить его в том же месте, где вызывающий абонент ожидает его поиска, но он сделает копию, если это невозможно. Возможно, но не обязательно, что есть указатель на структуру, возвращаемый через регистры.

Конечно, детали будут зависеть от архитектуры.