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)
Ответ на: комментарий от Zhbert

Самое время разбавить местный балаган дедов-пердедов, не?

Тем не менее, баг во вполне серьёзном коде, написанном серьёзными дядями.

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

Просто прочитай, что такое #define и как это работает. Если уже читал — прочитай ещё раз, в другом месте и включив голову.

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

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

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

Ну стукни по голове того, кто тот код с багом писал. Си-то тут причём?

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

Баг был в коде, из которого я это вычленил. Здесь-то понятно что его нет.

А что говорят твои сотрудники?

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

Как ты запишешь отрицательное число в шестнадцатеричном формате? Как ты взведёшь 32 битовый флаг в знаковом целом?

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

Да, кому-то от этого легче. Ты вот скажи, зачем числовые литералы записываются в шестнадцатиричном формате, в каких ситуациях так случается? Ответишь правильно - получишь ответ на своё возмущение.

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

Как ты запишешь отрицательное число в шестнадцатеричном формате?

А в чём проблема? Пишешь -0x80000001, компилятор это спокойно переваривает.

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

меня постигло крайнее изумление!

«В первый раз?»

Авторы Си не могли перенестись во времени и увидеть, что нетипизированные константы (как в Go) — это удобно.

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

Это будет -2147483648 в десятичном формате. Что тебя смущает-то? Или ты хочешь обязательно дополненный код хексом писать? А зачем это делать, учитывая, что сишечка не гарантирует что реализация обязательно использует 2’s complement формат?

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

-0x80000000 это сколько будет?

-0x80000000 — это -0x80000000, ни меньше, ни больше. Идеальное число до тех пор, пока оно не будет использовано в конкретном контексте, где присутствуют ограничения типов.

Так сделано в Go и это устраняет проблему, о которой говорит ОП.

(И позволяет определить нижнюю границу int без хитрых трюков вроде -0x7fffffff - 1)

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

Так то да, но скорее наркоманы те кто продолжает сишку по доброй воле использовать, и совсем поехавшие фанатики те что-то новое начинают. По сабжу большая проблема то что оно вот тут «if(X == I) puts(«X == I»);» автокастит как попало и компилер даже не пикнет

zurg ★★
()

Всё таки это были 60-70е, Америка
так что могли быть хиппарями

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

«Проблема» в том для чего испольузют шестнадцатеричные и двоичные литералы, а для чего десятичные. Ты правда два и два не можешь сложить?

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

в конкретном контексте, где присутствуют ограничения типов

Ага, только в си это вот прямо сразу. Как препроцессор обработал и заработал компилятор, так появились ограничения.

legolegs ★★★★★
()

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

а еще удивишься что есть отрицательный ноль и не всегда равен положительному нулю

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

выпускной вон был вчера.

По посту видно, что он удался.

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

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

Ага. В линуксовом ядре, если быть точным. Там половина авторов не понимают, как работает язык, на котором они пишут. Я вот в детстве думал, что линукс пишут суровые бородатые профессионалы, а как стал этим за деньги заниматься, так волосы на жопе шевелятся от ужаса.

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

Суровыми бородатыми профессионалами не рождаются.

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

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

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

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

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

Мы на ЛОРе. Тут все такие.

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

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

Ещё больше тебя должно беспокоить что 1 это int а 3000000000 это long.

И кстати, если компилятор 32-битный то второе может оказаться unsigned int.

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

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

Ещё больше тебя должно беспокоить что 1 это int а 3000000000 это long.

Ребята из Microsoft с их LLP64 хотят с тобой поговорить.

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

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

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

Тут все такие.

Говори за себя.

о качестве кода в ядре ты сам можешь судить

Я и сужу по миллиардам устройств в промышленной эксплуатации - от роутеров до смартфонов.

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

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

Дефайн просто копирует буквально подстроку и заменяет ей другую подстроку перед компиляцией, компилятор видит 0х и ожидаемо назначает тип unsigned; вводить отрицательные смещения, маски или адреса – вот где наркомания затаилась. Никто так не делает, а все потому что максимум можно записать в signed форме не FFFF, а 7FFF.

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

переживает циклы рефакторинга, в рамках которых ломаются инварианты,

Всё упирается на самом деле вот во что: кто-то хочет сделать всё побыстрее и не хочет учитывать все аспекты своих правок.

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

А опасность от разных типов числовых констант не большая, чем от замены типа переменной где-то. Заменил тип - смотри где оно используется и как замена типа повлияет на работу. Заменил значение макроса с совсем маленького на какое-то большое сомнительного типа - тоже смотри все места где он указан. Там ведь не только тип может испортиться, где-то могут и арифметические переполнения случиться даже если тип остался как был. (int 1000*10 = 10000 везде и гарантированно, int 1000000000*10 = скорее всего что-то битое).

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

Говори за себя.

То есть ты - пустозвон и балабол. ЧТД.

Сорян, ты первый начал.

Я и сужу по миллиардам устройств в промышленной эксплуатации - от роутеров до смартфонов.

А можно про Windows так же судить? Не, нельзя? Классная же ОС выходит! Миллиарды юзеров не могут ошибаться. Про популярность языков программирования и JavaScript даже вспоминать не буду, а то у тебя припадок будет.

Тот факт, что Linux просто стал самым дешёвым и быстрым вариантом в случаях, когда качество не столь важно, от тебя, похоже, ускользает.

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

Рефакторинги лучше делать не методом «перепишем и заменим» а пошагово, на каждом шагу следя за всеми эффектами от внесённых в нём правок

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

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

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

Always has been.

Ещё сами K&R писали, что не надо проверять границы буферов, следить за временами жизни объектов и т.д.. Если что, оно же упадёт с SIGSEGV!

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

суровые бородатые профессионалы

Они и есть суровые и бородатые. Некоторые, правда, гладкобритые, нарушают традиции. Кстати, может в этом причина? В чем вопрос-то?

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

Привет, только придумай в этот раз другую шутку, за прошлую быстро отлетишь. Целую.

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

а еще удивишься что есть отрицательный ноль и не всегда равен положительному нулю

В 2’s complement нет отрицательного нуля. Про IEEE754 я тут сознательно не пишу, это вообще рак и хтонь.

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

А можно про Windows так же судить? Не, нельзя? Классная же ОС выходит!

Можно! Я разрешаю.

Да, выходит. Всё верно.

а то у тебя припадок будет.

Но я - не ты, у меня нет тяжёлых поражений церебральной системы. Поэтому нет, не будет.

Тот факт, что Linux просто стал самым дешёвым и быстрым вариантом в случаях, когда качество не столь важно, от тебя, похоже, ускользает.

Это не от меня ускользает факт, а от тебя ускользает мысль. «Вариантом» чего стал бедный Linux?

r--r--r--
()

Прикольно, конечно, но в C такой наркомании полно. Ужасный язык.

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

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

мы не доводим прямо до полного моделирования кода

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

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