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

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

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

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

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

  • За переменной может скрываться регистр/память внешней аппаратуры. Там может быть не только управление, но и результат, который можно только читать.
  • Есть статическое ОЗУ, которое не очищается при перезагрузках. Есть ОЗУ с батарейным питанием. В таком ОЗУ хранятся данные, которые будут востребованы по запуску программы.

Всем этим пользуются в микроконтроллерах, а не в обычных персоналках. Но язык, конечно, Си.

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

Если я объявляю глобальный массив в 10 кибибайтов,

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

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

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

Разве это не нарушает стандарт?

Objects with static storage duration (3.7.1) shall be zero-initialized

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

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

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

я ничуть не токсичный. это ты тут отравляешь атмосферу странными заявлениями.

глобальные переменные - это зло. и вот в норме их вообще быть не должно.

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

Глобальные переменные в практически любом коде, работающем на микроконтролере, это не зло, а повсеместное явление.

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

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

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

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

на микроконтроллерах ты чаще всего сидишь в прерываниях

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

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

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

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

Я кажется понял почему вы так пишете про глобальные переменные.

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

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

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

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

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

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

Лол так часто и делают. Особенно когда хотят избежать динамической работы с памятью: выделяют статично кусок под «кучу» и в нём и развлекаются как могут.

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

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

Лично я до сих пор не встречался с необходимостью использовать кучу в своём коде для микроконтролера. Не то, чтобы я отрицал эту возможность, но пока нужды не увидел. Реализация malloc/free это несколько килобайтов кода, а пользы от неё я пока не увидел, например. При этом вред очевиден - любой malloc может выдать NULL, если память кончится. А при статическом распределении памяти она кончиться не может: линкер прекрасно показывает и проверяет, сколько памяти мы использовали.

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

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

я писала для микроконтроллеров много лет. для разных, от пиков и атмелов, до альтеровских FPGA. и я знаю, о чём я говорю.

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

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

до альтеровских FPGA

Будто ниос чем-то существенным отличается от любых других микроконтроллеров.

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

А теперь сравни это с нормальным кодом:

int i = a;
while (i <= b) {
  body;
  if (i < INT_MAX) {
    i = i + 1;
  } else {
    break;
  }
}

Намного лучше и понятней получилось.

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

int x = 0; y = 2; i++;

то тебя попросят переписать этот код.

Если ты напишешь в коде

int x = y = 2, i++, 3;

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

Но в for это всё норма жизни почему-то считается…

Единственное, чем for лучше while в этой инкарнации - весь код, работающий с переменной цикла собран в одном месте, а также нельзя случайно сломать цикл с помощью continue.

К слову, можно сделать так, решая эту проблему, забавно даже:

int i = a;
while (i < b) {
  defer i = i + 1;
  body;
}
vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 2)
Ответ на: комментарий от vbr

нормальным кодом

Этот код не нормальный, ни по внешнему виду (нафлудил мусорных строк, что это за бред break на отдельной строке да ещё и в персональных {}), ни по сути кода (i два раза сравнивается с верхним пределом - вот как раз для таких как ты и делают оптимизаторы, чтобы подобный мусор вырезать из кода перед компиляцией).

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

в си нет обозначения для in/out параметров

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

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

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

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

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

4 строки против 2.

Какая-то странная оценочная функция. Я добавил одну нетиповую проверку к стандартному объявлению цикла for.

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

Причём тут musl? Обнуляет ОС при выделении памяти, и вовсе не ради семантики чего-то там, а ради безопасности (чтобы прога не украла оставленные там кем-то данные).

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

4 строки против 2.

Чем меньше строк, тем проще что-ли? Ну отломай Enter у себя, вот заживём, всё в одну строку будет.

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

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

Но можно делать и так (IAR):

static __no_init uint32_t Var @0x10000000;
Var = 1;

PS. Правда, это скорее-всего уже просто синтаксический сахар от компилятора (но это не точно) и за ним скрывается привычная нам

*(uint32_t*)0x10000000 = 1;

Для меня это все-равно переменная, по адресу 0x10000000.

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

Изменение бит (хоть одного, хоть нескольких) делается так:

a = (a & 0xFFEC) | 0x0200;
И разумеется тут одно чтение и одна запись. Чуть с битовыми переменными когда ширина через двоеточие - не нужна.

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

Ты добавил в «рандомных» местах 4 строки к стандартному объявлению цикла while.

Я добавил 2 строки (точнее 1) в конце тела цикла.

У тебя слишком рандомная оценочная функция.

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

Считаю что «он болен» тут др другой причине: здоровые люди слово «кибибайт» не используют.

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

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

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

Писать в одну строчку всю программу не стоит (если это не int main() { return 0; }), но это не значит что надо флудить переводами строки и фигурными скобками где не надо. Мусорные строки - плохо, они мешают окинуть одним взглядом логику алгоритма, если он из-за этого мусора раздувается в несколько раз.

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

Твой код непонятен. Это write-only код. Чтобы прочитать одну строчку такого кода, надо потратить несколько минут и всё равно можно ошибиться. Это эталон непригодного для практического использования кода, даже для C. Проще читать ассемблерный выхлоп, чем такой код.

Вот как надо писать этот код:

int a1 = a;
a1 &= ~(1 << 0);
a1 &= ~(1 << 1);
a1 |= (1 << 9);
a = a1;
vbr ★★★★★
()
Ответ на: комментарий от firkax

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

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

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

а переменная - это чисто программистская штука, место для хранения каких-то временных данных программы во время исполнения.

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

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

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

Если тебе надо тратить несколько минут, чтобы распарсить шестнадцатиричное число на биты - то ты видимо тормоз.

Вот как надо писать этот код:

Ты опять флудишь лишними строками. И мусорную переменную ещё создал.

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

А вот как этот код писался бы, будь C спроектирован получше:

int a1 = a;
a1[0] = 0;
a1[1] = 0;
a1[9] = 1;
a = a1;

Красота.

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

Если тебе надо тратить несколько минут, чтобы распарсить шестнадцатиричное число на биты - то ты видимо тормоз.

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

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

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

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

Если тебе надо тратить несколько минут, чтобы распарсить шестнадцатиричное число на биты - то ты видимо тормоз.

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

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

я не работала с nios, мы писали на AHDL. но ниос я ради любопытства потыкала палочкой, да. мне, честно говоря, не понравилось.

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

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

А компилятор тоже документацию прочитает?
Сейчас компилятор может применить оптимизации для write-only аргументов только если ему доступно тело функции на момент оптимизации (например, она определена в том же юните, либо используется LTO)
С точки зрения программиста тут вопросов нет - выходные аргументы обычно как-то помечены или описаны

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

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

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

Она 200 строк только в видении флудеров, а на самом деле - 20 строк. Ради флуда переводами строк и скобками делать декомпозицию не следует.

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

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

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

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

про анализ функций я тоже написала. ты не можешь использовать функцию, если не знаешь её сигнатуру.

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

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

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

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

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

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

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

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

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

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

Это в обычном линуксе или в каком-то эмбеде?

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