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

Всегда рад стараться для моих фанатов :3

Я заметил, что тут Development почти вымер без меня. Вот, вношу свою лепту в оживление ресурса.

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

Является ли Brainfuck низкоуровневым?

Да, если запустить его на слегка модифицированной машине Тьюринга.

Вот именно, что «если». А на обычном x86_64? Так-то и здесь можно заявить, что (оригинальный, описанный в CSM) C является низкоуровневым для процессоров типа PDP-11, но на современных машинах это уже совсем далеко не так. Как минимум, потому что стандарт языка явно описывает поведение некой абстрактной машины Си, которая может ложиться, а может и не ложиться на реальное железо.

Окей, задача посложнее. Является ли лямбда-исчисление низкоуровневым языком?

Для ясности, я использую следующую понятийную базу: если программист думает в терминах решаемой задачи — язык высокоуровневый. Если в терминах устройства, на котором код будет исполняться — низкоуровневый.

Окей, а какой язык Rust? Потому что на нём вон пишут одновременно и операционки, где надо знать про выравнивание, и веб-говно – причём и бэк, и фронт на WASM. Это всеуровневый язык сразу?

Кстати, а если для Си выкатить библиотеку бухгалтерского учёта, он сразу взлетит в уровне?

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

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

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

Да вроде есть уже.

Не думаю. Там есть сахар для instanceof, но деструктуризации нет, поэтому это не совсем то. Они идут в ту сторону, но пока не дошли.

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

А на обычном x86_64?

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

Является ли лямбда-исчисление низкоуровневым языком?

Да.

Окей, а какой язык Rust?

Про Раст я знаю только из обсуждений на лоре. Знаю, что он годится поджигать жопы, но как это относится к низко/высоко-уровневости языка сказать затрудняюсь.

а если для Си выкатить библиотеку бухгалтерского учёта

Пупок развяжется.

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

А в С нет вообще ничего. Только указатели на буфера в памяти и арифметика для них.

Это очень ограниченный взгляд на язык. Конечно же в C гораздо больше фич. Например в C есть функции.

О чём тут вообще спорить?

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

На мой взгляд практика - критерий истины. На C пишут ядро, в котором десятки миллионов строк кода, работающих согласованно. На C пишут уйму userspace софта. Например я сейчас использую GNOME, вполне себе близкий к пользователю софт, в котором прекрасно отображаются самые разные строки. Т.е. уровень C на практике достаточен для реализации программ произвольной сложности.

На ассемблере ничего подобного я не знаю. Вроде была в 90-х какая-то ОС на ассемблере, но я лично не пользовался и не могу сказать, насколько далеко она ушла от игрушечного уровня. Поэтому пока я по этому критерию поставил бы уровень C ощутимо выше уровня ассемблера.

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

И что вообще значит, что в C нет строк? "Hello, world!" - вот и строка. Так можно дойти и до того, что в Java тоже нет строк. Там только классы, например java.lang.String и массивы. Ну и что. Это только значит, что поддержка строки реализуется средствами языка. Как в Java, так и в C. Это даже хорошо.

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

Для начала, в приведённом коде нет ни одной константы. Штуки 0x80000001 и 2147483649 называются «литералы». И не знаю как для сишников, а для плюсовиков вполне привычное дело, что тип литерала зависит от способа его записи — на литералы даже можно всякие модификаторы вешать. А чтобы непоняток не было, придумали константы, с ключевым словом const. Там какой тип напишешь, такой и будет.

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

Но доказательств не предоставил.

Разупорись

То есть, не предоставил. Следовательно, ты - балабол и пустозвон.

Полезных программ без препроцессора. Синтетические примеры не считаются,

Программа из 10 строчек, управляющая 100 шлагбаумами с суточным трафиком в 1000 водителей каждый имеет 100 000 (прописью - сто тысяч) пользователей. Это полезная программа, и она может быть написана без директив препроцессора.

Я понимаю, что тут распространён повальный турбоаутизм

И самый ярчайший его представитель - это ты.

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

