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

Цитата:

ISO C99 and ISO C++17 support floating-point numbers written not only in the usual decimal notation, such as 1.55e1, but also numbers such as 0x1.fp3 written in hexadecimal format.

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

Логикой и поиском.

Не, так не пойдёт. «Жопой чую что багов нет» – это не гарантии, это херня какая-то.

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

Нет. Крупные проекты обычно старые как мой дедушка, а в Си во времена динозавров имена полей структур были глобальными и не привязанными к собственно типу. Т.е. если проект тянется с 80х, то эта политика вероятно вызвана этим фактом. Или же тем, что программисты один раз заучили и с тех пор тянут.

Подробнее тут: https://a.opnxng.com/exchange/stackoverflow.com/questions/13366083/why-does-the-arrow-operator-in-c-exist#13366168

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

hateyoufeel вернулся к нам в виде доброгобледного привидения.👻

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

Ты про TCP на досуге почитай, прозреешь. А между прочим на этом все эти ваши интернеты живут, и чо?

TCP сравнительно норм для того, для чего его придумали и используют. Особенно если выкинуть некоторые дебильные надстройки типа долгой буферизации (см. TCP_NODELAY).

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

чувак не пали, а то опять зобанят и на лоре станет как-то скучно, и сишники бояться с ним связываться

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

TCP сравнительно норм

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

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

То есть ты не читал. Или не вникал. Или я не знаю.

Я хуже, я писал.

Там такие вещества в основе, что про них даже вслух говорить нельзя, можно в пространстве потеряться.

Подробности! Подробности!

Так-то я могу из башки назвать кучу проблем, которые существуют с TCP сегодня. Но это не следствие плохого дизайна, это следствие изменившихся условий. В противоположности языку Си, который хорошим дизайном не отличался вообще никогда (см. выше про глобальные имена полей, это вообще хтонический ад).

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

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

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

следствие изменившихся условий. В противоположности языку Си

Он что, в криокамере хранился все это время? Условия изменились для всех.

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

Он что, в криокамере хранился все это время? Условия изменились для всех.

Разные условия. Тем не менее, если ты напишешь с нуля реализацию TCP, руководствуясь исключительно RFC793, то с её помощью вполне можно насрать на ЛОРе. А вот на оригинальном Си ты сегодня вряд ли что-то полезное сделаешь.

Всё ещё хочу примеров упомянутой наркомании в TCP, к слову.

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

Подробности! Подробности!

Ладно, ты сам напросился.

Изначальные проблемы, архитектурные:

  • один потерянный пакет блокирует все последующие, даже если они из разных потоков. (огонь)
  • Связка надёжности и порядка - доставка по порядку и надёжность неразделимы.
  • 4-tuple привязка, соединение = (src IP, src port, dst IP, dst port). Сменил WiFi на LTE - конец, соединение разорвано.
  • 3-way handshake - 1 RTT до данных. Для спутниковых линков (~600ms RTT) - боль.

Окаменелости (последние лет 10-15):

  • NAT, файрволы, «optimizing proxies» ломают всё что не понимают. Расширить TCP заголовок невозможно. Поэтому MPTCP, TCP Fast Open, TCP MD5 (короче все) пробиваются с трудом.
  • Congestion control в ядре - любая инновация требует обновления ядра. BBR ждали годы.
  • огромные буферы в роутерах создают задержку без потерь, loss-based CC (Reno, Cubic) этого не видят. BBR частично решает, но rollout медленный.

Современные (5G, edge, IoT):

  • мобильные устройства меняют IP постоянно. TCP этого не умеет.
  • 0-RTT - для CDN/edge каждый RTT на вес золота.
  • TCP termination proxies - CDN terminating TCP и re-initiating - ломают end-to-end, добавляют задержку, видят plaintext.

Итого: TCP это окаменелое говно мамонта. Его нельзя эволюционировать - можно только заменить.

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

