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

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

Как этот код пишут «опытные» сишники:

Я пишу так:

int foo(int x) 
{
    int result = 1;
    char *s = malloc(1);

    if (x == 0)
        result = 0;

    free(s);
    return result;
}

Если внутри foo() начинаются сложные ветвления, то функцию foo() явно стоит разделить на несколько.

Я опытный си-шник или нет?

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

Кстати про signed и size_t - очень часто попадается signed там где нужно size_t по совершенно дурацкой причине - какому-то дебилу в каком-то комитете возмнилось что сравнивать signed и unsigned - это некошерно и как минимум варнинг, который должен быть по дефолту и задалбывать юзера. Хотя никаких проблем при сравнением signed и unsigned быть в принципе не может. И если нуб пишет классическое int i; ... for(i=0;i<size;i++){...} ... то компилятор своим бредовым варнингом вынуждает его поменять тип одной из переменных. И частенько это оказывается size, потому что i может потянуть за собой сотни других переменных, которые обязаны быть signed по логике алгоритма и с которыми может по ходу дела сравнивается i (или другим способом взаимодействует) из-за этого же самого варнинга. Это при том, что нынче компилятор без особых проблем может не только определить где такое сравнение может вызвать проблемы, а где нет, но и частенько определить максимально возможное значение size (который вообще нередко проистекает тупо из sizeof(), либо константы размера массива/аллоцированного буфера например) и не выпендриваться если size заведомо не выходит за положительный предел int.

В общем, конкретно эта хрень с signed и size_t проистекает чисто из тупорылости какого-то никчёмного теоретика занимающегося откровенным «safety trolling».

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

И если нуб пишет классическоеint i; ... for(i=0;i<size;i++){...} ...

Нуб мог бы писать for(auto i = 0; i < size; i++) и всем было бы хорошо, но в Си не могут добавить вывод типов. Я уже молчу про for(auto x : array). То есть, в C23 конечно добавили возможность использовать auto, но оно работает в очень ограниченном числе случаев, и потому слегка бесполезно.

Это вторая причина не любить сишечку: очень уж убогонький говноязычок.

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

Сначала вывод типов, потом лямбды, синие волосы, девочкомальчики.. проходили - знаем.

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

«Настоящий сишник» в любой непонятной обощенной ситуации пишет макросы, а не функции.

#define FREE_PTR(p) do { if(p) { free(p); p = NULL; } } while(0)

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

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

Сначала вывод типов, потом лямбды, синие волосы, девочкомальчики.. проходили - знаем.

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

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

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

И defer тогда бы не понадобился.

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

По хорошему нужна хорошая подсистема управления памятью.

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

Вообще, вот здесь подробнее есть.

C23’s auto is modeled on the existing gcc __auto_type extension.
No Multiple Declaration Support

An example of the first difference is:

auto x = get_x(), y = get_y(); // C++: OK; C: error

Why didn’t the C committee allow auto to declare multiple variables in the same declaration? Because __auto_type doesn’t allow it and the committee simply wanted to standardize the existing __auto_type.
No * Support

An example of the second difference is:

auto *p = strchr( s, '.' );  // C++: 'char'; C: error
auto  q = strchr( s, '.' );  // C++ & C: 'char*'

Why didn’t the C committee allow *? First, in C++, you need to be able to do auto&, that is optionally use references with auto. Since & is allowed, * is also allowed in C++ for symmetry.

C, of course, doesn’t have references so there’s no symmetry issue. And __auto_type doesn’t allow *.

https://dev.to/pauljlucas/auto-in-c23-ij9

Просто анал-карнавал какой-то. Кто там про теоретиков писал, @firkax или @Stanson? В стандарт тянут фичи GCC вообще не приходя в сознание, получается вот такое вот гнутое говно. Хотя адекватный пример с C++ прямо перед глазами был.

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

по «сумме» чтений записей дальнейших

Хиндли-Милнер не лопнет от тьюринг-полноты для вывода типа?

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

«эта хрень» проистекает из представления signed и unsigned типов в памяти машины. вот после ассемблера не возникает подобных странных вопросов. поэтому ассемблер вообще полезен для упорядочивания мозгов при изучении программирования.

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

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

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

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

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

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

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

