LINUX.ORG.RU

Если вам не хватало UB в C, то вам принесли ещё

 ,


1

2

Привет, мои дорогие любители сишки!

Если вам начало казаться, что разработчики стандарата языка C стали предсказуемыми и больше не могут удивлять вас новыми идеями, то вы ошибались. В новом стандарте C23, комитет постановил:

— zero-sized reallocations with realloc are undefined behavior;

То есть вот это валидный код:

void *ptr = malloc(0);
free(ptr);

А вот это – UB:

void *ptr = malloc(4096);
ptr = realloc(ptr, 0); <-- хаха UB

И это несмотря на то, что в манах уже давно написано следующее:

If size is equal to zero, and ptr is not NULL, then the call is equivalent to free(ptr)

Изменение вносится задним числом, наделяя кучу корректного (согласно документации glibc) кода способностью полностью изменить логику работы программы. Ведь это то, чего нам так не хватало!

В тред призываются известные эксперты по C: @Stanson и @alex1101, возможно они смогут нам объяснить, зачем разработчики стандарта C постоянно пытаются отстрелить себе обе ноги самыми нелепыми способами.

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

Кому не надо умный компилятор, тот выбирает Java или что-то подобное.

Позволю себе не согласиться. Да - мы нынче все очень сильно зависим от оптимизаций. Но то что делают C/C++ компиляторы - просто несопоставимо с тем что происходит в других языках с конструкциями очень удалёнными от железа (так это назовём).

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

Та же Ада или Фортран

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

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

Так тот же NAG есть и в версии для C (NAG CL).

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

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

«For a complete listing of mathematical and statistical functionality in the new NAG C Library (Mark 9) visit www.nag.com.» (https://www.embedded.com/nag-c-c-library-adds-150-new-functions/)

Возможно биндинг. А возможно сам NAG сейчас компилируется на Си, а предоставляет интерфейс для фортрана. Исходников-то нет.

P.S. Как можно в 2024 году предоставлять интерфейс с именами f08afc, x04cac, …

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

Как можно в 2024 году предоставлять интерфейс с именами f08afc, x04cac

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

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

Да я в курсе. Просто недоумеваю.

А что касается конкурентов, то не могу найти ни одного теста скорости этого NAG. Например, по сравнению с Blitz++ или OpenCV.

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

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

Любую одну функцию из него написать несложно

Я думаю - Вы себе льстите. Я бы не смог. По крайней мере - из того что нам интересно. И я немножечко «подкован». Ну - как минимум мне так хочется думать.

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

Трансформации делает LLVM и они абсолютно те же самые, что и для Си.

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

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

В https://uclibc.org/ алгоритмы без комментариев и разобраться в них не просто.

https://uclibc.org/downloads/uClibc-snapshot.tar.bz2

Посмотрите исходники в поддиректории libm например.

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

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

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

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

насколько фронтенд корректно скомпилировал исходный код в LLVM IR

Всё верно. Если фронтенд не сформировал кода, который может приводить к UB (как безопасное подмножество Rust), то проблем нет. Но тогда в общем случае код будет медленнее, так как необходимы дополнительные проверки.

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

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

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

Но ей богу, пример с unreachable просто высосан из жопы, как и все остальные примеры UB с unsafe. Для таких людей ещё на ручных гранатах пишут: «В РОТ НЕ КЛАСТЬ, В ЖОПУ НЕ СОВАТЬ». Ничего неожиданного от такого использования unsafe{} нет.

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

Тогда почему в бенчмарках код на расте без unsafe{} показывает производительность на одном уровне с C? По твоим словам выходит, что если тот же код переписать с unsafe{}, он будет ещё быстрее.

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

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

for(int i = 1; i<100; i++)
{
  if(i<1000) do1(); else do2();
}

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

for(int i = 1; i<100; i++)
{
  do1();
}
monk ★★★★★
()
Ответ на: комментарий от hateyoufeel

Тогда почему в бенчмарках код на расте без unsafe{} показывает производительность на одном уровне с C? По твоим словам выходит, что если тот же код переписать с unsafe{}, он будет ещё быстрее.

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

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

в бенчмарках код на расте без unsafe{} показывает производительность на одном уровне с C

Вот пример.

C unsafe https://benchmarksgame-team.pages.debian.net/benchmarksgame/program/fannkuchredux-rust-6.html — 3,51 секунды.

Без unsafe https://benchmarksgame-team.pages.debian.net/benchmarksgame/program/fannkuchredux-rust-4.html — 7,21 секунды

Версия на Си https://benchmarksgame-team.pages.debian.net/benchmarksgame/program/fannkuchredux-gcc-6.html — 2,1 секунды.

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

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

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

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

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

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

Я не тороплюсь и поэтому пишу на Racket. Ценой потери скорости выполнения в 5-10 раз получаю гарантию того, что нет UB, и большее удобство разработки.

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

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

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

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

Вот они куда-то и торопятся.

файловую систему, драйвер устройства или оконный менеджер никто на ракет писать не будет…

https://github.com/tonyg/pi-nothing/blob/master/baremetal/raspberrypi.nothing

Смотря какие цели. Если цель — написать быстро и надёжно, то могут.

monk ★★★★★
()

Если вам начало казаться

Если вам начало казаться, то нужно принять таблетки и всё пройдёт. Есть железное правило дизайна софта, если что-то непонятно в языке высокого уровня, коим является С, то нужно писать этот кусок кода на асме. Откуда вы берётесь?

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

если что-то непонятно в языке высокого уровня, коим является С

Так было всё понятно.

If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object

То есть можно было использовать realloc(p, 0) и не беспокоиться, что оптимизатор выкинет этот код как невозможный.

то нужно писать этот кусок кода на асме

Писать на асме вызов realloc?

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

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

alysnix ★★★
()

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

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

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

Единственное, стоит добавить категорию типа Unidiomatic Behavior, для кода, который с одной стороны нежелателен, но с другой не даёт права компилятору с нифига ломать код.

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

Добавят это условие к списку уже существующих UB в анализе диапазонов и будет

if(x == 0) { .... }
...
realloc(p, x);

выкидывать условие, так как x «всегда не 0».

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

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

Код if(x = test()) во все санитайзеры добавили, несмотря на то, что это не UB. И даже не implementation defined.

А большУю часть UB никакие санитайзеры не отлавливают.

И нормальные люди, если ломают совместимость, создают новое имя. Сделали бы realloc_s с рекомендацией старый не использовать (как было с strcpy и gets).

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

И нормальные люди, если ломают совместимость, создают новое имя.

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

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

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

Если поменяют, то придется переписать весь ране портированный софт.

Сделаем наш код портируемым снова…

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

по поводу очевидно некорректного вызова

Так это он в новом стандарте очевидно некорректный (UB), а раньше там был дефайнед бихевиор, но невнятно описанный, так что мнения о том, что именно там «дефайнед», разошлись.

red75prim ★★★
()
Последнее исправление: red75prim (всего исправлений: 1)

Мне вот что непонятно.


ptr = realloc(old_ptr, 3); //ptr == old_ptr, size 3
ptr = realloc(old_ptr, 2); //ptr == old_ptr, size 2
ptr = realloc(old_ptr, 1); //ptr == old_ptr, size 1
ptr = realloc(old_ptr, 0); //UB ?? 

По какой инопланетной логике здесь должно быть UB, а не ptr == old_ptr, size 0 ? т.е. эквивалент free

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

но невнятно описанный

Вполне внятно:

implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object

Это то же самое, что malloc(0). Кстати, ждём в следующем раунде объявление UB malloc(0)? А там и до деления на 0 дойти можно.

monk ★★★★★
()