Последний раз редактировалось tolstopuz 23.07.2023, 01:33, всего редактировалось 1 раз.
На самом деле, конечно, возврат структуры по значению в чистом 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 и так далее. Тонны малочитаемого и небезопасного кода. Сам такое писал.
|