2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему На страницу Пред.  1, 2, 3, 4, 5  След.
 
 Re: Возврат структуры из функции
Сообщение22.07.2023, 18:46 
Заслуженный участник
Аватара пользователя


16/07/14
9201
Цюрих
oleg_2 в сообщении #1602092 писал(а):
И вроде бы естественно, если при оптимизации компилятор не стал бы прогонять возвращаемое значение через зарезервированное место на стеке, а сразу все изменения проводил сразу в переменной вызывающей функции, как если бы структура была передана в функцию по указателю.
Это называется return value optimization. И это происходит - в некоторых случаях стандарт требует это делать, во многих других разрешает (и на практике компиляторы делают).
oleg_2 в сообщении #1602092 писал(а):
Но я не знаю, где бы было бы полезно передавать структуры по значению. Ну например, если вызываемая функция выдаёт много значений, десять значений типа int, все их в структуру и возвращать структуру по значению. Но эту же структуру можно передать в функцию по указателю, казалось бы всё то же самое.
Представьте какую-нибудь такую ситуацию:
Используется синтаксис C++
struct MySuperStruct;
MySuperStruct f(int q) {
  MySuperStruct a, b;
  doMagic(a, b, q);
  return moreMagic(q) ? a : b;
}
В этой ситуации нам нужно будет создать две структуры, и одну из них уничтожить, но заранее неизвестно какую. Поэтому какую из структур - a или b - размещать по заранее подготовленному адресу - неизвестно.
oleg_2 в сообщении #1602092 писал(а):
Вот если бы при вызове функции место её на стеке копировалось бы в быструю память, а работы со структурами много, и каждый раз добираться до этих структур по указателю в самые отдалённые закутки памяти долго, тогда хорошо.
Стек и куча с точки зрения чтения из них устроены одинаково. Да, стек идет подряд, а переменные на куче - скорее всего нет, поэтому выше вероятность промахов. Но ни один из них не является "быстрой памятью". Есть кеши процессора, но они кучу от стека не отличают.

 Профиль  
                  
 
 Re: Возврат структуры из функции
Сообщение22.07.2023, 19:39 
Заслуженный участник
Аватара пользователя


01/09/13
4676
Но в данном случае не проще. И в случае, который несколько выше приводился, тоже не проще.
А то, если следовать такой логике, и С++ не нужен - только хардкор!

 Профиль  
                  
 
 Re: Возврат структуры из функции
Сообщение22.07.2023, 19:54 
Заслуженный участник
Аватара пользователя


16/07/14
9201
Цюрих
Genaa в сообщении #1602102 писал(а):
Вы уверены, что такое не повторится с другим компилятором?
Я уверен, что с любым компилятором, с которым такое повторится, произойдет и огромное количество аналогичных странных вещей.
Вообще, при всём (без шуток) уважении к Беллару, TinyCC не предназначен для практического использования, это упражнение по написанию компиляторов.

 Профиль  
                  
 
 Re: Возврат структуры из функции
Сообщение22.07.2023, 20:08 


02/10/12
308
mihaild в сообщении #1602106 писал(а):
Используется синтаксис C
struct MySuperStruct;
MySuperStruct f(int q) {
  MySuperStruct a, b;
  doMagic(a, b, q);
  return moreMagic(q) ? a : b;
}
 




Пример я вроде бы понял. Правда не очень понятно, что может таить в себе функция
doMagic(a, b, q);. Выбор структуры a или b зависит только от q. Значит выбор структуры не зависит от функции
doMagic(a, b, q);, и что там в ней такое, что нельзя сначала сделать выбор, а потом работать со структурами. У меня знаний и практики мало, поэтому пример мне кажется несколько надуманным. Но да, тогда надо по указателю передавать две структуры, а не одну, это я понял.

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

Да, чего-то я ещё не знаю и не понимаю, но всё равно спасибо за ответ.

Примечание.
Код из стартового сообщения у меня работает, компилятор GCC, только варнинг даёт, что не main, а int main. И мне в нескольких местах попадалось, что структуры в Си можно передавать по значению. Но я пока делал только по указателю.

 Профиль  
                  
 
 Re: Возврат структуры из функции
Сообщение22.07.2023, 20:30 
Заслуженный участник
Аватара пользователя