Ну вот. У одного уровень зависит от задач, у другого – от платформы исполнения. В принципе, термин «низкоуровневый» язык достаточно бессмысленным получается, потому что единственный ЯП, который ему соответствует, это язык ассемблера. Т.е. как написал @pfg выше, текстовое представление опкодов процессора. Си этому определению безусловно никак не соответствует, потому как глядя на код на языке Си без подробного знания внутренностей компилятора практически невозможно предугадать, в какой ассемблерный код он превратится.

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

JVM bytecode
Ассемблер полностью из «платформонезависимых свойств» без явного указания целевой платформы

  1. Речь шла про программы ассемблера, а не язык:

У ассемблеров, как и у других программ,

  1. JVM bytecode - это не языка ассемблера, а машинные инструкции.

  2. Виртуальность платформы не делает платформу менее платформой. Скорее, где-то даже наоборот.

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

Программа из 10 строчек, управляющая 100 шлагбаумами с суточным трафиком в 1000 водителей каждый имеет 100 000 (прописью - сто тысяч) пользователей. Это полезная программа, и она может быть написана без директив препроцессора.

Если честно, я скорее ожидал, что ты тут притащишь выхлоп gcc -E в качестве аргумента, но ты всё ещё сыпешь странными абстрактными примерами, которые непонятно что должны доказывать и непонятно зачем.

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

Например в C есть функции.

Ну, офигеть теперь.

На C пишут уйму userspace софта

Раньше писали, а сейчас — дописывают. Тот же гном родом из 98-го года. Да и для него то vala изобретут, то начинают половину оболочки на js/css переписывать.

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

Этого мало. Нужно ещё, чтобы он не сильно проигрывал другим альтернативам. Так-то и на коболе с фортраном можно много чего сделать и раньше делали. А сейчас не станут. С подходом «практика — критерий истины» я согласен целиком и полностью. На практике тридцать лет назад С был языком по умолчанию для всего. Сейчас это очень нишевая штука, когда из железа нужно каждый процент производительности выжимать.

На ассемблере ничего подобного я не знаю. Вроде была в 90-х какая-то ОС на ассемблере

https://kolibrios.org/en

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

JVM bytecode - это не языка ассемблера, а машинные инструкции.

Виртуальность платформы не делает платформу менее платформой. Скорее, где-то даже наоборот.

Курица — не птица, Беларусь — не заграница.

Only man has reason. A woman is not a man. Therefore, women don’t have reason.

Болтологией можно заниматься сколько угодно.

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

И что вообще значит, что в C нет строк?

Это значти, что для того, чтобы работать в С со строками и не облажаться (мало кому удаётся), нужно постоянно держать в памяти и никогда не забывать, что строка это на самом деле указатель на терминированную нулевым байтом область в памяти. Тогда как в высокоуровневом языке строка — это некая абстрактная сущность, определяемая через набор допустимых операций по работе со строками.

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

В принципе, термин «низкоуровневый» язык достаточно бессмысленным получается, потому что единственный ЯП, который ему соответствует, это язык ассемблера.

Из этого следует, что ваше определение никуда не годится, только и всего. Меняйте формулировку.

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

Я тут утверждаю, что препроцессор – ЧАСТЬ языка Си, причём достаточно неотъемлемая, потому как я ещё не видел программ на Си

[Опровержение проверяемым доводом].

которые непонятно что должны доказывать и непонятно зачем.

Это потому что твой суетливый шизоидный мозг не в состоянии удержать в голове контекст более одного сообщения. Зачем ты с таким уровнем айсикью в программисты подался - вообще не понятно. Шёл бы в продаваны - там тебя ждал бы сокрушительный успех.

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

Болтологией можно заниматься сколько угодно.

Можно, конечно. Хочется заниматься - занимайся. Лично я - пас.

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

так можно любой скриптовый язык объявить языком машинных инструкций среды исполнения скрипта :)
и даже, с точки зрения философии, быть правым в этом :)
виртуальная консоль такая же консоль, только другая и вообще не железная :)
если обмазаться философиями и гуманитарщиной можно что угодно вывести из чего угодно… ибо оно не ортогональная математика.

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

