реклама на сайте
подробности

 
 
> Возврат структуры из функции, косяк Кернигана-Ричи
ViKo
сообщение Feb 9 2015, 07:04
Сообщение #1


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Перечитывая книжку классиков по программированию на языке С, обнаружил, что они возвращают из функции целую структуру. Конкретно, структуру из двух координат точки. Как это (возможно)?
У Шилдта этот вопрос вежливо замалчивается...
Go to the top of the page
 
+Quote Post
2 страниц V   1 2 >  
Start new topic
Ответов (1 - 21)
Grizzzly
сообщение Feb 9 2015, 07:26
Сообщение #2


Знающий
****

Группа: Свой
Сообщений: 565
Регистрация: 22-02-13
Пользователь №: 75 748



Цитата(ViKo @ Feb 9 2015, 10:04) *
Как это (возможно)?

Компилатор формирует при вызове функции место в стеке и передает адрес на эту область в функцию. В самой функции структура - локальная переменная (в стеке). Для возвращения структуры компилятор копирует поля локальной структуры по тому адресу, который предоставила функция. По сути у функции есть еще один "невидимый" входной параметр - адрес для возврата.
Go to the top of the page
 
+Quote Post
ViKo
сообщение Feb 9 2015, 07:45
Сообщение #3


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Тогда это должно называться возврат указателя на структуру.
Go to the top of the page
 
+Quote Post
Bear_ku
сообщение Feb 9 2015, 08:15
Сообщение #4


Частый гость
**

Группа: Участник
Сообщений: 154
Регистрация: 9-09-11
Пользователь №: 67 076



А вы дерзкий! Я бы не решился возвращать из функции указатель на локальную переменную )
Go to the top of the page
 
+Quote Post
SM
сообщение Feb 9 2015, 08:29
Сообщение #5


Гуру
******

Группа: Свой
Сообщений: 7 946
Регистрация: 25-02-05
Из: Moscow, Russia
Пользователь №: 2 881



Цитата(Bear_ku @ Feb 9 2015, 11:15) *
А вы дерзкий! Я бы не решился возвращать из функции указатель на локальную переменную )

А никто так и не делает. Возвращают данные, которые компилятор неявно копирует из локальной переменной в lvalue, указатель на который также неявно передается в ф-цию. То есть, возвращаются только данные.
Go to the top of the page
 
+Quote Post
ViKo
сообщение Feb 9 2015, 08:52
Сообщение #6


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Ничего не понимаю. То, что передается в функцию, не имеет отношения к тому, что выдается. Именно так показано в примерах в книге K&R. Давайте ограничимся возвратом.
Указатель на локальную переменную выдавать не будем. Будем выдавать указатель на глобальную структуру. Хотя, зачем его выдавать, можно просто флаг готовности результата выдать.
Если можно выдать структуру, то почему нельзя выдать аналогичным образом массив?
Go to the top of the page
 
+Quote Post
Bear_ku
сообщение Feb 9 2015, 08:52
Сообщение #7


Частый гость
**

Группа: Участник
Сообщений: 154
Регистрация: 9-09-11
Пользователь №: 67 076



Цитата(ViKo @ Feb 9 2015, 12:45) *
Тогда это должно называться возврат указателя на структуру.
Вообще мой пост был обращен к этому посту.
Ну вот так уж повелось. Структуры и присваивать можно друг другу, а вот массивы уже увы. На счет глобальных структур вы погорячились, никто не мешает работать подобным образом и с локальными переменными. Просто примите это как должное и живите дальше спокойно )

Сообщение отредактировал Bear_ku - Feb 9 2015, 09:03
Go to the top of the page
 
+Quote Post
SM
сообщение Feb 9 2015, 09:00
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 7 946
Регистрация: 25-02-05
Из: Moscow, Russia
Пользователь №: 2 881



Цитата(ViKo @ Feb 9 2015, 11:52) *
Если можно выдать структуру, то почему нельзя выдать аналогичным образом массив?


Можно. Для этого он должен быть внутри структуры, которая может быть из одного массива.

А аналогичным образом нельзя потому, что массив есть указатель на кусок предвыделенной на этапе компиляции памяти. А структура - есть сам набор данных, а не указатель на него. Поэтому "return <массив>" - ошибочен, так как получается требование возврата указателя, возможно на локальный буфер, время жизни которого кончается с выходом из ф-ции. А "return <структура>" - корректно, так как это требования возврата самих данных, хранящихся в структуре, а не указателя на структуру. Поэтому присваивания и возвраты массивов изначально не предусмотрены, а структур - пожалуйста.
Go to the top of the page
 