16/07/14
9201
Цюрих
oleg_2 в сообщении #1602119 писал(а):
Правда не очень понятно, что может таить в себе функция doMagic(a, b, q);
И не только Вы не поняли. Компилятор тоже может быть не в курсе (если функция определена в другой единице трансляции).
Подобных примеров - когда в функции есть несколько return, возвращающих значение с разными адресами, можно привести много, и на практике они встречаются часто.
oleg_2 в сообщении #1602119 писал(а):
В работе функции участвуют несколько структур, и если они переданы по значению, то они все в одном месте на стеке, и этот участок стека в кэш. А по указателям эти структуры могут быть в разных областях памяти, а можно ли в кэш скопировать несколько кусков памяти из разных мест, этого я не знаю. Я думал, что нельзя.
Можно. Кеш-линия - это сейчас типично 64 байта, и все кеш-линии независимы. Если размер структуры кратен 64 байтам, то для кеширования нет никакой разницы, на стеке она или в куче. Правда есть еще момент - вычисление адреса, для стека как правило он как правило вычисляется как rsp + константа времени компиляции, для кучи нужно хранить указатель, который в зависимости от ситуации может быть в регистре, в кеше или за которым еще отдельно придется идти в память.
oleg_2 в сообщении #1602119 писал(а):
Код из стартового сообщения у меня работает, компилятор GCC, только варнинг даёт, что не main, а int main
В каких-то древних стандартах можно было не указывать тип возвращаемого значения функции, и он подразумевался int. Сейчас это запретили, но большинство компиляторов такое всё еще разрешает.
oleg_2 в сообщении #1602119 писал(а):
И мне в нескольких местах попадалось, что структуры в Си можно передавать по значению. Но я пока делал только по указателю.
Да, можно, и в ситуациях, когда это правда уместно, так стоит сделать (благо с RVO это не влияет на производительность, зато позволяет уменьшить количество побочных эффектов).

 Профиль  
                  
 
 Re: Возврат структуры из функции
Сообщение22.07.2023, 21:14 
Заслуженный участник


31/12/05
1520
Genaa в сообщении #1602102 писал(а):
Речь про другое.
Я не понимаю, что принципиально меняется, если вместо структуры будет int.
код: [ скачать ] [ спрятать ]
Используется синтаксис C

int gerasim(int оtvet)
{
   otvet++;
   return otvet;
}

int main()
{
     int mu;
     mu = 5;  
     gerasim(mu);  /* mu == 5 , иначе проблема */
     mu = gerasim(mu); /* mu == 6 , иначе за что боролись? */

}
 

Genaa в сообщении #1602102 писал(а):
По любому, если локальную структуру возвращать , лучше явно выделить память под неё, либо динамически, либо передав адрес буфера , а не надеяться на мудрость компилятора, буде это допустимо.
Вы зачем-то придумали пример кода, который отличается от обсуждаемого, и теперь яростно с ним боретесь. Ваш аргумент отлично применим и к коду с int:

По любому, если локальный int возвращать, лучше явно выделить память под него, либо динамически, либо передав адрес буфера, а не надеяться на мудрость компилятора, буде это допустимо.

Genaa в сообщении #1602102 писал(а):
А играть в кошки-мышки с компилятором — результат видели в начале топика?
Видел. Человек по каким-то личным причинам принципиально хочет пользоваться игрушечным компилятором, который до 2017 года не понимал стандарт C89, да еще и не обновляет его версию, хотя в ней хотя бы этот баг исправлен. Печально.

 Профиль  
                  
 
 Re: Возврат структуры из функции
Сообщение22.07.2023, 22:55 
Аватара пользователя


27/07/16
557
tolstopuz в сообщении #1602126 писал(а):
По любому, если локальный int возвращать, лучше явно выделить память под него, либо динамически, либо передав адрес буфера, а не надеяться на мудрость компилятора, буде это допустимо.
Описывайте локальную структуру любого размера да и возвращайте её целиком. Компилятор разберется откуда взять память. Явное выделения вообще незачем. Тем более, что можно освободить вручную … не тo. Если можно c int, то можно со всеми. Размер не имеет значения. Расписал на уровне главной программы переменные структурного типа, передал их в параметры функций (они скопируются куда надо при вызове, а потом память освободится сама) . Всё чисто функционально. Железок годных для исполнения такого кода полным полно.. Короче, счастье близко. Лепота!

 Профиль  
                  
 
 Re: Возврат структуры из функции
Сообщение22.07.2023, 23:07 
Заслуженный участник
Аватара пользователя


