Подтвердить что ты не робот

Какова конкретизация бинарных литералов в С++ 14?

Я пробовал искать, но не смог найти много о бинарных литералах и суждениях. Являются ли бинарные литералы малодушными, big-endian или чем-то еще (например, сопоставление целевой платформы)?

В качестве примера, что такое десятичное значение 0b0111? Это 7? Специфичность платформы? Что-то другое? Изменить: я выбрал плохую величину 7, поскольку она представлена ​​в пределах одного байта. Несмотря на этот вопрос, вопрос был достаточно удовлетворен.

Некоторые предпосылки: В основном я пытаюсь понять, что такое значение наименее значимых битов, и маскировать его бинарными литералами, казалось, было хорошим способом... но только если есть какая-то гарантия о контенте.

4b9b3361

Ответ 1

Краткий ответ: нет ни одного. Напишите число так, как вы бы написали его на бумаге.

Длинный ответ: Endianness никогда не раскрывается непосредственно в коде, если вы действительно не пытаетесь его вывести (например, используя трюки с указателями). 0b0111 - это 7, это те же правила, что и в шестнадцатеричной записи

int i = 0xAA77;

не означает 0x77AA на некоторых платформах, потому что это было бы абсурдно. Куда бы все-таки добавились лишние нули с 32-битными значениями? 0x77AA0000 ли они дополнены спереди, тогда все перевернется на 0x77AA0000, или они будут добавлены после? Я понятия не имею, что кто-то ожидает, если бы это было так.

Дело в том, что C++ не делает никаких предположений о порядке байтов машины *, если вы пишете код с использованием примитивов и литералов, которые он предоставляет, поведение будет одинаковым для разных компьютеров (если вы не начнете обходить тип система, которую вам может понадобиться).

Для адреса вашего обновления: номер будет таким, каким вы его напишите. Биты не будут переупорядочены или что-либо подобное, старший бит слева и младший бит справа.


Кажется, здесь есть недоразумение о том, что такое порядок байтов. Порядковый номер относится к тому, как байты упорядочены в памяти и как они должны интерпретироваться. Если бы я дал вам число "4172" и сказал "если это четыре тысячи сто семьдесят два, что за порядковый номер", вы не сможете дать ответ, потому что вопрос не имеет смысла. (некоторые утверждают, что самая большая цифра слева означает большой порядковый номер, но без учета памяти вопрос о порядковом порядке не отвечает или не актуален). Это просто число, нет байтов для интерпретации, нет адресов памяти. Предполагая 4-байтовое целочисленное представление, байты, которые ему соответствуют:

        low address ----> high address
Big endian:    00 00 10 4c
Little endian: 4c 10 00 00

Итак, учитывая любой из них и сказав, что "это внутреннее представление компьютера 4172", вы можете определить, является ли он младшим или большим порядковым номером.

Теперь рассмотрим ваш двоичный литерал 0b0111 эти 4 бита представляют один ниббл и могут быть сохранены как

              low ---> high
Big endian:    00 00 00 07
Little endian: 07 00 00 00

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

Endianness не об отдельных битах. Учитывая, что байт равен 8 битам, если я 0b00000111 вам 0b00000111 и скажу: "Это младший или старший порядковый номер?" опять же, вы не можете сказать, потому что у вас есть только один байт (и нет адресов). Порядковый номер не относится к порядку битов в байте, он относится к порядку целых байтов относительно адреса (если, конечно, у вас нет однобитных байтов).

Вам не нужно заботиться о том, что ваш компьютер использует для внутренних целей. 0b0111 просто избавляет вас от необходимости писать что-то вроде

unsigned int mask = 7 // only keep the lowest 3 bits

написав

unsigned int mask = 0b0111;

Не нужно комментировать, объясняя значение числа.


* В C++ 20 вы можете проверить порядковый номер, используя std :: endian.

Ответ 2

