LINUX.ORG.RU

О божественном C и введение в него через ed

 , ,


4

2

Бытие (частично) определяет.

*Какая регулярка преобразует for с телом в while с телом ?

*Какой #define макрит for в while?

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

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

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

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

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

что не имеет никакого отношения к strict aliasing

Ну я же написал - я ведь могу разыменовывать полученный указатель, поскольку он имеет тот же тип, а доступ по указателю того же типа - корректен по strict aliasing. Но указатель-то ссылается на черт знает что, а черт знает что получилось из-за отсутствия определенности оператора "->".

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

Ну я же написал - я ведь могу разыменовывать полученный указатель, поскольку он имеет тот же тип, а доступ по указателю того же типа - корректен по strict aliasing. Но указатель-то ссылается на черт знает что, а черт знает что получилось из-за отсутствия определенности оператора «->».

ЯННП, код в студию

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

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

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

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

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

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

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

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

Оптимизатор не должен вырезать код с побочными эффектами.

C89. 2.1.2.3 Program execution

Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment. Evaluation of an expression may produce side effects. At certain specified points in the execution sequence called sequence points, all side effects of previous evaluations shall be complete and no side effects of subsequent evaluations shall have taken place.

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

это иллюстрация на тему «как отстрелить себе яйца в домашних условиях без регистрации и смс»?

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

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

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

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

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

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

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

а нет побочных эффектов. нет. ни откуда не следует что этот код что-то там делает

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

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

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

ура. volatile оправдан, казнь отменяется.

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

структуры не удовлетворяют условию strict aliasing. мы задекорировали проблему, кастуя указатели на эти структуры

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

хороший компилятор и тут плюнет варнингом

Это уже додумки разрабов компилятора и не соответствует стандарту.

можно в файл записать содержимое первой структуры, а потом прочитать во вторую, или как нибудь еще сломать мозг компайлеру, но зачем?

Затем, чтобы продемонстировать, что гуглящееся в интернете и написанное в доках GCC strict aliasing имеет мало общего со стандартом Си. Напомню, что изначально разговор шел о том, что какие-то там негодяи нарушают strict aliasing, и именно потому они виноваты, а компиляторы - правы.

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

ура. volatile оправдан, казнь отменяется

Я не понимаю твоей радости, ведь твой код нельзя оптимизировать независимо от модификаторов указателей. И, тем не менее, есть компиляторы, которые его заоптимизируют в отсутствие volatile.

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

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

весь феншуй закончился вот тут - (s2*)&s, когда мы сказали, что указатель на данные типа s1 следует рассматривать, как указатель на данные типа s2. зная, что типы структур не удовлетворяют условиям strict aliasing.

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

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

Я не понимаю твоей радости, ведь твой код нельзя оптимизировать независимо от модификаторов указателей

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

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

(s2*)&s, когда мы сказали, что указатель на данные типа s1 следует рассматривать, как указатель на данные типа s2

Единственное ограничение, которое накладывает стандарт Си на это преобразование — объекты должны иметь одинаковое выравнивание. Всё, в остальном — делай что хочешь.

попытка получить значение поля ->c

А мы не получаем значение поля «c» — мы получаем его адрес. А значение мы получаем у указателя «p».

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

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

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

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

Синтаксис самих «регулярок» – нет, но они годятся только для разбора регулярных грамматик.

LL и LR парсеры тоже могут разбирать регулярные грамматики.

Насколько знаю в них нет ни какой-то промежуточной памяти, ни рекурсий.

Почти все реализации regexp’ов используют недетерминированный конечный автомат (НКА), не компилируют в детерминированный (ДКА). А для регулярных грамматик компиляция в ДКА довольно легко делается, если не задумываться про оптимизацию (оптимизируется размер автомата, скорость не меняется - линейная).

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

Поэтому к этим современным «regexp’ам» есть рекомендации, как правильно писать эти так называемые «регулярные выражения».

И разбирают используя НКА, потому что этот НКА можно достаточно легко расширить до «нерегулярных хотелок» пользователей «regexp».