Ты написал другой код. Подразумевается, что в условном операторе if (x == 0) нужно завершить работу функции. Мой пример это лишь демонстрация с минимумом строк, вроде это должно было бы быть очевидно.

Можешь считать, что там такое

int f(int x) {
  char *s = malloc(1);

  if (x == 0) {
    free(s);
    return 0;
  }

  // more code

  free(s);
  return 1;
}

При этом в // more code могут быть свои ветвления. И выносить это в отдельные функции - полный маразм. Это к примеру типичный код для обработки ошибок или проверки входных параметров, которые в общем случае будут практически на каждой строке.

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

дык каждое следующие употребление имени уменьшает пространство типов

т.е каждая операция в общем уточняет(не увеличивает домен) тип

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

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

Я начинаю думать что кто-то угнал твой аккаунт и посадил туда нейросеть.

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

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

Да нет, тут как раз не дебилу, варнинг полностью по делу. И придуман он не комитетом (кто бы сомневался) а авторами компиляторов. Причём варнинга не будет если типы короткие и расширяют свою разрядность перед сравнением. А дело вот в чём: если ты захочешь сравнить 0x80000000 и 0x00000000, один из них signed другой unsigned, то результат сравнения будет зависеть от того, знаковое или беззнаковое сравнение применит компилятор. Чтобы произвести сравнение более надёжно, требуется дополнительная логика, а поскольку компилятор не делает неявное поведение, то эту логику должен указать сам программист. Если же не указал - вот тебе варнинг.

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

Ну вот, нуб поленился исправить сотни некорректных типов и вместо этого решил исправить корректный size_t на дурацкий int. Виноват тут, конечно, нуб, а вовсе не компилятор. Кстати ещё один источник этой гадости заложен в древнем артефакте - прототипе main(), где argc типа int. И если gcc позволяет разумное unsigned int argc, то убогий шланг считает это ошибкой, причём фатальной и неотключаемой. Вот же вредители.

которые обязаны быть signed по логике алгоритма

Обычно ничего такого нет, а есть только ленивость их исправлять. А если есть - то чаще это глобальный стратегический просчёт программиста при начальном планировании логики. А если это не он (что крайне редко) - то существуют тайпкасты, вот их и надо использовать. Разумеется, не от балды дописав (int) или (size_t) перед одной из сторон, а предварительно проанализировав ситуацию и подумав, что именно и на какую сторону надо дописать, чтобы не создать баги.

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

Декралации переменных не то что вперемежку с кодом, а ещё и внутри императивного оператора - это совсем плохо. Ну и auto сам по себе описанной Stanson-ом ситуации ничем не поможет.

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

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

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

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

у всего есть цена

olelookoe ★★★
()
Ответ на: комментарий от qulinxao3
i = 0 # константа, единичное множество
i = i + 1 # множество с операцией + ?
i = i * 2 # множество операциями + и * ?
i = i - 1 # множество с операцией +, * и -; группа по операции + ?
i = i / 2 # множество с операцией +, *, -, /; группа по операции * ? кольцо ?
i = i & 0xFF # сдаюсь, что это?
anonymous
()
Ответ на: комментарий от firkax

Декралации переменных не то что вперемежку с кодом, а ещё и внутри императивного оператора - это совсем плохо.

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

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

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

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

почему значение счётчика должно быть доступно за пределами цикла после его окончания.

поиск индекса?

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

Дело в том что мусорить и правда не нужно, а именно - не надо замусоривать императивный код вставленными в него объявлениями

Это не мусор.

А если тебя беспокоит слишком длинная область видимости - выдели в отдельную функцию.

Каждый цикл выделять в отдельную функцию? Да не, нахрен так жить. Я просто буду использовать фишки C99 и более свежих стандартов и объявлять переменные в минимально возможном scope.

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

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

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

у всего есть цена

нужна единственно верная оценочная функция… и градиентный спуск.

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

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

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

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

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

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

Чтобы писать на Си, нужно представлять как работает процессор

Какой именно процессор?

