LINUX.ORG.RU

Авторы Си — наркоманы?

 , , ,


3

6

Столкнулся с интересным багом. После того как разобрался, что же именно происходит, меня постигло крайнее изумление! Оказывается, в языке Си тип числовой константы зависит от формата записи.

Дистиллированный пример кода, который это демонстрирует:

#include <stdbool.h>
#include <stdio.h>

#define IS_HEX(x) \
    _Generic((x), \
        unsigned int: true, \
        long: false \
    )

#define X 0x80000001
#define I 2147483649

int main(void) {
    if(X == I)
        puts("X == I");

    if(!IS_HEX(I))
        puts("I is not hexadecimal");

    if(IS_HEX(X))
        puts("X is hexadecimal");

    return 0;
}

Все три сообщения будут выведены на экран.

Зачем это сделано? Кому от этого легче? Какие оптимизации это позволяет проворачивать, кроме оптимизации отстрела ног программистам? Непонятно! В общем, стремлюсь поделиться своим негодованием здесь и предостеречь будущие поколения от наступления на эти грабли.



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

Хуже того, gcc для int и unsigned int вообще идентичный код выдает.

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

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

Потому что может доказать, что переполнения нет. Доступ к array гарантирует, что i не больше 1024.

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

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

Потому что диапазон явный от 0 до 200.

это где там в коде такой диапазон получается?

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

Вот. Посмотри на код для clang: https://godbolt.org/z/aaKabe81W

for(unsigned i = a; i != b; i++)
      array[i] = i;


тут, думается, может быть неоптимальный asm выходить, поскольку в int присваивается unsigned (что может породить переполнение).

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

Так смысл UB именно в том, чтобы можно было использовать эти отдельные ветки оптимизации.

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

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

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

как думается, с указателями это врядли связано, попросту потенциально-допустимый индекс в массиве равен максимуму у типа size_t — и если это один и тотже тип с uint64_t — тогда соответсвтенно для unsigned варианта не может быть переполнения в принципе в таком контексте.

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

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

В данном примере может и избыточен, но в общем случае, это гарантия, что компилятор не подвергнет каким-либо оптимизациям работу с этим символом, а сам символ доедет до этапа компоновки не зависимо от опций компилятора, таких как -fvisibility=internal.

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

Я это и имел в виду. UB в языке позволяет компилятору не делать проверок на эту ситуацию, так как «её не может быть».

Кстати, в языках без UB бывает аналогичная конструкция типа https://doc.rust-lang.org/std/hint/fn.unreachable_unchecked.html.

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

Нет опровержения.

Я второй пример привёл про Racket, в котором UB точно нет.

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

C++ ужасающе сложен. Для тех, кто стезёй своей жизни взял специализацию именно на этом языке - может и ничего. Но вот я пользуюсь эпизодически C, как и многими другими ЯП вроде Go, Java, JavaScript. И - нормально. Каждый из этих языков я знаю достаточно неплохо. С C++ такой вариант вообще не пройдёт, сложность этого языка поистине ужасающая. При этом было время, когда я стандарт C++98 чуть ли не на память цитировал. Но толку - там каждые несколько лёт всё усложняется и усложняется.

C концептуально - идеален. Но там столько раздражающих мелочей. Так и хочется сделать исправленный C.

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

еще постфиксный инкремент тут смотрится хуже префиксного

sni4ok
()
24 сентября 2026 г.
Ответ на: комментарий от vbr

C++ ужасающе сложен.

Но никто не заставляет использовать все аспекты языка. Можно жить в своем сабсете, и прекрасно себя чувствовать :).

Начинать с Си++ - это глупость, конечно. Си++ учится лучше, когда изучился Си :)

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

Си++ учится лучше, когда изучился Си

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

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

Но никто не заставляет использовать все аспекты языка. Можно жить в своем сабсете, и прекрасно себя чувствовать :).

Сомневаюсь. По крайней мере когда я пишу что-либо, я постоянно смотрю объявления используемых методов, а порой и в реализацию залезать приходится. Можно, конечно, избегать всех библиотек, включая stl, тогда может и получится так… Но всё же обычно так не делают. А чужие библиотеки вряд ли будут написаны на моём сабсете.

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

Начинать с Си++ - это глупость, конечно. Си++ учится лучше, когда изучился Си :)

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

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

Сказал человек в теме после двух месяцев с даты последнего сообщения.

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

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

Книжка трупострауса устарела, C++ с тех пор переродился трижды.

yorshka
() автор топика

Зачем это сделано? Кому от этого легче? Какие оптимизации это позволяет проворачивать, кроме оптимизации отстрела ног программистам? Непонятно! В общем, стремлюсь поделиться своим негодованием здесь и предостеречь будущие поколения от наступления на эти грабли.

Извините за некропостинг, но для этих же «будущих поколений» грех не заметить: то, что вы описываете лежит на поверхности. Возьмите, к примеру, «Язык C. Справочник» Принца и Кроуфорда, стр. 65-66. Это самое начало главы про литералы и там говорится, что от формата записи литерала зависит иерархия типов, в которые компилятор попытается его распознать.

Для литералов в десятичной записи иерархия будет такой:

int, long, long long

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

int, unsigned int, long, unsigned long, long long, unsigned long long

То есть такое поведение — не баг, а особенность языка. Если бы вы углубились в тему, то знали бы об этом.

Да, само такое поведение на первый взгляд странно, но язык C проектировался практиками для их же собственных задач. Превращение шестнадцатиричных констант в unsigned int в этом контексте вполне обосновано.

C-xC-f
()
Ответ на: комментарий от C-xC-f

сяшку стали проектировать между 84-88

тогда была ужо тином

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

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

сяшку стали проектировать между 84-88

Кхм:

C came into being in the years 1969-1973, in parallel with the early development of the Unix operating system; the most creative period occurred during 1972. Another spate of changes peaked between 1977 and 1979, when portability of the Unix system was being demonstrated. (Ritchie D. M. The development of the C programming language).

хохма именно в том что язык il по социологическим причинам стал лингвой франкой

Я сталкивался с мнением, что изначально C должен был быть промежуточным языком. Но после знакомства с упомянутой статьёй Ритчи и статьёй «Система разделения времени UNIX: Язык Си» (в авторах снова Ритчи и его коллеги по Bell), это убеждение сильно ослабло. Откуда у вас такая информация?

C-xC-f
()
Ответ на: комментарий от C-xC-f

инфа не тождественна данным не тождествнно знанию не тождественно пониманию - по всем парам

в тут уже упомянутом для старпёров юниксфесте Керниган опрашивал Кена и последний не сказал прямым утверждением - "я автор повсеместного языка - пока фанат вычислимости не прикрутил (плИвм_подобные)структуры к повсеметсному языку я по вниманию не проходил задачу перенести уних с асма на повсеместный) "

на tuh полно сырцов до K&R редакции и видно что сяшку точили без проекта ибо синтаксис for это буквально макрос ( кста достаточно распространёная среди Отцов практика у Вирта есть определение почти Паскаля на пять лет раньше вроде Эйлера в котором основной язык это макрорасширение не алгол60го передача имён как недоперелямбды)

не будь взрыва микропроцессорной техники от производителя памяти и по началу память была бутылочным сишка так и осталась бы промежуточным ибо она удобна для трансляции реально переносимого кода (awk sed prn и тд ) когда интерпретация становится узким по времени местом - а на асме как раз таки хотели перестать ибо людей стало не хватат

qulinxao3 ★☆
()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.