так можно любой скриптовый язык объявить языком машинных инструкций среды исполнения скрипта :)

Нет, нельзя. Потому что у "любого скриптового языка" нет спецификации и модели памяти. У java - есть.

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

виртуальная консоль такая же консоль

А вот тут ты прав.

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

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

Ну и ладно. Для работы со связными списками нужно ровно то же самое, например. По-мне это вообще больше вопрос дизайна хорошего API для работы со строками. Тот API, который есть в стандартной библиотеке C - не выдерживает никакой критики. Но есть и библиотеки получше. Со строками, как с массивами, вообще работать не вполне корректно, если мы говорим про тот же юникод. Там даже итерация по кодовым точкам будет в библиотечном коде скорей всего.

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

Ну вот Java и Kotlin значит выпадают из списка высокоуровневых языков. Там строка это не абстрактная сущность, это вполне конкретный библиотечный класс. Да, там есть «сахар» в виде строковых литералов, неявно создающих объекты этого класса, но это и в C есть по сути. И если хорошенько упороться, можно даже этот класс подменить на что-нибудь другое.

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

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

тыжъ только что заявлял «Виртуальность платформы не делает платформу менее платформой», а почему в память так уперся ?? :) виртуальная м-м-м память в виде готовых переменных чем тобе не вариант ??

А вот тут ты прав.

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

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

почему ??

Потому что нет вычислительной платформы - инструкций исполнителя + модели памяти.

однако алгоритмы работают циферки обрабатываются и все ок.

Нет, не работают, нет не обрабатываются, потому под "любой скриптовый язык" программу создать на языке инструкций вычислителя нельзя. Под некоторые - wasm например - можно.

У тебя и webassembly не платформа, чтоле?

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

Для работы со связными списками нужно ровно то же самое, например.

Не нужно. В высокоуровневом языке достаточно знать операции получения головы, хвоста, проверки на пустоту и добавления элемента. Заметьте, операции, а не внутреннее устройсво (которое, к слову, вовсе не обязано быть построено на указателях. Можно выпить квасу и реализовать односвязные списки чисто на лямбдах).

Но есть и библиотеки получше.

В каждом крупном проекте своя. Иногда три-четыре разных.

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

Естественно кто-то где-то астрактную сущность должен реализовать. Иначе как с ней преполагается работать?

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

может быть написана без директив препроцессора.

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

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

Замени "написана" на "скомпилирована". Посмотрим, поможет ли тебе это.

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

Да, можно.

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

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

Ну и да.

А также наверняка может быть написана без использования массивов.

Препроцессор может быть использован для любого произвольного текста. Его использование для исходного кода программ на си - лишь историческая традиция. Попробуй использовать массивы из Си без самого Си.

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

Кстати да, этот прикол с выравниванием есть на мой взгляд и в плюсах. Отчего часто вылезают разные баги, либо программа неимоверно тупит и называется это сборкой под процессор, хотя архитектура x86 это должен быть один и тот же код, а на деле в дистрибутивах не используются как правило оптимизации компилятора из-за разницы в строении процессоров. После этого выходит великий и ужас Линус Торвальдс, заявляет что все классно и ядро должно работать на любом x86_64 а какие-то самоделкины видимо врубают принудительную оптимизацию и называют это CachyOS и что там все инструкциями, которых в коде нет изначально. То есть видимо оптимизации компилятора снова. И предугадать в какой код тогда все превратится довольно сложно. Подозреваю без ИИ тут не обошлось.

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

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

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

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

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

Препроцессор может быть использован для любого произвольного текста.

Это просто деталь реализации. Вполне можно сделать компилятор, в котором препроцессор будет в одном бинарнике с собственно компилятором, и это всё ещё будет соответствующей стандарту реализацией.

Попробуй использовать массивы из Си без самого Си.

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

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

В принципе, термин «низкоуровневый» язык достаточно бессмысленным получается, потому что единственный ЯП, который ему соответствует, это язык ассемблера.

Из этого следует, что ваше определение никуда не годится, только и всего. Меняйте формулировку.

