LINUX.ORG.RU

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

 , , ,


1

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

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

Что за попытки подмены понятий?

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

А почему некие личности стараются избавляться от Си - конечно понимаю, тому есть как минимум две причины:

1) орды нубов не умеют писать на Си, расстраиваются от своих багов и ищут чем бы себя подкостылить (другими языками)

2) всякие социально-политические активисты избавление от Си использую как повод избавления от софта с неугодными им лицензиями или неугодных им авторов

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

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

Всё-таки мне стандарт нужен именно как целевая платформа. Если есть какой-то код, на котором написано, что он написан для C89, то почти наверняка его можно скомпилировать и под MSDOS и под современный Linux и под Windows. Если же есть какой-то код, на котором написано, что он написан для gcc 2.95, то запустить его под MSDOS или под современную ОС скорее всего будет сложно.

Аналогично, есть программы для ANSI Common Lisp, а есть для SBCL и ССL. Во втором случае достаточно быстро устаревают.

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

написан для gcc 2.95, то запустить его под MSDOS или под современную ОС скорее всего будет сложно.

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

Если есть какой-то код, на котором написано, что он написан для C89, то почти наверняка его можно скомпилировать и под MSDOS и под современный Linux и под Windows.

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

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

Под современный линукс скорее всего получится.

Если указан конкретный компилятор, значит может использоваться трактовка UB этого компилятора. Значит либо собирать это программу при помощи gcc 2.95 (и возможно все библиотеки, которые она использует), либо проверять весь исходник, либо просто надеяться на авось.

Подозреваю, что в большинстве случаев из первого второе не следует.

На это обычно есть надежда. И если код открыт и кто-то автору укажет, что вышел за рамки (и пришёл исправление), то автор скорее всего исправит. А если «код для gcc 2.95», то автор вряд ли будет исправлять из-за того, что какой-то другой компилятор C89 на этот код ругается.

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

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

Всё-таки мне стандарт нужен именно как целевая платформа.

А мне нужно в квартире прибраться, но чё-т никто не прибирается.

Если есть какой-то код, на котором написано, что он написан для C89, то почти наверняка его можно скомпилировать

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

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

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

В стандарте ещё указано, что какая операция должна делать.

Если вдруг

int main()
{
  return 2+2;
}

скомпилируется, но вернёт не 4, то эта программа стандарту не соответствует.

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

Точнее компилятор не соответствует стандарту.

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

Под MSDOS - да, сложно т.к. скорее всего он рассчитан на 32-битные int и использует юниксовые хедеры.

Если в требованиях только компилятор, то хедеры обычно подразумеваются те, что с компилятором. Под MSDOS есть 32-битный Watcom С++.

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

В стандарте ещё указано, что какая операция должна делать.

Или не указано.

Если вдруг … скомпилируется, но вернёт не 4, то эта программа стандарту не соответствует.

Мы сегодня снова в школу первый раз в первый класс?

Что должно вернуть согласно стандарту

int main(void)
{
  return 2'147'483'647 + 2'147'483'647;
}

?

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

Что должно вернуть согласно стандарту

Эта программа не соответствует стандарту, так как содержит UB, если int не больше 32 бит.

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

содержит UB, если int не больше 32 бит.
содержит UB, если int не больше
содержит UB, если int
если int
если

Ты сам себя вообще читаешь? У тебя одна и та же программа одновременно соответствует и не соответствует одному и тому же стандарту.

Попробуй помедитировать над своим двоемыслием.

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

Программа соответствует стандарту, если в ней нет UB на всех компиляторах, соответствующих стандарту.

Эта не соответствует.

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

Эта не соответствует.

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

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

Программа соответствует стандарту, если в ней нет UB на всех компиляторах, соответствующих стандарту.

Секундочку. Это ты щас на полном серьёзе сделал утверждение, что программа сама по себе принципиально не может соответствовать стандарту?

Ну нихера ж себе куда заводит отрицание реальности…

на всех компиляторах, соответствующих стандарту.