+Quote Post
andrew_b
сообщение Feb 9 2015, 09:05
Сообщение #9


Профессионал
*****

Группа: Свой
Сообщений: 1 975
Регистрация: 30-12-04
Из: Воронеж
Пользователь №: 1 757



Вас же не смущает возврат, например, целого числа. Почему вас смущает возврат структуры?
Go to the top of the page
 
+Quote Post
ViKo
сообщение Feb 9 2015, 09:09
Сообщение #10


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Цитата(SM @ Feb 9 2015, 12:00) *
А аналогичным образом нельзя потому, что массив есть указатель на кусок предвыделенной на этапе компиляции памяти. А структура - есть сам набор данных, а не указатель на него. Поэтому "return <массив>" - ошибочен, так как получается требование возврата указателя, возможно на локальный буфер, время жизни которого кончается с выходом из ф-ции. А "return <структура>" - корректно, так как это требования возврата самих данных, хранящихся в структуре, а не указателя на структуру. Поэтому присваивания и возвраты массивов изначально не предусмотрены, а структур - пожалуйста.

Кто же им (K&R) мешал сделать структуры и массивы одинаковыми в смысле обращения к ним - или адрес, или агрегатный тип данных?
Допустим, получили результат функции - структуру. Единственное, что приходит на ум - сохранить ее в своей (локальной, глобальной) переменной - структуре. И потом использовать ее отдельные члены. К чему вся эта чехарда?

Цитата(andrew_b @ Feb 9 2015, 12:05) *
Вас же не смущает возврат, например, целого числа. Почему вас смущает возврат структуры?

Число или указатель передается одним регистром.
Go to the top of the page
 
+Quote Post
SM
сообщение Feb 9 2015, 09:11
Сообщение #11


Гуру
******

Группа: Свой
Сообщений: 7 946
Регистрация: 25-02-05
Из: Moscow, Russia
Пользователь №: 2 881



Цитата(ViKo @ Feb 9 2015, 12:08) *
Единственное, что приходит на ум - сохранить ее в своей (локальной, глобальной) переменной - структуре. И потом использовать ее отдельные члены.

Ну вот именно так они и сделали! Оператор "return" сохраняет ее в этой самой, (локальной, глобальной) переменной. Что не нравится-то?

Цитата(ViKo @ Feb 9 2015, 12:09) *
Число или указатель передается одним регистром.

Ни в одном стандарте С нет понятия регистров, и вообще, каких либо требований к реализации на каких либо архитектурах. Так что, это высказывание совершенно некорректно. А вдруг у процессора вообще регистров нет, к примеру, только стек, и все?
Go to the top of the page
 
+Quote Post
andrew_b
сообщение Feb 9 2015, 09:13
Сообщение #12


Профессионал
*****

Группа: Свой
Сообщений: 1 975
Регистрация: 30-12-04
Из: Воронеж
Пользователь №: 1 757



Цитата(ViKo @ Feb 9 2015, 12:09) *
Число или указатель передается одним регистром.
А если число "шире", чем регистр. Например, long long int? Или вообще double?
Go to the top of the page
 
+Quote Post
ViKo
сообщение Feb 9 2015, 09:27
Сообщение #13


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Будем считать что я осознал и раскаялся... (хотя разницу с массивом считаю надуманной)

Вот что пишут в Procedure Call Standard for the ARM Architecture.

5.4 Result Return
The manner in which a result is returned from a function is determined by the type of that result.
For the base standard:
 A Half-precision Floating Point Type is converted to single precision and returned in r0.
 A Fundamental Data Type that is smaller than 4 bytes is zero- or sign-extended to a word and returned in r0.
 A word-sized Fundamental Data Type (e.g., int, float) is returned in r0.
 A double-word sized Fundamental Data Type (e.g., long long, double and 64-bit containerized vectors) is
returned in r0 and r1.
 A 128-bit containerized vector is returned in r0-r3.
 A Composite Type not larger than 4 bytes is returned in r0. The format is as if the result had been stored in
memory at a word-aligned address and then loaded into r0 with an LDR instruction. Any bits in r0 that lie
outside the bounds of the result have unspecified values.
 A Composite Type larger than 4 bytes, or whose size cannot be determined statically by both caller and
callee, is stored in memory at an address passed as an extra argument when the function was called (§5.5,
rule A.4). The memory to be used for the result may be modified at any point during the function call.

Цитата(SM @ Feb 9 2015, 12:11) *
Ну вот именно так они и сделали! Оператор "return" сохраняет ее в этой самой, (локальной, глобальной) переменной. Что не нравится-то?