что есть адресное пространство, что код и данные в этом одном адресном пространстве

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

что константы это данные в этом пространстве и они могут быть и не защищены от записи и т.д. и т.п.

В Си запись в const является UB. Сорян, Си такого не позволяет.

Благодаря этому, Си подходит под все современные процессоры.

Любой язык программирования подходит под все современные процессоры. Твоё высказывание не имеет смысла. Хуже того, оно не является правдой, потому что Си не подходит под все процессоры. Как минимум, Си не подходит для программирования GPU вообще ровно никак.

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

Си это ж язык действий, которые умеет процессор.

Нет, алгоритмов.

А вот при компиляции создаётся бинарный код пригодный для его выполнения на процессоре.

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

С это язык для работы с абстрактной машиной

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

со своими тараканами, конечно же
которых мы ценим, любим и в обиду не даем

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

Какой именно процессор?

Любой, любой процессор исполняет код из одного, единого адресного пространства. Единое, потому что адресное пространство, для команд исполняющейся программы, одно (не путайте с виртуальной памятью).

Они могут быть в разных. Хуже того, они часто бывают в разных.

Сейчас нет процессоров с несколькими разными адресными пространствами.

Любой язык программирования подходит под все современные процессоры.

Нет, конечно. Си настолько приближен к процессору, что можно без операционки программы делать и даже без менеджера динамической памяти. А, например, C# без динамической памяти попросту невозможен.

Только не надо вдаваться в крайности и всё пытаться делать только на Си или только на C#. Каждый язык удобен для своей задачи. И не нужно пытаться в Си тащить то, что есть в других, более высокоуровневых языках.

Т.е. я тоже против всяких defer языке.

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

int f(int x) { char *s = malloc(1);

if (x == 0) { free(s); return 0; }

// more code

free(s); return 1; }

Можно так

int f(int x) { char *s = 0;

if (x == 0) return 0;

s = malloc(1);

// more code

free(s); return 1; }

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

Все так, просто иначе, очень сложно объяснить, почему программы на Си требуют знание работы процессора.

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

Не требуют. Это программы на ассемблере требуют знаний процессора. А программы на С требуют знания стандарта.

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

В Си запись в const является UB. Сорян, Си такого не позволяет.

в общем случае, если нет аппаратной защиты, а структурные константы лежат в писабельной области памяти(а не в rom), можно и на си их попортить. и на с++

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

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

Алгоритм - это все-таки четкий набор действий. Действия мы описываем на языке программирования. Компилятор преобразует набор действий в бинарный код процессора, который потом процессором и выполняется.

Неизменным остается лишь адресное пространство, для чего в языке введено такое понятие как «адрес». Переменная, хранящая адрес, это указатель.

Адресное пространство языка Си == адресное пространство процессора. Именно этим язык и приближен к 99.(9)% современных процессоров.

С квантовым так не прокатит.

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

Си это ж язык действий, которые умеет процессор.

Код на Си будет работать на архитектуре любого процессора на котором имеется компилятор с Си.

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

Это потому что у любого из 99.(9)% процессоров есть адресное пространство. Но, один и тот же код на Си может работать по-разному на разных типах процессоров, как раз для этого и требуется - портирование.

Портирование еще требуется и на разные операционки, но в данном случае, за язык Си, я имею в виду именно разные типы процессоров. Т.к. за разные операционки надо портировать уже более высокоуровневые вещи, например, для работы с файлами в Lin и в Win, нужно использовать разный код, т.к. у ОС разный API. Только благодаря библиотеками, многие вещи типа open()/read()/write()/close() делаются одинаково. Но вот пути до файлов имеют разный формат и это нужно учитывать в коде. В общем, портирование для ОС, это другое, не к языку Си.

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

можно и на си их попортить

Нет нельзя ), УБ - это высокоуровневая фича, как и сам язык Си, и не зависит от реализуемости в железе

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

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

Это у новичков так.

В профессиональном проекте всегда имеется хедер, который в зависимости от того на какой архитектуре код компилировался устанаввливает размеры типов данных, …

Посмотрите к примеру https://github.com/harbour/coreм хедер hbdefs.h

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