4-tuple привязка, соединение = (src IP, src port, dst IP, dst port). Сменил WiFi на LTE - конец, соединение разорвано.

Это правильно, не проблема.

Связка надёжности и порядка - доставка по порядку и надёжность неразделимы.

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

NAT, файрволы,

Congestion control

огромные буферы

мобильные устройства меняют IP

0-RTT - для CDN/edge

TCP termination proxies

А протокол-то тут при чём?

Это всё проблемы негров - сетевых программ. К пуговицам протокола претензии-то какие? Что его авторы в 70-каком-то году прошлого века не смогли спроектировать под большие буферы спутников, меняющие орбиту и IP-адреса?

один потерянный пакет блокирует все последующие, даже если они из разных потоков. (огонь)

Про что речь? Про out-of-band ?

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

один потерянный пакет блокирует все последующие, даже если они из разных потоков. (огонь)

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

Связка надёжности и порядка - доставка по порядку и надёжность неразделимы.

См. выше. TCP – это протокол для передачи потока данных, не независимых друг от друга сообщений.

4-tuple привязка, соединение = (src IP, src port, dst IP, dst port). Сменил WiFi на LTE - конец, соединение разорвано.

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

3-way handshake - 1 RTT до данных. Для спутниковых линков (~600ms RTT) - боль.

600ms RTT – это дерьмовый спутниковый линк. У Старлинка порядка 50ms. Ты ещё модемы для телефонных линий сюда притащи.

Итого: TCP это окаменелое говно мамонта. Его нельзя эволюционировать - можно только заменить.

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

На C программирую регулярно. Брат жив.

Нет, на оригинальном Си, описанном в CSM, ты не программируешь. Ты даже на K&R сегодня ничего не напишешь, компиляторы выкинули тот синтаксис. Это уже немного другой язык.

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

мобильные устройства меняют IP постоянно. TCP этого не умеет.

Кстати, вот это неправда. У операторов SDN по самые гланды, как раз чтобы IP не менялся и у тебя при смене БС не было переподключений. Но так-то да, это следствие 4-tuple идентификатора соединений.

Вот тут автор раскрывает тему получше: https://apenwarr.ca/log/20170810

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

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

Истинные профессионалы отпускают бороду там?

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

Нет, сейчас это делается для того, чтобы пользовательские дефайны случайно не сломали всё.

Например, из POSIX:

The <time.h> header shall also declare the itimerspec structure, which has at least the following members:

struct timespec  it_interval  Timer period. 
struct timespec  it_value     Timer expiration. 

Если бы там было просто `struct timespec interval`, то можно было бы сломать так:

// vasyan_time_utils.h

#pragma once

#include <stdio.h>
#include <time.h>

#define TS_FMT "%.9f"
#define TS_ARG(ts) ((ts).tv_sec + 1e-9 * (ts).tv_nsec)
#define vasyan_dump_timer(timer) \
    do { \
        struct itimerspec tmp_; \
        timer_gettime((timer), &tmp_); \
        printf( \
            "{interval=" TS_FMT ", value=" TS_FMT "}\n", \
            TS_ARG(tmp_.interval), \
            TS_ARG(tmp_.value) \
        ); \
    } while (0)
// main.c

#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include "vasyan_time_utils.h"

#define interval 10

void do_something(void)
{
    while (1) {
        printf("ЖОПА\n");
        sleep(interval);
    }
}

int main()
{    
    timer_t t;
    // ...
    vasyan_dump_timer(t); // раскроется в «TS_ARG(tmp_.10)»
}

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

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

4-tuple - это проблема. Да, «соединение между двумя точками» звучит логично. Но эти «две точки» - это IP-адреса, а не приложения. IP-адрес - это топология, не идентичность. Мой телефон - это я, независимо от того, через WiFi или LTE. TCP не разделяет «кто я» и «где я» - и это именно архитектурный дефект. QUIC использует Connection ID - идентификатор соединения, который не зависит от IP-адреса. Пакет несёт CID - получатель знает «это моё соединение», даже если IP сменился. TCP такого идентификатора не имеет - единственный способ определить «чьё это соединение» это 4-tuple.
Connection ID показывает что можно было спроектировать иначе.

