LINUX.ORG.RU

defer в C быть!

 ,


0

9

Привет, ЛОР!

Как я писал три года назад, в стандарт языка Си было предложено добавить выражение defer, выполняющее функцию или блок кода по выходу из области видимости, где оно было объявлено.

На днях данное предложение получило официальный статус и, скорее всего, defer появится в будущем стандарте C2y.

При этом, defer почти наверняка не будет добавлен в C++, так как его использование будет конфликтовать с другими частями этого языка.

Ссылка на пост в блоге автора: https://thephd.dev/c2y-the-defer-technical-specification-its-time-go-go-go

Спецификация: https://thephd.dev/_vendor/future_cxx/technical%20specification/C%20-%20defer/C%20-%20defer%20Technical%20Specification.pdf

Ответ на: комментарий от anonymous

Мало кто знает, но кроме точки с запятой, есть еще запятая.

Можно так:

a = 1;
b = 2;

А можно и так:

a = 1, b = 2;

Запятая как раз в циклах применяется.

Vic
()
Ответ на: комментарий от Vic

По той же самой причине, в синтаксисе си-функций нет возврата множества переменных, поэтому нам, как и процессору, надо использовать адреса переменных.

Возврат обычно кладётся в первый регистр.
Что мешало возвраты разложить по регистрам, а что не влезло вернуть по адресу?
Впрочем, тебе никто не запрещает вернуть структуру во внутреннем коде (но в интерфейсах так делать не стоит - ABI между компиляторами может различаться)
Так что отсутствие множественного возврата - действительно одно из неприятных ограничений языка. В rust можно вернуть enum, в c++ variant - но в сишном интерфейсе этого не сделать...

mittorn ★★★★★
()
Ответ на: комментарий от Vic

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от Iron_Bug

Интеловский старый вроде бы умел, причём оба варианта синтаксиса - и gnu и msvc, включая 64битный.
А новый icx скорее всего ведёт себя полностью как clang - они давно выкинули собственный фронтенд.

mittorn ★★★★★
()
Ответ на: комментарий от mittorn

насчёт «возвратов»: return - это условность. это действительно стек при выходе. но можно без ограничений передавать в функции параметры-указатели и записывать туда что заблагорассудится, в любых количествах. более того, как правило в Си возврат данных идёт через параметры, а return - это просто логический результат выполнения операции: успешно/неуспешно/код ошибки.

Iron_Bug ★★★★★
()
Ответ на: комментарий от Iron_Bug

