LINUX.ORG.RU

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

 , , ,


2

5

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

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

#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 ★★★★★
()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.