«Используй другой протокол» - не аргумент. SCTP существует с 2000 года. Не пошёл. Почему? Потому что TCP - everywhere, NAT его пропускает, файрволы его понимают, все его поддерживают. «Используй другой протокол» - это как «не нравится страна - переезжай.» Технически верно, практически - нет. Экосистемная ловушка тоже проблема дизайна: протокол, который невозможно развивать и невозможно заменить - имеет architectural problem.

HoL blocking - «не по назначению»? HTTP/2 мультиплексирует поверх TCP именно потому, что TCP - единственный протокол, который проходит через интернет. Это не «не по назначению», это «единственный доступный инструмент не подходит для задачи, но альтернатив нет.» Если гаечный ключ не подходит - это проблема гаечного ключа? Или проблема в том, что других не завезли? «Проблемы экосистемы, не протокола» - экосистема выросла из протокола. TCP не предусматривал расширяемость заголовков - поэтому middleboxes заморозили его. Это как сказать «проблема не в архитектуре здания, а в том, что лифт не рассчитан на 100 этажей.» Ну так здание - 100 этажей. Архитектор должен был предусмотреть.

Итого: TCP спроектирован для сети 1974 года: стационарные хосты, проводные линии, доверенная middlebox-свободная среда. Для этих условий - идеален. Но «идеален для 1974» и «подходит для 2026» - не одно и то же. Проблема именно в том, что протокол нельзя обновить - он замерз. И это тоже проблема дизайна: отсутствие future-proofing.

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

Т.е. для формализации того, какие дефайны может дефайнить пользователь,

Для формализации идентификаторов есть префикс __. А твой пример - это костыль из прошлого, залитый гранитом в стандарт.

r--r--r--
()

_Generic

Авторы Си - хорошие люди, а вот те кто это туда внёс - наркоманы :)

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

Но эти «две точки» - это IP-адреса,

Нет, конечно. Это именно что приложения.

P-адрес - это топология, не идентичность.

  1. Наоборот.
  2. К TCP это отношение не имеет, так как к этому моменту IP уже выбран как протокол маршрутизации.

Мой телефон - это я

Бывает, но авторы TCP-то тут при чём?

TCP не разделяет «кто я» и «где я» - и это именно архитектурный дефект.

А ещё он не моет полы и не бегает за пивом. Дефект за дефектом, что ж ты будешь делать-то?

«Используй другой протокол» - не аргумент.

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

TCP спроектирован для сети 1974 года:

НЕ МОЖЕТ БЫТЬ!!! Как так? Как они посмели спроектировать протокол без учёта спутников и личностей, идентифицирующих себя с телефонами в далёком будущем?!?

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

Для формализации идентификаторов есть префикс __.