Все целые литералы, в том числе двоичные, интерпретируются так же, как мы обычно читаем числа (наиболее значимые самые последние цифры).

Стандарт С++ гарантирует такую ​​же интерпретацию литералов, не заботясь о конкретной среде, в которой вы находитесь. Таким образом, в этом контексте вам не нужно заниматься контентом.

Ваш пример 0b0111 всегда равен семи.

Стандарт С++ не использует термины endianness в отношении числовых литералов. Скорее, он просто описывает, что литералы имеют последовательную интерпретацию и что интерпретация такова, которую вы ожидаете.

Стандарт С++ - Целочисленные литеры - 2.14.2 - параграф 1

Integer литерал представляет собой последовательность цифр, которая не имеет периода или экспоненциальная часть, с необязательным разделением одинарных кавычек, которые игнорируются при определении его стоимости. Integer литерал может иметь префикс, который определяет его базу и суффикс, который указывает его тип. Лексически первая цифра последовательности цифр является самой значительной. двоичный целочисленный литерал (базовый два) начинается с 0b или 0B и состоит из последовательность двоичных цифр. восьмеричный целочисленный литерал (базовая восьмерка) начинается с цифры 0 и состоит из последовательности восьмеричных цифр. Дискретный целочисленный литерал (базовая десятка) начинается с цифры, отличной от 0 и состоит из последовательности десятичных цифр. Шестнадцатеричное целое число буквальный (базовый шестнадцати) начинается с 0x или 0X и состоит из последовательности шестнадцатеричных цифр, которые включают десятичные цифры и буквы от a до f и от A до F с десятичными значениями через десять 15. [Пример: число двенадцать может быть записано 12, 014, 0XC или 0b1100. Литералы 1048576, 1048576, 0X100000, 0x100000 и 0004000000 все имеют одинаковое значение. - конец примера]

Википедия описывает, что такое endianness, и использует нашу систему чисел в качестве примера для понимания big-endian.

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

Системы Big-Endian хранят наиболее значимый байт слова в наименьший адрес, а младший байт хранится в самый большой адрес (также см. "Самый значительный бит" ). Little-младшему системы, напротив, сохраняют наименее значимый байт в наименьшем адрес.

Примером по поводу суждения является мысль о том, как десятичное число записывается и читается в примечании о стоимости места. Предполагая, что система письма где числа записываются слева направо, крайняя левая позиция аналогичный наименьшему адресу используемой памяти и самому правому положение наибольшее. Например, число сто двадцать три написано 1 2 3, а сотни - самые левые. Любой, кто читает этот номер также знает, что самая левая цифра имеет самое большое место стоимость. Это пример широкомасштабной конвенции, жизнь.

В этом контексте мы рассматриваем цифру целочисленного литерала как "байт слова", а слово должно быть буквальным. Кроме того, самый левый символ в литерале считается наименьшим адресом.

В литеральном 1234 цифры 1, 2, 3 и 4 являются "байтами слова", а 1234 - "словом". С бинарным литералом 0b0111 цифры 0, один, один и один являются "байтами слова", а слово 0111.

Это соображение позволяет нам понять предисловие в контексте языка С++ и показывает, что целочисленные литералы похожи на "big-endian".

Ответ 3

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

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

Ответ 4

Языки C/С++ не заботятся о сущности многобайтовых целых чисел. Компиляторы C/С++. Компиляторы анализируют исходный код и генерируют машинный код для конкретной целевой платформы. Компилятор, в общем, хранит целые литералы так же, как и хранит целое число; так что целевые команды ЦП будут непосредственно поддерживать чтение и запись их в памяти.

Компилятор учитывает различия между целевыми платформами, поэтому вам не нужно.

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

Ответ 5

Одна фотография иногда более тысячи слов.

источник по отношению к контенту памяти

Ответ 6