на этапе лексического анализа

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

anonymous ()

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

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

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

Итак, докладываю результаты бенчей самопальных ассоциативных массивов с volatile на структурах и примитивных типах, и без онных. Дизасм и отличия времени выполенния показывает, что volatile применилось. одна итерация - 4 изменения, 6 выборок; всего миллион итераций, ровно половина добавляемых элементов оставалась в массиве, вторая половина стиралась. Сборка на O2.

С volatile на всём, до чего дотянулись руки, кроме простейших локальных переменных char/int, время выполнения 2.58-3.64 секунд.
С volatile только на структурах глобальных данных время выполнения 2.58-2.63 секунд.
Без volatile время выполнения 2.32-2.38 секунд.

То есть, примерно 10% прироста. Ради этого вообще нужно было что-то делать? Ломать работающий код, нагибать програмистов раком и заставлять их расставлять идентификаторы const/volatile, чтобы первый вообще не дал прироста производительности, а второй дал 10%? К слову, бенчи strict aliasing показывали, что прирост производительности в среднем составлял 2-5%, с большим разбросом в плюс и даже минус на разных задачах.

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

LL и LR парсеры тоже могут разбирать регулярные грамматики.

При чём здесь они вообще?

Почти все реализации regexp’ов используют недетерминированный конечный автомат (НКА), не компилируют в детерминированный (ДКА).

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

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

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

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

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

Лексические анализаторы уже давно мощнее регулярных грамматик. Они умеют дергать внешние библиотеки для regexp (которые тоже мощнее регулярных грамматик).

Мало какие лексические анализаторы строят ДКА, так как это сложно (иногда невозможно из-за того, что используемый «regexp» - не совсем regexp) и размер конечного автомата получается в разы больше НКА.

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

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

синтаксис

f(a,b,c)

*a,b,*c{

}

[/quote]

грепается - ^head$ - взаимообусловленность синтаксиса того что в какой то момент стало Си и инстурементов ввода - ed в первую очеред наличествует.

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

То есть, примерно 10% прироста. Ради этого вообще нужно было что-то делать? Ломать работающий код, нагибать програмистов раком и заставлять их расставлять идентификаторы const/volatile, чтобы первый вообще не дал прироста производительности, а второй дал 10%? К слову, бенчи strict aliasing показывали, что прирост производительности в среднем составлял 2-5%, с большим разбросом в плюс и даже минус на разных задачах.

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

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

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

применение const, volatile и strict aliaing rule никак не связано с вопросами производительности.

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

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

Весь облом в том, что он не будет найден. А провал стандарта Си заключается в том, что ссылка на const может ссылаться на изначально изменяемые данные, поскольку можно взять указатель-на-const от не-const данных.

int x = 5;
const int *p = &x;
Это абсолютно корректный код. А поскольку другой указатель на изменяемый «x» можете неограничено передаваться, в том числе в ту же функцию, то оказывается вообще недопустимо для компилятора пытаться оптимизировать что-то на основании модификатора const.

По этой причине в D введен новый модификатор - immutable, который гарантирует неизменяемость значения, вплоть до размещения в read-only странице и segmentation fault при попытке изменения — вот в таких раскладах компилятор уже может начинать оптимизацию.

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

А единственный случай, когда const может помочь программисту, это что-то вроде такого:

if (a[1] = rand())
Но со временем сишные задроты просто научились писать:
if (rand() == a[1])
...
if (2 == a[1])

Потому, я не вижу, в каком месте

удобно жить без const? не очень

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

Да, спасибо за уточнение про «при чтении». Однако же, гарантия соответствия прочитанных данных содержимому памяти в случае, когда память меняется железом, может быть получена либо при отсутствии кэшированности содержимого памяти, либо при наличии поддержки когерентности для DMA в данной системе, что далеко не всегда имеет место даже в x86. Это уже не говоря про внеочередное исполнение. В отсутствие гарантий точка чтения неясна и volatile не помогает, к нему нужны дополнительные движения.

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

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