В множество "компиляторов, соответствующих стандарту" входят компиляторы, где int имеет 16 бит. Это законно:

5.3.5.3.2 Characteristics of integer types <limits.h>
...
— width for an object of type unsigned int
UINT_WIDTH 16

То есть, по твоему определению, любая программа, где допускается, что значение типа int вмещает более 16 бит, является не соответствующей стандарту, так как будет не валидной в Turbo C 2.0. Это примерно все существующие сейчас программы на си.

Попробуй подумать ещё. Прошлый раз у тебя получилось на удивление хреново.

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

Программа не может соответствовать или не соответствовать стандарту.

А что тогда может или не может соответствовать стандарту?

Ну, раз уж ты, как я вижу, решил потягаться за лавры тупняка с монком…

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

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

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

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

Кстати, по-моему, одни из методов проверки кода – выделить тебе объект по границам страницы и посмотреть, выйдешь ты за него или нет.

Выделить по 2 байта на границах и проверить их состояние.

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

Ну это уже мы придумываем как написать свой санитайзер )))

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

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

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

А что тогда может или не может соответствовать стандарту?

Реализация языка.

// Ваш Копетан Очевидность

yorshka
() автор топика
Ответ на: комментарий от watchcat382

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

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

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

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

Strictly Conforming Program — это соответствующая стандарту, а не корректная или некорректная.

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

То есть, по твоему определению, любая программа, где допускается, что значение типа int вмещает более 16 бит, является не соответствующей стандарту, так как будет не валидной в Turbo C 2.0. Это примерно все существующие сейчас программы на си.

Любая программа, где int должен быть не меньше 32 бит, не соответствует стандарту именно потому, что не скомпилируется на Turbo C. Программа, где допускается, что значение типа int вмещает более 16 бит (то есть может быть 16, 32, 64, …) может соответствовать стандарту.

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

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

У одного массива участок abc:0001…abc:0019, линейно. У другого массива xyz:0005…xyz:0088, тоже линейно. Но расстояние между abc:0001 и xyz:0005 не существует.

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

Я про то, что если вычисление между x:1 и y:1 дало бы величину 100, то между x:5 и y:5 тоже дало бы туже величину. А то что эта величина бессмысленна, как и умножение и деление указателя, это и так очевидно.

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

Я про то, что если вычисление между x:1 и y:1 дало бы величину 100

Есть архитектуры, где из x:1 никак нельзя получить y:1 (например, Эльбрус с 128-битными указателями), потому что создание и вычисление значения указателя - это особая команда процессора.

То есть, можно придумать некое расстояние в километрах между Гамлетом и Раскольниковым, но смысла оно иметь не будет совсем.

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

Я про то, что если вычисление между x:1 и y:1 дало бы величину 100, то между x:5 и y:5 тоже дало бы туже величину.

Не обязательно. Можно предположить архитектуры, где память линейна, но у массива при создании можно указать направление роста. Тогда x:1 и y:1 дало бы величину 100, а между x:5 и y:5 было бы уже 108.

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

Любая программа, где int должен быть не меньше 32 бит, не соответствует стандарту

Ты сейчас (как и в прошлый раз) занёс в несоответствие стандарту примерно вообще все программы, кроме программ под MS-DOS.

Тебя точно ничего не смущает в этом манямирке?

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

Ты сейчас (как и в прошлый раз) занёс в несоответствие стандарту примерно вообще все программы, кроме программ под MS-DOS.

Нет. Все программы на Си, которые не требуют дополнительных библиотек, но они не работают под MS-DOS. Всё верно, эти программы не переносимы.

Также не соответствуют стандарту программы на Си, которые не требуют дополнительных библиотек, но они работают только под MS-DOS.

Соответствуют стандарту те программы, работа которых гарантирована на любом компиляторе, соответствующем стандарту.

Также, как со стандартом на HTML4. Если сайт открывается только IE5 или только Google Chrome, значит текст этого сайта в чём-то не соответствует стандарту. Если же соответствует стандарту, то будет работать с любым браузером, который этот стандарт поддерживает.

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