16/07/14
9201
Цюрих
Genaa в сообщении #1602138 писал(а):
Описывайте локальную структуру любого размера да и возвращайте её целиком. Компилятор разберется откуда взять память
Genaa в сообщении #1602102 писал(а):
По любому, если локальную структуру возвращать , лучше явно выделить память под неё, либо динамически, либо передав адрес буфера , а не надеяться на мудрость компилятора
Так какой из вариантов правильный?

(ответ)

Оба неправильные. Как правило явное выделение памяти не нужно, но в некоторых случаях нужно, серебряной пули нет.

 Профиль  
                  
 
 Re: Возврат структуры из функции
Сообщение22.07.2023, 23:23 
Аватара пользователя


28/10/21
100
mihaild в сообщении #1602067 писал(а):
TheRuinedMap в сообщении #1602010 писал(а):
Можете
Разве?
C standard писал(а):
6.2.4.2. ... If an object is referred to outside of its lifetime, the behavior is undefined.
6.2.4.4. An object whose identifier is declared with no linkage and without the storage-class specifier static has automatic storage duration.
6.2.4.5. For such an object that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way.


Речь, как я понимаю, шла о механизме внутренней ("под капотом") реализации возврата больших объектов по значению. Мой комментарий про "можно" был сделан именно для этого уровня рассмотрения. В таком контексте неприменимы процитированные вами ограничения уровня языка. Если некая реализация знает, что значение останется в стеке (над вершиной стека) и будет некоторое время сидеть там нетронутым, никто не запрещает ей вычитать его оттуда.

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

Разумеется, программисту, работающему на уровне языка, такие трюки недоступны. Но об этом речи и не шло.

 Профиль  
                  
 
 Re: Возврат структуры из функции
Сообщение23.07.2023, 00:15 
Заслуженный участник


31/12/05
1520
Genaa в сообщении #1602138 писал(а):
Расписал на уровне главной программы переменные структурного типа
Наконец-то я это услышал. Каждая переменная должна быть расписана на уровне главной программы, прошита, пропечатана на каждой странице и согласована с Главным Конструктором. Суровое наследие 70-х...

 Профиль  
                  
 
 Re: Возврат структуры из функции
Сообщение23.07.2023, 00:45 
Аватара пользователя


27/07/16
557
mihaild в сообщении #1602139 писал(а):
Как правило явное выделение памяти не нужно, но в некоторых случаях нужно, серебряной пули нет.

Таки этот вариант жизнь давно выбрала, плюс предпочтения. Первый вариант — пародия на былое и думы)) Ничто не ново под Луной, но и … рога отдельно, и копыта стерлись.

 Профиль  
                  
 
 Re: Возврат структуры из функции
Сообщение23.07.2023, 01:33 
Заслуженный участник


31/12/05
1520
На самом деле, конечно, возврат структуры по значению в чистом C годится только в простейших случаях. Эта возможность - только один из кирпичиков, которые пришлось добавить в язык, чтобы выполнить давнюю мечту - уметь писать формулы со сложными типами так же удобно, как и с простыми.

Чем когда-то покорил Фортран? Если на ассемблере надо запрограммировать формулу, то есть что-то из школьного учебника математики, то приходится:
1) расписать порядок вычислений формулы до уровня отдельных операций;
2) под каждый промежуточный результат выделить место для временного хранения, в памяти или в регистрах.
На Фортране же можно было просто списать формулу из книги, возможно, слегка изменив синтаксис.

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

Когда проектировали язык Java (начало 1990-х), в нем специально не стали делать возможность создавать свои операции для пользовательских типов. То есть если a и b - целые числа, то a+b отлично работает, а если, скажем, деньги с десятичным округлением копеек, то язык специально спроектирован так, чтобы написать a+b было нельзя. Пишите MyCoolMoneyClass.add(a, b) и радуйтесь!

Чтобы возможность писать формулы для сложных типов появилась в языках общего назначения, пришлось сделать очень много:
- перегрузка операций, потому что инфиксная запись с одно-двухсимвольными операциями компактнее и понятнее, чем имена функций, скобки и запятые;
- поддержка исключений, потому что иначе у a/b нет возможности сказать о делении на ноль - код возврата же заменен возвращаемым значением;
- полиморфизм, либо в виде шаблонов (C++), либо в виде динамической типизации (Python), потому что у математиков не так много структур, зато они комбинируются необозримым числом способов, не дублировать же разработку для матриц целых чисел и матриц чисел с плавающей точкой четверной точности;
- богатые способы описания владения объектами (C++ 11) или сборка мусора (Python), потому что большая матрица все-таки будет возвращаться в куче, но в стеке должен быть какой-то кусок, указывающий на эту матрицу и дающий знать, кто и когда прибьет ее в куче;
- expression templates, которые позволяют собрать разобранное компилятором на отдельные операции выражение обратно в дерево и оптимизировать его.

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