Тут никакое определение не годится. Потому что то же лямбда-исчисление может являться чем угодно, но только не низкоуровневым языком, если, конечно, ты не готов назвать одну из самых популярных реализаций лямбда-исчисления низкоуровневым языком (System F – буквально типизированное лямбда-исчисление и ничего больше).

Я просто пытаюсь вычленить твоё определение и довести его до абсурда, и это не то чтобы требует больших усилий.

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

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

Просто что?

Массив в ANSI C — это:

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

enep ★★★★★
()
#include <stdbool.h>
#include <stdio.h>

// Макрос IS_HEX проверяет тип аргумента на этапе компиляции
// _Generic - это конструкция C11 для выбора по типу выражения
// Если аргумент имеет тип unsigned int, макрос возвращает true
// Если аргумент имеет тип long, макрос возвращает false
// Для других типов код не скомпилируется (нет ветки default)
#define IS_HEX(x) \
    _Generic((x), \
        unsigned int: true, \
        long: false \
    )

// X определён как шестнадцатеричная константа 0x80000001
// В беззнаковом представлении 32-битного числа это 2^31 + 1 = 2147483649
#define X 0x80000001

// I определён как десятичная константа 2147483649
// Это число выходит за пределы знакового 32-битного int (макс. 2147483647)
// Компилятор автоматически интерпретирует его как unsigned int
#define I 2147483649

int main(void) {
    // Сравнение X и I: оба имеют значение 2147483649
    // Оба числа приводятся к беззнаковому типу, поэтому они равны
    // Условие истинно → выведется "X == I"
    if(X == I)
        puts("X == I");

    // IS_HEX(I): I имеет тип unsigned int (т.к. число 2147483649 > INT_MAX)
    // _Generic выберет ветку unsigned int → макрос вернёт true
    // !true = false → условие ложно, строка НЕ выведется
    if(!IS_HEX(I))
        puts("I is not hexadecimal");

    // IS_HEX(X): X — это литерал 0x80000001
    // В C шестнадцатеричный литерал может быть знаковым или беззнаковым
    // 0x80000001 при 32-битной арифметике: если long 32-битный, то тип будет unsigned int
    // Если long 64-битный, то тип может быть unsigned long или long
    // В зависимости от компилятора, IS_HEX может вернуть true или false
    // На многих 32-битных системах выведется "X is hexadecimal"
    if(IS_HEX(X))
        puts("X is hexadecimal");

    return 0;
}

Ключевые моменты:

  • Выход за диапазон int: 2147483649 больше INT_MAX (2147483647), поэтому компилятор C автоматически назначает тип unsigned int
  • Тип шестнадцатеричных констант: 0x80000001 в 32-битной системе имеет тип unsigned int, но на 64-битной может быть long или unsigned long
  • Поведение макроса: IS_HEX работает не по синтаксису (форма записи числа), а по его типу — он проверяет, является ли выражение беззнаковым целым (unsigned int)
enep ★★★★★
()
Ответ на: комментарий от anonymous

Если кто-то решится переопределить сдвиг так, чтоб не было UB, то ему придётся или проверку в рантайме делать или, как предложили выше, помещать операнд в строгие границы. Так или иначе даже для тех случаев, когда код верный появятся дополнительные операции.

Почему? Его можно определить в этих случаях как Unspecified behavior. То есть результат сдвига должен что-то всегда вернуть и в некоторых случаях это что-то зависит от платформы и может быть недокументировано.

Но чем тебе, как программеру на языке C, это поможет?

Поможет тем, что компилятор не будет вызов функции

int f(int x, int y, int *z) 
{
  if (x > 0) {
    *z = x >> y;
    return x + y
  } else {
    return x - y
  }
}

оптимизировать

  k = f(x, 42, &z);

в

  k = x - 42;
monk ★★★★★
()
Ответ на: комментарий от monk

Всё вы верно пишете. И в то же время это не проясняет мой вопрос. Попробую развернуть мысль.

То, что можно доопределить все UB до какого-то единого соглашения, это понятно. Можно приводить частные случаи к единому на всех платформах поведению. Где-то это ничего не будет стоить, где-то появятся накладные расходы. Но вот какая от этого выгода, мне неочевидно.