Чтобы использовать структуру, выданную из функции, ее нужно вручную присвоить своей.
Я же не могу выполнить, например, следующее:
myStruct.a = func(...).a;

А сначала
myStruct = func(...);
потом работать с членами myStruct.
Или при возврате структура останется в тех же регистрах, и будет считаться уже myStruct?
Go to the top of the page
 
+Quote Post
SSerge
сообщение Feb 9 2015, 09:33
Сообщение #14


Профессионал
*****

Группа: Свой
Сообщений: 1 719
Регистрация: 13-09-05
Из: Novosibirsk
Пользователь №: 8 528



Цитата(ViKo @ Feb 9 2015, 16:09) *
Кто же им (K&R) мешал сделать структуры и массивы одинаковыми в смысле обращения к ним - или адрес, или агрегатный тип данных?

Да они поначалу так и собирались делать, т.е. неуклонно следовать правилу: передача параметров (и возврат) производится по значению.
Но потом подумали и сделали "для удобства" исключение для массивов.

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

Фортран, кстати, придерживается диаметрально противоположной стратегии, там все параметры передаются по ссылке. Так что даже для передачи константы приходится завести для неё где-то в памяти ячейку и передавать в функцию её адрес.


--------------------
Russia est omnis divisa in partes octo.
Go to the top of the page
 
+Quote Post
SM
сообщение Feb 9 2015, 09:34
Сообщение #15


Гуру
******

Группа: Свой
Сообщений: 7 946
Регистрация: 25-02-05
Из: Moscow, Russia
Пользователь №: 2 881



Цитата(ViKo @ Feb 9 2015, 12:27) *
Я же не могу выполнить, например, следующее:
myStruct.a = func(...).a;


Это Вас кто-то жестко обманул. Можете.

myStruct.a = (func(...)).a;
Go to the top of the page
 
+Quote Post
jcxz
сообщение Feb 9 2015, 12:09
Сообщение #16


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(Grizzzly @ Feb 9 2015, 13:26) *
Компилатор формирует при вызове функции место в стеке и передает адрес на эту область в функцию. В самой функции структура - локальная переменная (в стеке). Для возвращения структуры компилятор копирует поля локальной структуры по тому адресу, который предоставила функция. По сути у функции есть еще один "невидимый" входной параметр - адрес для возврата.

Не смотрел в листинг, но сомневаюсь что компилятор что-то передаёт неявно в функцию (кроме указателя this член-функциям), так как соглашения вызова расписаны и там нет предусмотрено это. Да и незачем.
Если функция возвращает структуру, то она "сама об этом знает". И она знает, что вызывающая функция позаботилась о месте в стеке, и просто вычисляет адрес, исходя из SP в точке входа в функцию. Думаю так.
Вообще - это дефолтный способ передачи и возврата параметров в функцию - через стек. И только оптимизатор заменяет стек на входе и выходе функции на регистры (если может и это не запрещено опциями).
Так что компилятор в точке вызова функции по дефолту должен сперва сформировать стековый фрейм для возвращаемого значения (вне зависимости от его типа), а потом поместить в него входные аргументы вызова.

И ещё замечание: насколько я знаю, некоторые компиляторы (согласно соглашениям вызова) могут возвращать не в одном регистре, а в паре. Т.е. в IAR for ARM - если возвращаемое значение - long long, то используется
регистровая пара R0:R1, а не стек. То же самое и для структур - если размер структуры влезает в одну регистровую пару, то она будет возвращена через регистры (один R0 или пару R0:R1), а не через стек.

Вроде так.
Go to the top of the page
 
+Quote Post
SM
сообщение Feb 9 2015, 12:41
Сообщение #17


Гуру
******

Группа: Свой
Сообщений: 7 946
Регистрация: 25-02-05
Из: Moscow, Russia
Пользователь №: 2 881



Цитата(jcxz @ Feb 9 2015, 15:09) *
Не смотрел в листинг, но сомневаюсь что компилятор что-то передаёт неявно в функцию

Вот неверующих-то развелось sm.gif Вообще-то, это в документации на ABI прописано. Вот пример:

Код
typedef struct {
  int a;
  int b;
  int c;
} T;

T my_func (int a)
{
  T v;

  v.a = a;
  v.b = ~a;
  v.c = -a;
  return v;
}

static T s;

void my_func1(int a)
{
  s = my_func(a);
}


а вот фрагмент вызова ф-ции:

