LINUX.ORG.RU

Новая версия книги «Modern C», адаптированная для C23

 , ,


3

8

Привет, ЛОР!

Я просто оставлю это здесь: https://inria.hal.science/hal-02383654

В книгу добавлены такие новые (для сишников) штуки как _BitInt(N), constexpr, арифметика с проверкой переполнения, auto, nullptr, typeof и прочие фишки нового стандарта.

Ответ на: комментарий от vbr

Затем же, зачем в C++. Значение NULL зависит от реализации, поэтому при использовании _Generic могут возникнуть лулзы. Смотри пример тут: https://en.cppreference.com/w/c/language/nullptr

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

Точно.

        _BitInt(n) (also accessible as signed _BitInt(n)), the bit-precise signed integer types (where n is replaced by an integer constant expression denoting the precise width (including the sign bit), that cannot be larger than BITINT_MAXWIDTH from <limits.h>)
        unsigned _BitInt(n), the bit-precise unsigned integer types (where n is replaced by an integer constant expression denoting the precise width, that cannot be larger than BITINT_MAXWIDTH from <limits.h>) 

https://en.cppreference.com/w/c/language/arithmetic_types

Оно не обязательно должно быть Big. Например, bool задаётся как unsigned _BitInt(1).

hateyoufeel ★★★★★
() автор топика
Ответ на: комментарий от Bfgeshka
$ cat bitint.c 
#include <limits.h>
#include <stdio.h>