То же элементарное деление на 0, например, таких дебатов не вызывает. Кому нужно, тот проверит делитель. Ну и про операнды в сдвигах будет помнить тоже. Проще говоря, самостоятельно вспоминать про UB, а компилятор при этом никакой инициативы не навязывает. Мне кажется, 30 лет назад это больше ценилось чем заранее перестраховываться от всех возможных случаев. Максимально возможная производительность в общем случае, но недоопределённые углы.

А вы как считаете, стоит оставить Си в покое вместе с UB или с этим непременно «нужно» что-то делать?

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

А вы как считаете, стоит оставить Си в покое вместе с UB или с этим непременно «нужно» что-то делать?

Ну, смотря чего ты хочешь. Если ты хочешь, чтобы компилятор делал с кодом первертозный BDSM, то можно оставить как есть. Если же ты хочешь какой-то определённости и гарантий, то от UB стоит избавиться.

Плюс, оно на самом деле вообще не нужно в современных реалиях.

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

Переходи на язык Shakespeare, там ваще всё логично:))

REDDERa
()

Во-первых, надо выхлоп писать более понятно, а не 3 сообщения было выведено на экран, компилировать на телефоне я это не буду. Во-вторых, компиляция в голове говорит что X=I (значение и представление это разные вещи), I в десятичном представлении, X в шестнадцатеричной. Про выходы за границы и прочие детали мне думать не хочется.

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

Потому что то же лямбда-исчисление может являться чем угодно, но только не низкоуровневым языком

Оснований для подобных категорических заявлений мы, конечно, не увидим. Да и зачем? Пустое это.

Я просто пытаюсь

троллить тупостью. Ваше право, не стану осуждать.

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

Ну, смотря чего ты хочешь.

Если прям вообще желать от души, то хочу чтоб Си жил долго и счастливо.😇

Возможно, для этого действительно придётся как-то ублажать избалованную публику. Однако есть ощущение, что с этим будут тянуть так долго, что раньше БЯМы натаскают до такой степени, что они будут вежливо превращать любой код на Си в бриллиант.

Плюс, оно на самом деле

Мне кажется, это близко к моим предположениям. Если БЯМ будет «додумывать» за пользователем, то вопрос про UB для человека потеряет актуальность. Правда, кто знает, как скоро вместе с ним потеряет для человека актуальность и вопрос программирование в целом.

anonymous
()

Вы неправильно определяете хексовость. Вот, как правильно:

#include <stdio.h>

#define STRINGIFY_(x) #x
#define IS_HEX(x) is_hex_impl_(STRINGIFY_(x))

static inline __attribute__((always_inline))
int is_hex_impl_(const char *s)
{
    if (*s != '0') { return 0; }
    ++s;

    if (*s != 'x' && *s != 'X') { return 0; }
    ++s;

    if (*s == '\0') { return 0; }

    for (;;) {
        char c = *s;
        if (!(
            (c >= '0' && c <= '9') ||
            (c >= 'a' && c <= 'f') ||
            (c >= 'A' && c <= 'F')))
        {
            break;
        }
        ++s;
    }

    if (*s == 'u' || *s == 'U') { ++s; }
    if (*s == 'l' || *s == 'L') { ++s; }
    if (*s == 'l' || *s == 'L') { ++s; }

    return *s == '\0';
}

#define X 0x80000001
#define I 2147483649

int main()
{
    if (X == I) puts("X == I");

    if (!IS_HEX(I)) puts("I is not hexadecimal");

    if (IS_HEX(X)) puts("X is hexadecimal");
}

А в C++ без препроцессора так можно?

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

Потому что то же лямбда-исчисление может являться чем угодно, но только не низкоуровневым языком

Оснований для подобных категорических заявлений мы, конечно, не увидим. Да и зачем? Пустое это.

Основания:

  1. потому что современные процессоры не программируются в терминах лямбда-исчисления;
  2. потому что любая реализация лямбда-исчисления требует мощной поддержки рантайма, в частности, требуется сборщик мусора.

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

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

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