Гы-гы, он зарезервирован стандартом вообще-то. Для реализации (__attribute__((something))) и стандартной библиотеки (extern int vdprintf (int __fd, const char *__restrict __fmt, __gnuc_va_list __arg);. А itimerspec в <time.h> в стандартную библиотеку C99 не входит. И сторонним библиотекам что прикажешь делать? Они-то как между собой и пользовательским кодом договорятся?

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

Это уже немного другой язык.

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

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

Для формализации идентификаторов есть префикс __.

Гы-гы, он зарезервирован стандартом вообще-то.

Гы-гы, погугли, для чего он зарезервирован.

А itimerspec в <time.h> в стандартную библиотеку C99 не входит.

Потому что этот копролит достандартных времён в стандарте никому даром не упёрся.

И сторонним библиотекам что прикажешь делать? Они-то как между собой и пользовательским кодом договорятся?

Да, с божьей помощью и публичного включаемого файла.

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

Для условий 1981 года TCP подходил очень хорошо. Сишечка же никогда хорошей не была.

TCP и тогда был так себе. То что эта башня будет стоять боком было понятно уже на этапе дизайна. Единственный неубиваемый аргумент за - «и так сойдет!».

А сишечка прекрасна, несмотря на свой почтенный возраст.

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

TCP и тогда был так себе.

Но тебя тогда не было. Откуда ты знаешь, как он был?

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

Гы-гы, погугли, для чего он зарезервирован.

Зачем мне гуглить? Я же написал, для чего.

Потому что этот копролит достандартных времён в стандарте никому даром не упёрся.

struct dirent, struct stat, struct sigaction тоже, видимо, никому даром не упёрлись. А как директории-то читать теперь? И как получать сведения о файлах?

Да, с божьей помощью и публичного включаемого файла.

ЯННП. В публичном включаемом файле будет написано:

struct vasyan_hash_table_entry {
    char *key;
    char *value;
    struct vasyan_hash_table_entry *next;
};

Потом кто-то может задефайнить key, value или next. Что с этим делать?

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

А сишечка прекрасна, несмотря на свой почтенный возраст.

Особенно код вида:

struct Y {
  int b;
  int a;
};

int i = 5;
i->b = 42;  /* Write 42 into `int` at address 7 */
100->a = 0; /* Write 0 into `int` at address 100 */

Хорошо что хотя бы эту срань выкинули. Ещё бы выкинули придурков из комитета, которые лютуют за то, чтобы NULL+0 было неопределённым поведением.

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

Потом кто-то может задефайнить key, value или next. Что с этим делать?

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

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

Проблема в том, что твой тезис «префиксы делаются потому, что все привыкли к этому и для обратной совместимости со старым кодом» — неверен. Они делаются для того, чтобы все сущности могли договориться, какие идентификаторы им можно дефайнить, а какие — нет.

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

Потом кто-то может задефайнить key, value или next. Что с этим делать?

Я тебе более того скажу: кто-то может задефайнить true и false. И даже небо, и даже Аллаха!

Старая шутка времён C++98:

#define false (rand() % 100 == 1)
yorshka
() автор топика
Ответ на: комментарий от yorshka

Особенно код вида:

«а еще я могу и вот так и вот так себе дверью яйца прищемить!»

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

твой тезис «префиксы делаются потому, что все привыкли

Но это не мой тезис. Я даже примерно не понимаю, откуда ты его взял, и как привязал ко мне.

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

Я тебе более того скажу: кто-то может задефайнить true и false.

А вот это уже undefined behavior. Ну, в C99, и если перед/после подключить <stdbool.h>.

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

А это как понимать?

Т.е. для формализации того, какие дефайны может дефайнить пользователь,

Для формализации идентификаторов есть префикс __. А твой пример - это костыль из прошлого, залитый гранитом в стандарт.

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

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

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

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

Что тут не понятного?

Формальный, то есть формально описанный механизм для разрешения конфликтов описан в стандарте.

Торчащие публично левые структуры без префиксов с мутными полями - это тяжкое наследие тёмного прошлого.

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

Если стоит задача сломать препроцессором что-нибудь, то её можно сделать быстрее и проще, пример был выше.

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

0_0, это что за хтонь? я чёт не помню/не застал подобного. Откуда взялся адрес 7, да и что вообще происходит? :)

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

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

Ну как бы да, я это и имел в виду. Поэтому и нужны префиксы. А “__” вместо них использовать не получится.

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

А вот это уже undefined behavior. Ну, в C99, и если перед/после подключить <stdbool.h>.

Как будто сишников это останавливало когда-либо. Повторюсь, мы говорим про язык, где выражение NULL+0 является UB. Ну просто потому что.

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

Поэтому и нужны префиксы.

Так обычные библиотеки используют префиксы - libpng - png_ и т.д.

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

0_0, это что за хтонь? я чёт не помню/не застал подобного. Откуда взялся адрес 7, да и что вообще происходит? :)

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

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