В чём врака-то? Я расчитывал на помощь профессионалов Си в такой простой задаче, но увы, не всем дано написать цикл от a до b без ошибок :(

unC0Rr ★★★★★
()
Ответ на: комментарий от zurg

А большой разницы нет, emum/variant можно воспринимать как кортеж из enum и union

mittorn ★★★★★
()
Ответ на: комментарий от mittorn

я не люблю гну-тый синтаксис ассемблера. какой-то он плохочитаемый, с обилием этих процентов. интелловский читается и пишется приятнее. к тому же, в интелловском логика аргументов всяких mov и прочих операторов какая-то более естественная. хотя, может, это мне так кажется, потому что я ещё в детстве читала Абеля и писала на интелловском ассемблере. сила привычки.

Iron_Bug ★★★★★
()
Ответ на: комментарий от mittorn

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от anonymous

я не интересовалась, кто это разрабатывал. но штука была удобная. именно для профилирования и отладки.

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

Iron_Bug ★★★★★
()
Последнее исправление: Iron_Bug (всего исправлений: 1)
Ответ на: комментарий от Iron_Bug

Современные calling conversion обычно подразумевают передачу как аргументов, так и возврата через регистры. И в этом случае передача их по адресу заставляет явно передавать их через стек.
И если во внутренней функции компилятор может такую конструкцию упростить до записи в регистр, то на внешней у него не остаётся такой возможности.
В общем, передача состояния ошибки через указатель (притом что проверяться оно будет тут же через регистр) - докинет несколько ненужных операций с памятью и хотелось бы для столь базовой задачи иметь чисто регистровое решение.
Скорее всего в случае возврата структуры на amd64/arm64 был бы сгенерирован оптимальный код, использующий первые 2 регистра (но я не проверял), на i386 же зависит от ABI. msvc, насколько я помню, передаст адрес структуры для возврата в аргумент, gcc же генерирует что-то иное, но тоже скорее всего на стеке

mittorn ★★★★★
()
Ответ на: комментарий от mittorn

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от Iron_Bug

Я подозреваю, что плохо читается не сам gnu'тый синтаксис, а AT&T синтаксис, который он использует.
В армовом gnu-ассемблере такой проблемы нет - там проценты только в подстановках регистров:
https://raw.githubusercontent.com/FWGS/http-folder-uploader/master/armeabi.h

mittorn ★★★★★
()
Ответ на: комментарий от anonymous

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от mittorn

ну, это понятно. AT&T и есть «гнутый ассемблер». хотя, конечно, его придумали не GNU. просто он по дефолту всегда шёл в их софте. хотя тот же gcc спокойно жрёт и инетлловский синтаксис.

Iron_Bug ★★★★★
()
Ответ на: комментарий от Iron_Bug

Скорее закопали именно собственную реализацию фронтенда для языков. Сейчас llvm/clang вытесняет всё т.к для корпораций это буквально халявная кодовая база, изменения в которой не требуется открывать (в отличие от того же gcc) и которая оперативно получает поддержку новых стандартов

mittorn ★★★★★
()
Последнее исправление: mittorn (всего исправлений: 2)
Ответ на: комментарий от mittorn

Что мешало возвраты разложить по регистрам, а что не влезло вернуть по адресу?

Размещение возвращаемого результата в памяти (в адресном пространстве).

  • Чтобы вернуть один результат через регистры, уже нужно разное количество регистров, в зависимости от типа возвращаемого результата: для int один регистр, для double уже два регистра и т.д. Вместо регистров, если их мало, используется аппаратный стек (push/pop). Если аппаратный стек маленький, используется программный стек (вызов функций).
  • Большой массив или структура в регистры точно не поместится, поэтому, для них заранее выделяется память в стеке «взывателя», а код генерируется так, как если бы вы сами подавали адрес переменной во входных параметрах.
  • Еще, результат возвращается методом копирования, что не всегда рационально, особенно для больших структур и массивов.
Vic
()
Ответ на: комментарий от mittorn

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от Iron_Bug

… поэтому данные передают указателями

И с использованием «&».

Иногда приеняю такой метод разработки APi.
Пусть имеется сорок функций, которые парсят какой-то файл.
Чтобы не плодить много параметров в функциях, создаём структуру
в которую помещаем всё необходимиое для раьоты функций.
Супер быстро всё работает и функции имеют максимум два параметра.

anonymous
()
Ответ на: комментарий от mittorn

вот потому я шланг и не люблю. есть ещё много причин, но эта, пожалуй, главная. они, на самом деле, убивают опенсорц.

Iron_Bug ★★★★★
()
Последнее исправление: Iron_Bug (всего исправлений: 1)
Ответ на: комментарий от anonymous

А с динаическими переменными вовсе песня.
Можно управлять временем их жизни, …

anonymous
()
Ответ на: комментарий от Iron_Bug

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

mittorn ★★★★★
()
Последнее исправление: mittorn (всего исправлений: 1)
Ответ на: комментарий от anonymous

Если не ошибаюсь, то филал в Новгороде.

В Нижнем только. Там @yoghurt работал одно время ЕМНИП. Но они этот офис закрыли по некоторым причинам.

hateyoufeel ★★★★★
() автор топика
Ответ на: комментарий от mittorn

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

Это хорошо. Должна быть конкуренция, иначе, они сами превратятся в тех же корпоратов, против которых объединяли людей, и люди объединяться уже против них.

Vic
()
Ответ на: комментарий от anonymous

И ещё один неприятный момент - в си нет обозначения для in/out параметров. Да, есть const. Но его отсутствие не означает, что функция может только писать по переданному указателю.
Возможно явный синтаксис для out параметров решил бы эту проблему - так компилятору и анализатору не известно, что эта переданная и возможно неинициализированная переменная не будет читаться внешней функцией

mittorn ★★★★★
()
Ответ на: комментарий от mittorn

а эффективного решения для полобного возврата в интерфейсах нет

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от Vic

Глобальную переменную объявить можно (просто напиши ее вне функций)

Ты невнимательно читаешь, что я пишу. Нельзя объявить глобальную неинициализированную переменную. Если я объявляю глобальный массив в 10 кибибайтов, это значит, что при каждом запуске устройства мой процессор будет тратить тысячи тактов, обнуляя эту память. Если мне не нужны нули в этой памяти (а они мне почти никогда не нужны), значит эта работа прошла впустую, процессор просто сожрал кусок батарейки, чтобы нагреть воздух. Это как раз тот пример, когда оказался С не близок к процессорам. У процессора память при старте либо заполнена единичными битами (если питание было отключено), либо тем, что осталось от предыдущего выполнения (если процессор был перезапущен).

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

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

За break/continue скрываются команды перехода (jump) на автоматически сгенерированные метки

Нет, не скрываются. За break/continue ничего не скрывается, это просто семантика программы. Скомпилируется ли она в JMP инструкции или компилятор раскроет цикл - в общем случае предсказать невозможно. Но суть не в этом. Пускай даже там скрываются команды перехода. Моя мысль в том, что break/continue можно и нужно расширить для внешних циклов, как это было сделано в Java 20 лет назад. И это делают, то ли в новом стандарте, то ли в новых предложениях к стандарту, но это надо было сделать ещё 20 лет назад.

Переполнение обрабатывает также как это делает процессор, операция выполняется с переполнением, а для обнаружения переполнения проверяешь признак переполнения, если тебе это нужно.

Нет, переполнение не обрабатывает так же, как это делает процессор и в этом коде уже приводили примеры кода, где это наглядно. Повторяться не вижу смысла.

vbr ★★★★★
()
Ответ на: комментарий от anonymous

Вот почему Intel может организовать разработку компиляторов в России, а мы не можем?

Кто «вы»? У Эльбруса свой компилятор, например. Правда, с купленным фронтом.

hateyoufeel ★★★★★
() автор топика
Ответ на: комментарий от hateyoufeel

Тот пост был не о компиляторах, а о том, что в России много
талантливых программистов, которым по силам вести разработку
любой сложности, а - «воз и ныне там».

anonymous
()
Ответ на: комментарий от vbr

Нельзя объявить глобальную неинициализированную переменную

кто мешает? хотя многие компиляторы за тебя её обнулят, на самом деле. но на некоторых платформах такого поведения нет (например, в musl) и люди напарываются на баги из-за неинициализированных переменных. привыкли, что за них система всё обнулит, а тут такого поведения нет и вылезают баги и глюки. приходится патчить.

Iron_Bug ★★★★★
()
Ответ на: комментарий от vbr

Если я объявляю глобальный массив в 10 кибибайтов, это значит, что при каждом запуске устройства мой процессор будет тратить тысячи тактов, обнуляя эту память

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

mittorn ★★★★★
()
Ответ на: комментарий от Iron_Bug

кто мешает?

Стандарт языка C, в котором глобальные переменные обязаны обнуляться.

хотя многие компиляторы за тебя её обнулят, на самом деле

Не многие, а все.

но на некоторых платформах такого поведения нет (например, в musl)

Это поведение есть везде, ибо оно прописано в стандарте.

Чтобы этого поведения не было, нужно проставлять переменной атрибут, помещающий её в отдельную секцию.

vbr ★★★★★
()
Ответ на: комментарий от mittorn

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

У меня нет никаких страниц и никакой ОС, никто ничего не обязан обнулять.

vbr ★★★★★
()
Ответ на: комментарий от vbr

C даёт ровно 0 гарантий того, что он это действительно сделает одновременно

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от vbr

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от anonymous

Тот пост был не о компиляторах, а о том, что в России много

талантливых программистов, которым по силам вести разработку

любой сложности, а - «воз и ныне там».

Потому что многие талантливые программисты бежали роняя кал через Верхний Ларс. По крайней мере, те кто не уехал до этого.

hateyoufeel ★★★★★
() автор топика
Ответ на: комментарий от Iron_Bug

если ты одной операцией меняешь несколько бит регистра, то они запишутся в одной транзакции

И ты, конечно же, укажешь, где в стандарте C прописаны транзакции? Или главное - верить, что в компиляторе есть некие мифические транзакции?

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

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

vbr ★★★★★
()
Ответ на: комментарий от vbr

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от Iron_Bug

ну, тем более. но это не мешает тебе объявлять неинициализированные глобальные переменные.

Мешает. В языке C нет возможности объявить неинициализированную глобальную переменную. Проприетарный синтаксис такие возможности даёт, но это уже не C.

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

Вот я и хочу заботиться, а C мне мешает.

vbr ★★★★★
()
Ответ на: комментарий от anonymous

программисты прекрасно работают на зарубежные компании. потому что в Эрефии почему-то программистам платят так себе. ну, некоторые компании платят, но далеко не все. и точно несравнимо с зарубежным уровнем зарплат.

что касается опенсорца, то он международный по своей сути. он не может быть «американским» или «российским». опенсорц пишут люди из разных стран и тут как-то нет деления на какие-то тупые национальные бренды.

Iron_Bug ★★★★★
()
Ответ на: комментарий от vbr

Ну вот тебе и «ближе к железу… ближе к железу»

zurg
()
Ответ на: комментарий от Iron_Bug

ну и чем это тебе помешало-то

Я уже написал, чем мне это помешало - тем, что на инициализацию переменной тратятся циклы процессора впустую.

действительно, неинициализированная глобальная переменная - это бред

Нет, это инициализация глобальных переменных по умолчанию - бред. Если я явно написал char buffer[10240] = {0} - без проблем, инициализируй. А если я пишу char buffer[10240];, то инициализировать его не надо. Так должно быть.

ты работаешь с ней из разных контекстов и не знаешь, что там лежит изначально. это бессмысленно.

Из каких контекстов? Что там лежит изначально, мне не важно. Там, к примеру, кольцевой буфер, в который я буду записывать и считывать значения. И что лежит там вначале - не имеет значения, это никак не используется.

vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 1)
Ответ на: комментарий от vbr

я тебя спросила, чем тебе это мешает. ты мне повторяешь какой-то теоретический бред. на практике это никак не влияет на тебя и твою программу. в чём проблема?

Iron_Bug ★★★★★
()
Закрыто добавление комментариев для недавно зарегистрированных пользователей (со score < 50)