Порядковый номер определяется реализацией. Стандарт гарантирует, что каждый объект имеет представление объекта в виде массива char и unsigned char, с которым вы можете работать, вызывая memcpy() или memcmp(). В С++ 17 допустимо reinterpret_cast указатель или ссылку на любой тип объекта (не указатель на void, указатель на функцию или nullptr) на указатель на char, unsigned char или std::byte, что допустимые псевдонимы для любого типа объекта.

Что люди имеют в виду, когда говорят о "порядке байтов", так это порядок байтов в представлении этого объекта. Например, если вы объявляете unsigned char int_bytes[sizeof(int)] = {1}; и int i; затем memcpy( &i, int_bytes, sizeof(i)); Вы получаете 0x01, 0x01000000, 0x0100, 0x0100000000000000 или что-то еще? Ответ: да. Существуют реальные реализации, которые дают каждый из этих результатов, и все они соответствуют стандарту. Причина этого в том, что компилятор может использовать собственный формат процессора.

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

Язык позволяет вам выстрелить себе в ногу, произвольно поворачивая биты представления объекта, но он может получить представление о ловушке, которое может вызвать неопределенное поведение, если вы попытаетесь использовать его позже. (Это может означать, например, переписывание таблицы виртуальных функций для вставки произвольного кода.) <type_traits> имеет несколько шаблонов для проверки безопасности <type_traits> с представлением объекта. Вы можете скопировать один объект поверх другого того же типа с помощью memcpy( &dest, &src, sizeof(dest) ) если этот тип is_trivially_copyable. Вы можете сделать копию в правильно выровненную неинициализированную память, если она is_trivially_move_constructible. Вы можете проверить, идентичны ли два объекта одного и того же типа с memcmp( &a, &b, sizeof(a) ) и правильно ли хешировать объект, применяя хеш-функцию к байту в его представлении объекта, если тип has_unique_object_representations. Целочисленный тип не имеет ловушек и т.д. По большей части, тем не менее, если вы выполняете операции над объектными представлениями, где важен порядок байтов, вы говорите компилятору предположить, что вы знаете, что делаете, и ваш код не будет переносимым.

Как уже упоминали другие, двоичные литералы пишутся с самыми старшими цифрами, такими как десятичные, восьмеричные или шестнадцатеричные литералы. Это отличается от порядка байтов и не влияет на необходимость вызова ntohs() для номера порта из заголовка TCP, считанного из Интернета.

Ответ 7

в приложении Я скажу, что даже компилятор не заботится, например, на платформе LLVM только бэкэнд (технически не компилятор) позаботится о endianess.

Ответ 8

Возможно, вам стоит подумать о том, что C или С++ или любой другой язык, как внутренне маленький endian (подумайте о том, как работают побитовые операторы). Если базовый HW является большим эндизиатом, компилятор гарантирует, что данные хранятся в большой эндианте (то же самое для другой цели), однако ваши бит-мутные операции работают так, как если бы данные были незначительными. Важно помнить, что в отношении языка данные немногочисленны. Связанные с Endianness проблемы возникают, когда вы передаете данные из одного типа в другой. Пока вы не делаете этого, вы добры.

Меня спросили о том, что выражение "язык C/С++ является внутренне маленьким конечным", поэтому я предоставляю пример, который многие знают, как он работает, но здесь я иду.

typedef union
{
    struct {
        int a:1;
        int reserved:31;
    } bits;

    unsigned int value;
} u;

u test;
test.bits.a = 1;
test.bits.reserved = 0;

printf("After bits assignment, test.value = 0x%08X\n", test.value);

test.value = 0x00000001;

printf("After value assignment, test.value = 0x%08X\n", test.value);

Вывод в маленькой системе:

After bits assignment, test.value = 0x00000001
After value assignment, test.value = 0x00000001

Вывод в системе большого конца:

After bits assignment, test.value = 0x80000000
After value assignment, test.value = 0x00000001

Итак, , если вы не знаете, о чем идет речь о процессоре, где все выходит правильно? в маленькой системе! Таким образом, я говорю, что язык C/С++ по сути своей малозначен.