По второму тезису, «железячная переменная», либа GNU Std C будет не согласна, поскольку использует volatile для сигналов, ввода-вывода, pthread, «typedef volatile int __spin_lock_t;», TLS. Короче говоря, везде, где черезчур умный оптимизитор может поломать работу кода. Еще есть очень много «asm volatile» вставок, но там сложно судить о функциях «volatile» именно как сишного аналога.

несоблюдение правила строгого алиасинга - индикатор говнокода и потенциального наличия проблем

Еще раз повторюсь, что говнокод здесь - это как минимум линукс, glib, gtk, gnome, и весь связанный с ними софт. Я боюсь, что LOR не является подходящим местом для подобных разговоров.

применение const, volatile и strict aliaing rule никак не связано с вопросами производительности.

Для комментариев в Си есть конструкции «//» и «/* */». Их функционал и удобство намного превышают те, которые есть у const, volatile и strict aliasing.

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

Весь облом в том, что он не будет найден.

typedef int const int_const;                      // An unassignable int
typedef int * int_ptr;                            // An assignable pointer to an assignable int
typedef int_const * int_const_ptr;                // An assignable pointer to an unassignable int
typedef int * const int_ptr_const;                // An unassignable pointer to an assignable int
typedef int_const * const int_const_ptr_const;    // An unassignable pointer to an unassignable int

int main(void)
{
  int i = 2;                                      // An assignable int
  int_const j = 3;                                // An unassignable int

  i = j;                                          // OK
  // j = i;                                       // ERROR; cannot assign to const

  int_ptr pointer_to_int;
  pointer_to_int = &i;                            // OK
  // pointer_to_int = &j;                         // ERROR; `int_const` cannot be safely converted to `int`

  int_const_ptr pointer_to_const_int;
  pointer_to_const_int = &i;                      // OK! `int` can be converted to `const int`
  pointer_to_const_int = &j;                      // OK

  int_ptr_const const_pointer_to_int = &i;        // OK
  // int_ptr_const const_pointer_to_int_2 = &j;   // ERROR; `int_const` cannot be converted to `int`

  *const_pointer_to_int = 2;                      // OK, constness of pointer does not affect constness of pointee
  // const_pointer_to_int = &i;                   // ERROR, cannot assign to const pointer

  int_const_ptr_const const_pointer_to_const_int_1 = &i;  // OK! `int` can be converted to `int const`
  int_const_ptr_const const_pointer_to_const_int_2 = &j;  // OK

  return 0;
}

можно взять указатель-на-const от не-const данных.

можно. конст данные? ок. конст указатель на данные? тоже ок. конст указатель на конст данные? норм. ты скажи чо надо-то, барин?

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

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

линукс, glib, gtk, gnome

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

такова расплата за гибкость.

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

могут быть и другие мнения, не возражаю )

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

можно. конст данные? ок. конст указатель на данные? тоже ок. конст указатель на конст данные? норм. ты скажи чо надо-то, барин?

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct X {
	int i;
	int j;
};

int foo(struct X *p, struct X *q, const struct X *r) {
	q->j = r->j + 1;
	p->i = r->j + 2;
	return r->j;
}

int main() {
	unsigned char *c = malloc(3 * sizeof(int));
	memset(c, 0, 3 * sizeof(int));
	struct X *p = (struct X *)(c + sizeof(int));
	struct X *q = (struct X *)c;
	const struct X *r = q;
	printf("%i\n", foo(p, q, r));
}
Вывод:
$ gcc -O0 main.c && ./a.out
3
$ gcc -O2 main.c && ./a.out
1

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

гарантируется железом и механизмами типа запрета прерывания.

Вот именно, и этими механизмами нужно правильно воспользоваться, что выходит за полномочия модификатора «volatile».

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

Все и всегда нежелательны. Это особо остро стало видно в многопоточных приложениях - просто там эти проблемы в значительной степени были заткнуты через атомики и «asm volatile». Я — один из немногих, кто видит, что эту фичу «оптимизации» из компиляторов нужно было вынести под корень еще 20 лет назад, но теперь ее будут тащить тупо ради совместимости, а в многопотоках и вводе-выводе людям придется вешать volatile на абсолютно все структуры данных, как пришлось делать мне.