Не все же. А только те, где надеятся, что инт не будет меньше 32 бит.

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

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

С первого раза не дошло, попробуем ещё раз.

Соответствуют стандарту те программы, работа которых гарантирована на любом компиляторе, соответствующем стандарту.

Это примерно почти пустое множество. Буквально хелловорлды из учебников.

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

Ты сейчас (как и в прошлый раз) занёс в несоответствие стандарту примерно вообще все программы, кроме программ под MS-DOS.

Обожаю, когда сишники познают мир.

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

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

Все когда-то с этого начинали :)

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

Вот и верь после этого википедии :((((

Unsigned integer имеет не ограниченный тип, а type My_Unsigned is mod 2 ** 32;

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

Это примерно почти пустое множество. Буквально хелловорлды из учебников.

Это весь libc и coreutils. И все функции, не содержащие в себе более 65КБ данных. И всё, что работает на ELKS: https://github.com/ghaerr/elks

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

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

Может. Добавь компилятору -Werror.

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

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

yorshka
() автор топика
Ответ на: комментарий от Zhbert

А некоторые, как я, всё ещё продолжают ^.^ начинать хехехе

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

И все функции, не содержащие в себе более 65КБ данных

Ну, ок, давай попробуем как для самых маленьких.

Вот есть ПДД, где указано, что со знаком "Знак-1" скорость должна быть не более 60 км/ч. Вроде мы все понимаем, что если ТС движется со скростью 45 км./ч., то его поведение соответствует правилам. И есть "Знак-2", где указана минимальная скорость в 60 км./ч. И если ТС движется со скоростью в 75 км./ч., то оно соответствует правилам.

В средней школе это называется "числовые диапазоны".

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

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

Не беззнаковые числа, а «числа с типом Беззнаковое число».

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

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

Наоборот же. Ты вообще читать умеешь?

Ты пишешь «Это примерно почти пустое множество. Буквально хелловорлды из учебников.» в контексте «Соответствуют стандарту».

Я отвечаю «И все функции, не содержащие в себе более 65КБ данных».

Ты как-то из утверждения, что «все функции, не содержащие в себе более 65КБ данных соответствуют стандарты» делаешь процитированный вывод. Что я что-то писал про нарушение стандарта.

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

И все функции, не содержащие в себе более 65КБ данных

Ты как-то из утверждения, что «все функции, не содержащие в себе более 65КБ данных соответствуют стандарты» делаешь процитированный вывод. Что я что-то писал про нарушение стандарта.

Я запутался. Функции, содержащие данные больше 65КБ данных соответствуют стандарту или нет?

Ну или ещё проще:

int i = 100'000;

это корректный код с точки зрения стандарта или нет? Если нет, то какой именно пункт (требование) стандарта нарушено?

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

Не выяснили. Вы посмотрели на константные вычисления.

$ cat overflow.adb
with Ada.Text_IO;


procedure Hello is
 
use Ada.Text_IO;
i : Natural;
begin

i:= Integer'Value(Get_Line); 

Put_Line(Integer'Image(i + 1));

end Hello;
$ gnat compile overflow.adb
gcc -c -I/nix/store/sahxj9lw2ca3480pgsvn3h1qjf90wwyx-gnat-13.4.0/lib/gcc/x86_64-unknown-linux-gnu/13.4.0/adainclude -I/nix/store/sahxj9lw2ca3480pgsvn3h1qjf90wwyx-gnat-13.4.0/lib/gcc/x86_64-unknown-linux-gnu/13.4.0/adalib -B/nix/store/iszhvkw5j242b48rhk5j8dwingwiqsjp-gnat-wrapper-13.4.0/bin/ overflow.adb
overflow.adb:5:11: warning: file name does not match unit name, should be "hello.adb" [enabled by default]
$ gnatbind overflow        
$ gnatlink overflow
$ ./overflow 
2147483647

raised CONSTRAINT_ERROR : overflow.adb:13 overflow check failed

Выяснили. Уймись.

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