Без всего этого букета возврат структур по значению в C полезен очень редко, но вектора на плоскости в собственном коде - как раз идеальный случай.

До сих пор существует много людей, которым доставляет удовольствие вручную ассемблировать выражение, в 80-х я тоже любил программировать на Б3-34. Сейчас вручную расписывать работу с простыми типами как-то несолидно, но на сложных типах, скажем, векторах и матрицах у этих людей есть возможность показать собственную важность:
- расписать структуры (возможно, даже на уровне главной программы) под промежуточные результаты;
- расписать порядок вычислений в виде вызовов функций по одной на строку.
Фактически это обычное ручное ассемблирование формулы, см. выше.

Еще я помню из 90-х стройные столбики strcpy, strncat и так далее. Тонны малочитаемого и небезопасного кода. Сам такое писал.

 Профиль  
                  
 
 Re: Возврат структуры из функции
Сообщение23.07.2023, 01:37 
Заслуженный участник
Аватара пользователя


16/07/14
9201
Цюрих
Genaa в сообщении #1602148 писал(а):
Таки этот вариант жизнь давно выбрала
Который из них первый? В любом случае, я легко найду кучу примеров современного кода в крупных проектах под оба варианта.
TheRuinedMap в сообщении #1602142 писал(а):
Речь, как я понимаю, шла о механизме внутренней ("под капотом") реализации возврата больших объектов по значению. Мой комментарий про "можно" был сделан именно для этого уровня рассмотрения
А, я думал, что "Вы можете" понимается как "код может".

 Профиль  
                  
 
 Re: Возврат структуры из функции
Сообщение23.07.2023, 01:46 
Заслуженный участник


31/12/05
1520
код: [ скачать ] [ спрятать ]
Используется синтаксис C++
#include <boost/numeric/ublas/tensor.hpp>
#include <iostream>

int main()
{
  using namespace boost::numeric::ublas::index;
  using tensor  = boost::numeric::ublas::tensor_dynamic<float>;
  auto ones     = boost::numeric::ublas::ones<float>{};

  tensor A = ones(3,4,5);
  tensor B = ones(4,6,3,2);

  tensor C = 2*ones(5,6,2) + A(_i,_j,_k)*B(_j,_l,_i,_m) + 5;
 
  // Matlab Compatible Formatted Output
  std::cout << "C=" << C << ";" << std::endl;
}

 Профиль  
                  
 
 Re: Возврат структуры из функции
Сообщение23.07.2023, 13:09 
Заслуженный участник


18/01/15
3237
tolstopuz в сообщении #1601717 писал(а):
Я проверил: версия 0.9.26-win64 от 2013-02-15 печатает "2 0" и подвисает,
Да, вот эта самая версия у меня и есть. Скачал я ее, впрочем, не семь лет назад, а порядка года, в связи с переездом на другой компьютер. (Почему именно эту версию взял --- не помню. Были какие -то причины.).
tolstopuz в сообщении #1601717 писал(а):
Я не представляю, как после этого можно доверять результатам программ, скомпилированных этим компилятором.
Как говорится, "можно, если осторожно", а именно, если достаточно хорошо эти программы тестировать. В случае, если функция не работает, и причина этого в упор не видна, можно предположить ошибку компилятора, а также догадаться, в чем она состоит, и как ее компенсировать. Например, еще раньше я заметил (тоже разбираясь с непонятной ошибкой), что при описании массива он, вообще говоря, не заполняется нулями. Ну и что, при необходимости заполняю сам руками, небо на землю не рухнуло.

В книжках и статьях тоже бывают ошибки, тем не менее люди же их читают, и извлекают для себя пользу. Или, используя другую аналогию, если на дороге есть некоторое количество ям, по ней всё равно можно доехать из пункта А в пункт Б, при известной осторожности. Тем более, если в машине нет никого особо привередливого в отношении комфорта езды, кроме себя самого (т.е., программы я пишу только сам для себя). Такая вот у меня философия. (Я её никому не предлагаю, если что, но вот такая она есть.)

tolstopuz в сообщении #1601717 писал(а):
а версия 0.9.27-win64 от 2017-12-17 правильно печатает "4 6".
Спасибо, буду иметь в виду.

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 65 ]  На страницу Пред.  1, 2, 3, 4, 5  След.

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group