линукс, glib, gtk, gnome

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

Под 30 млн строк в ядре, и оно стабильно работает на миллионах серверов несмотря на крики о том, что работать оно не может. Я сторонник того, что практический успех значит всё (не путать с коммерческим), а не работающая теоретическая чистота (strict aliasing) не значит ничего.

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

т.е ещё проще(не сложнее) чем Oberon-0 - возможно без циклов ибо tco c конд - ой блин схема лезет - но не ( а { - при том что при наличии синтаксиса открывающие операторные скобки избыточны ибо есть операторы тогда остаётся только }

ну и да - ща всяко Control Chacters aka <’ ’ почти анохронизм - их тож можно использовать для лаконичности - т.е utf8 - входной но ограничимся ascii используя семантику только для \n \r \t 0 - остальные символы - операторы - это не для офустикации - а для упрощения компилятора что бы все лексемы символьны - 120 при рекурсии( т.е появлении локальных имён) - отдельных лексем хватит на всё - при возможности обращаться к сущьностям через /составнойобьект/егосоставноеполе - и может даже для обращения полей аргументов инстанцированных функций и их внутренних переменных - которые всяко смещения на стеке — этот абзац если только он упрощает селф-компилятор

т.е. охота лицизреть самое простое но не проще(в этом смысле брэнкфак не проще ибо не использует 120 символов :) )

ранее тут указанный (через ссылку ссылки) https://github.com/kragen/stoneknifeforth тоже не прост ибо отказ от синтаксиса(примитивного) не упрощает

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

кажущейся простоты кода

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

этими механизмами нужно правильно воспользоваться

любыми механизмами нужно уметь правильно воспользоваться.

а в многопотоках и вводе-выводе людям придется вешать volatile на абсолютно все структуры данных

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

Под 30 млн строк в ядре, и оно стабильно работает на миллионах серверов несмотря на крики о том, что работать оно не может.

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

не работающая теоретическая чистота (strict aliasing)

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

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

Вы все слабы в компилировании там подход нужен что бы именно лучшую модель вытащить написанную до нас и городите свои велики , не видать короче с вами быстрого директ икса потому что лебедь , щук и рука. Лучше работать с агностиками чем с дебичами с высоким чсв , а по итогу пиу

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

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

Я предпочел бы услышать конкретно, где в этом коде UB. Потому что стандарты Си допускают превращение абсолютно любого типа к «char» и «void *».

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

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

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

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

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

Намного более предсказуемая судьба обретается использованием опции "-fno-strict-aliasing" без переписывания годами тестированного кода, или просто использованием MSVC. Напомню также, что сама стандартная либа Си не компилируется со strict aliasing, что есть показательно.

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

Я предпочел бы услышать конкретно, где в этом коде UB.

  1. If the value being stored in an object is read from another object that overlaps in any way the storage of the first object, then the overlap shall be exact and the two objects shall have qualified or unqualified versions of a compatible type; otherwise, the behavior is undefined. (6.5.16.1p3)

пояснение:

struct X { int i; int j; };

int foo (struct X *p, struct X *q)
{
  q->j = 1;
  p->i = 0;
  return q->j;
}

will optimize to return 1. If *p and *q were allowed to overlap (&p->i == &q->j) this would invoke undefined behavior.

  1. An attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type (6.7.3).

касается

	struct X *q = (struct X *)c;
	const struct X *r = q;
	q->j = r->j + 1;
	unsigned char *c = malloc(3 * sizeof(int));
	memset(c, 0, 3 * sizeof(int));
	struct X *p = (struct X *)(c + sizeof(int));
	struct X *q = (struct X *)c;

молчаливо предполагается, что

sizeof(struct X) == 2 * sizeof(int);

стандарт говорит, что мы не можем рассчитывать на то, что sizeof(struct) == sizeof(struct_member1) + sizeof(struct_member2) + …