Код
; 30   :
; 31   :   s = my_func(a);

    mov    eax, DWORD PTR _a$[ebp]
    push    eax
    lea    ecx, DWORD PTR $T792[ebp]
    push    ecx
    call    _my_func
    add    esp, 8
    mov    edx, DWORD PTR [eax]
    mov    DWORD PTR _s, edx
    mov    ecx, DWORD PTR [eax+4]
    mov    DWORD PTR _s+4, ecx
    mov    edx, DWORD PTR [eax+8]
    mov    DWORD PTR _s+8, edx


push eax - педерача параметра "a"
lea и push ecx - неявная передача указателя на область стека для возврата структуры
затем вызов функции, и копирование результата в статическую переменную.

А вот если бы структура состояла бы из двух int, а не трех - то все бы упростилось. Оба поля вернулись бы в edx:eax без стека и неявных указателей.
Go to the top of the page
 
+Quote Post
Grizzzly
сообщение Feb 9 2015, 12:42
Сообщение #18


Знающий
****

Группа: Свой
Сообщений: 565
Регистрация: 22-02-13
Пользователь №: 75 748



Цитата(jcxz @ Feb 9 2015, 15:09) *
Не смотрел в листинг, но сомневаюсь что компилятор что-то передаёт неявно в функцию (кроме указателя this член-функциям), так как соглашения вызова расписаны и там нет предусмотрено это. Да и незачем.

В компиляторе VC 2005 в функцию передается на регистре адрес зарезервированный области. Возможно, в более поздних версиях и других компиляторах этого нет.
Go to the top of the page
 
+Quote Post
SM
сообщение Feb 9 2015, 12:43
Сообщение #19


Гуру
******

Группа: Свой
Сообщений: 7 946
Регистрация: 25-02-05
Из: Moscow, Russia
Пользователь №: 2 881



Цитата(Grizzzly @ Feb 9 2015, 15:42) *
Возможно, в более поздних версиях и других компиляторах этого нет.

Везде есть. Совместимость то по ABI у них полная.
Go to the top of the page
 
+Quote Post
Grizzzly
сообщение Feb 9 2015, 12:45
Сообщение #20


Знающий
****

Группа: Свой
Сообщений: 565
Регистрация: 22-02-13
Пользователь №: 75 748



Цитата(SM @ Feb 9 2015, 15:41) *
а вот фрагмент вызова ф-ции:

Спасыб! wink.gif
Go to the top of the page
 
+Quote Post
ar__systems
сообщение Mar 13 2015, 15:17
Сообщение #21


self made
****

Группа: Свой
Сообщений: 855
Регистрация: 7-03-09
Из: Toronto, Canada
Пользователь №: 45 795



Цитата(SM @ Feb 9 2015, 03:29) *
А никто так и не делает. Возвращают данные, которые компилятор неявно копирует из локальной переменной в lvalue, указатель на который также неявно передается в ф-цию. То есть, возвращаются только данные.


Компилятор ничего подобного не делает, и никаких "неявных адресов возврата" в функцию не передается.

Функция находит свои параметры, локальные переменные и результат относительно указателя стека.


Цитата(Grizzzly @ Feb 9 2015, 07:42) *
В компиляторе VC 2005 в функцию передается на регистре адрес зарезервированный области. Возможно, в более поздних версиях и других компиляторах этого нет.

Какой еще зарезервироавнной области? Что вы такое говорите? "С" calling convention никто не отменял и компилятор того что вы говорите просто не может делать. Я думаю вы неправильно поняли листинг.
Go to the top of the page
 
+Quote Post
XVR
сообщение Mar 16 2015, 12:21
Сообщение #22


Гуру
******

Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847



Цитата(ar__systems @ Mar 13 2015, 18:17) *
Какой еще зарезервироавнной области? Что вы такое говорите? "С" calling convention никто не отменял и компилятор того что вы говорите просто не может делать. Я думаю вы неправильно поняли листинг.
Ох уж эти сказочники rolleyes.gif 'Не может, не может', еще и "С" calling convention сюда приплели biggrin.gif
Вот, выдержка из вашего любимого calling conversion (прямо с сайта MS)
Цитата
On x86 plaftorms, all arguments are widened to 32 bits when they are passed.
Return values are also widened to 32 bits and returned in the EAX register, except for 8-byte structures, which are returned in the EDX:EAX register pair.
Larger structures are returned in the EAX register as pointers to hidden return structures. Parameters are pushed onto the stack from right to left. Structures that are not PODs will not be returned in registers.

Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 28th July 2025 - 01:29
Рейтинг@Mail.ru


Страница сгенерированна за 0.01595 секунд с 7
ELECTRONIX ©2004-2016