int main() {
  printf("%d\n", BITINT_MAXWIDTH);
  return 0;
$ gcc -std=c23 bitint.c -o bitint && ./bitint
65535
$ clang -std=c2x bitint.c -o bitint && ./bitint
8388608

Ага. И возможно, очень большого лол. Метровый инт – это прямо тема!

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

Что там с правами? CC?

Я подумал, что если распечатать 10 шт в твердой обложке и с шитым блоком, то книжка выйдет чуть меньше 800 р., кто хочет присоединиться - проставьте лайки, можем заказать.

https://book-expert.ru/kalkulyator-pechati-knigi/

В Москве в первую очередь, отправить в регионы наверное тоже можно.

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

Какая-то наркомания.

Ну так перестань долбить.

Надеюсь это не примут.

Уже давно приняли. Это в стандарте. Сишники сасать nullptr набигают void* в укозателе можно грабить корованы

@zurg

NULL подразумевает адрес 0, а nullptr абстрактный указатель в никуда

Они оба кастуются в 0. Разница только в типе. Повторюсь, смотри как это с _Generic работает.

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

The kernel is written in the C programming language [c-language]. More precisely, the kernel is typically compiled with gcc [gcc] under -std=gnu11 [gcc-c-dialect-options]: the GNU dialect of ISO C11. clang [clang] is also supported, see docs on Building Linux with Clang/LLVM.

https://www.kernel.org/doc/html/next/process/programming-language.html

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

Я вот такую программу написал:

#include <stddef.h>
#include <stdio.h>

int main(void)
{
    printf("%d %d\n", NULL, nullptr);
}

Печатает 0 0. Значит никакой разницы нет. Поэтому вопрос остаётся - зачем нужен nullptr при наличии NULL. Походу вредители в стандарт пробрались. Надо откатываться на ANSI C…

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

man обратная совместимость. Он будет считаться написанным на c11, если будет использовать фишки c11. Сейчас большая часть написана на c89. Остальное твои фантазии.

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

Устаревшими технологиями иногда приходится пользоваться, такие дела :( Но уже делают убийцу Линукса на Rust совместимую с существующим userland.

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

Я вот такую программу написал:

Лучше не надо. У тебя плохо получается. Как минимум, не надо %d с указателями использовать.

А во-вторых,

#include <stddef.h>
#include <stdio.h>
 
void g(int*)
{
    puts("Function g called");
}
 
#define DETECT_NULL_POINTER_CONSTANT(e) \
    _Generic(e,                         \
        void* : puts("void*"),          \
        nullptr_t : puts("nullptr_t"),  \
        default : puts("integer")       \
    )
 
int main()
{
    g(nullptr); // OK
    g(NULL); // OK
    g(0); // OK
 
    auto cloned_nullptr = nullptr;
    g(cloned_nullptr); // OK
 
    [[maybe_unused]] auto cloned_NULL = NULL;
//  g(cloned_NULL); // implementation-defined: maybe OK
 
    [[maybe_unused]] auto cloned_zero = 0;
//  g(cloned_zero); // Error
 
    DETECT_NULL_POINTER_CONSTANT(((void*)0));
    DETECT_NULL_POINTER_CONSTANT(0);
    DETECT_NULL_POINTER_CONSTANT(nullptr);
    DETECT_NULL_POINTER_CONSTANT(NULL); // implementation-defined
}

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

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

процессоры бывают разные, не только неймоновской архитектуры, тот же эльбрус, и может оказаться, что это разные типы

Процессоры к сишным типам отношения не имеют вообще никакого, если не считать размерности.

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

Я ничего не понял, что ты написал. Это вообще C? Я таким не пользуюсь. Не проще было бы специфицировать этот implementation defined, чем переделывать язык с нуля?

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

Я ничего не понял, что ты написал.

Не понял, но мнение имеешь?

Это вообще C?

100% соответствующий стандарту.

Не проще было бы специфицировать этот implementation defined

Спроси у сишников. Они любят наоборот добавлять способов отстрела собственной жопы в язык. Так-то можно давно было и implementation-defined behaviour, и undefined behaviour специфицировать, что сделало бы мир гораздо лучше. Благо, реализаций Си на сегодняшний день примерно три штуки. Но си – язык, созданный наркоманами, развиваемый шизофрениками, и используемый на 37% дебилами. В нём так просто нельзя сделать нормально, нужно обязательно убить несколько сотен человек и пятнадцать лет бродить по пустыне, насаживая жопу на каждый встречный кактус.

чем переделывать язык с нуля?

А где тут что с нуля переделывается? Просто добавлен ещё один тип указателей nullptr_t, единственным значением которого является nullptr.

Повторю: с NULL проблема в том, что вот это вот всё ниже – валидные определения NULL, и они отстреливают жопы когда идёт диспатчинг по типу.

#define NULL 0
#define NULL (10*2 - 20)
#define NULL ((void*)0)
hateyoufeel ★★★★★
() автор топика
Последнее исправление: hateyoufeel (всего исправлений: 2)
Ответ на: комментарий от hateyoufeel

Не понял, но мнение имеешь?

Если я не понял, значит это не нужно. Мнение такое имею, да.

100% соответствующий стандарту.

Это какой-то плохой, негодный стандарт.

А где тут что с нуля переделывается? Просто добавлен ещё один тип указателей nullptr_t, единственным значением которого является nullptr.

NULL это и есть нуль, английский не знаешь что-ли. Переделывают основу языка. Теперь надо переписывать миллиарды строк кода, что, конечно, никто делать не будет. Поэтому будет в одном месте NULL, в другом nullptr, разброд и шатание. Придётся добавлять в Makefile проверки, чтобы никто этими nullptr-ами пользоваться не вздумал. Геморрой на ровном месте. Хотя вроде без -std=c23 оно и не компилируется, но подозреваю, что это ненадолго.

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

Это какой-то плохой, негодный язык.

Починил.

NULL это и есть нуль, английский не знаешь что-ли. Переделывают основу языка.

Чо?

Теперь надо переписывать миллиарды строк кода, что, конечно, никто делать не будет. Поэтому будет в одном месте NULL, в другом nullptr, разброд и шатание.

Во-первых, нет. Потому что обратная совместимость остаётся. Во-вторых, find . -type f -name '*.(c|h)' -exec sed -i 's/NULL/nullptr/g' {} \;. В третьих, эту фичу из C++ притащили, там она уже несколько лет есть. И никто пока не умер.

С nullptr проблема в другом есть:

The object representation of nullptr is same as that of (void*)0. If an lvalue conversion produces a nullptr_t value with a different object representation, the behavior is undefined.

Хотя можно было бы просто запретить касты в nullptr_t, потому что зачем это вообще делать-то.

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

Это всё словоблудие, сорри. Я пока не увидел, чем nullptr_t отличается от void* и чем nullptr отличается от NULL. В пример приводятся какие-то генерики, которыми, уверен, никто особо и не пользовался никогда. Ну так и пропишите в этих своих генериках нормальные правила взаимодействия с NULL и всего делов. Притащили какую-то недоделанную фичу в язык, а теперь ради неё начинают и язык менять. Как говорится - дай волку палец - откусит руку.

Что там в C++ я не знаю и знать не хочу. У этих клоунов даже для malloc каст вручную надо писать. Мертворожденный язык.

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

Что там в C++ я не знаю и знать не хочу. У этих клоунов даже для malloc каст вручную надо писать. Мертворожденный язык.

А.. ты тупняком троллишь. Понял-понял!

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

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

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

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

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

Вот жеж, первый раз посмотрел сюда https://en.cppreference.com/w/c/types/nullptr_t , где оно вроде бы чётко отличает типы и сделал далеко идущий вывод, сишечка как обычно оказалась внезапней, ну хорошо не строго гарантированно )

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

сишечка как обычно оказалась внезапней, ну хорошо не строго гарантированно )

Сишечка не гарантирует, что по адресу (void*)0x0 нет данных. Сишечка гарантирует, что при попытке обратиться по указателю с таким значением компилятор попытается насовать тебе говна в код. Борьба с компилятором – это вообще исконно сишная забава, заодно перетёкшая в плюсы. В других язычках компилятор – твой друг и брат родной, помогающий тебе делать код лучше и выдающий кучу инфы о нём.

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

А если вернуться взад - зачем вообще NULL нужен? Я вот пытаюсь сообразить.

  1. Как спец значение для malloc и подобных функций. Ну вообще логичней было бы его назвать тогда ERROR а не NULL. И постановить равным -1.

  2. Как значение по умолчанию для глобальных переменных. Но это вот как раз и правильней было бы сделать implementation-defined и не рассчитывать, что твои глобальные переменные будут инициализированы куда-то. Надо - пиши сам начальное значение. Тут вообще непонятно, зачем глобальные переменные инициализировать чем-то кроме мусора (если программист не указал начальное значение).

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

Я вот пытаюсь сообразить.

Похвально! Я понимаю, что это может быть сложно по-первой, но ты обязательно справишься!

А если вернуться взад - зачем вообще NULL нужен?

Потому что Тони Хоару, когда он проектировал очередной Алгол (Algol W), это показалось хорошей идеей. Он потом очень извинялся за это.

Как спец значение для malloc и подобных функций. Ну вообще логичней было бы его назвать тогда ERROR а не NULL. И постановить равным -1.

Ага, mmap() в хрюниксах так и делает. При ошибке mmap() возвращает MAP_FAILED, который как раз определён как (void*)-1.

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

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

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

арифметика с проверкой переполнения

$ cat /usr/lib/gcc/x86_64-linux-gnu/14/include/stdckdint.h

#define ckd_add(r, a, b) ((_Bool) __builtin_add_overflow (a, b, r))
#define ckd_sub(r, a, b) ((_Bool) __builtin_sub_overflow (a, b, r))
#define ckd_mul(r, a, b) ((_Bool) __builtin_mul_overflow (a, b, r))

:)

dataman ★★★★★
()