стандарт гарантирует, что размер int как минимум 16 бит, тем самым превращая предположение о том, что размер int равен слову в тыкву. на целевой платформе, например 32 битной, при 16 битном int компилятор имеет право применить padding.

и куда мы попадаем при struct X *p = (struct X *)(c + sizeof(int)) ? в попу пальцем. концепция вообще перестает работать.

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

assert(sizeof(struct X) == 2 * sizeof(int));
unsigned char *c = malloc(3 * sizeof(int));
...

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

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

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

хз, у меня другой личный опыт.

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

либа GNU Std C будет не согласна,

Нет такой либы.

поскольку использует volatile для сигналов,

Абсолютно обоснованно и корректно.

ввода-вывода, pthread, «typedef volatile int __spin_lock_t;», TLS

Пруфы.

Еще раз повторюсь, что говнокод здесь - это как минимум линукс,

Линукс собирается с -fno-strict-aliasing.

glib, gtk, gnome, и весь связанный с ними софт

Пруфы.

shdown ()

Очень хотел бы внести правочки в GCC, чтобы сделать все переменные volatile, собрать с этим glibc и побенчить на чем-то вроде phoronix.
https://stackoverflow.com/questions/45244770/implicitly-declare-all-variables...
Но, к сожалению, на моей бсдяхе у меня не получилось вообще собраться glibc, поскольку оно требует GNU binutils, а последние требуют glibc.

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

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

стандарт гарантирует, что размер int как минимум 16 бит, тем самым превращая предположение о том, что размер int равен слову в тыкву. на целевой платформе, например 32 битной, при 16 битном int компилятор имеет право применить padding.

Стандарт не фиксирует значение выравнивания, но фактически все компиляторы (как минимум GCC, C builder, MSVC, MCC) используют единую схему, которая в данном случае не создает никаких промежутков между полями, и структура с целыми числами по хранению аналогична массиву этих же типов.

An attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type

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

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

ввода-вывода, pthread, «typedef volatile int __spin_lock_t;», TLS

Пруфы.

Грепаешь сорцы, получаешь пруфы.

Линукс собирается с -fno-strict-aliasing

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

glib, gtk, gnome, и весь связанный с ними софт

Пруфы.

GLib не работает со strict aliasing:
https://bugzilla.gnome.org/show_bug.cgi?id=791622
Потому всегда собирается с -fno-strict-aliasing:
https://github.com/GNOME/glib/blob/mainline/meson.build#L85

В гноме-гтк, как показали сорцы, strict aliasing таки сделали где-то так к 2018 году. Что есть неудивитель, учитывая, что именно GLib работает с потоками, вводом-выводом, и сигналами, а gtk/gnome все-таки больше однопоточны и полагаются на готовые абстракции. Потому я согласен, что я взял слишком широкий мазок кистью, и скорее должен был написать «GLib и связанный низкоуровневый софт».

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

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

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

фактически все компиляторы

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

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

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

Если бы все было так просто, то не понадобилось бы делать лазейки с «char *» и «void *». Элементарный пример: структура создана как изменяемая, она передается библиотечной функции, которая ожидает эту структуру, но с модификатором const. Неаккуратные действия со стороны програмиста могут с легкостью привести к тому, что библиотечная функция отработает не так, как она была запрограммирована.

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

Абсолютно все компиляторы не соответствуют стандарту в полной мере. Если компилятор работает, а стандарт - не работает, то стандарт оказывается лишним.

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

strict aliasing не нужен - он тут является единственным бесполезным мусором, который только создает проблемы и не дает никаких преимуществ ни софтописателям, ни софтоюзателям.

софтопейсатели не согласны )

https://stackoverflow.com/questions/98340/what-are-the-common-undefined-unspecified-behavior-for-c-that-you-run-into

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

структура создана как изменяемая, она передается библиотечной функции, которая ожидает эту структуру, но с модификатором const.

никаких проблем, все законно.

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

как? в пределах одного потока? нельзя изменить состояние переменной пока функция не вернет управление.

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

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

как? в пределах одного потока? нельзя изменить состояние переменной пока функция не вернет управление

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

byko3y ()