Просто пытаюсь подтвердить то, что кажется некоторым упущением в стандартах C и C++. Последние версии обоих поддерживают двоичные целочисленные литералы (
Код: Выделить всё
0bЦелочисленные литералы
Начиная с C++14 и C23, C и C++ поддерживают следующие общие форматы целочисленных литералов[1]:
Базовое число
Префикс
Пример
Стандартное
Десячное число (основание 10)
без префикса
Код: Выделить всё
123Восьмеричная (основание 8)
Код: Выделить всё
0Код: Выделить всё
0755Шестнадцатеричный (с основанием 16)
Код: Выделить всё
0xКод: Выделить всё
0x7FДвоичный (основание 2)
Код: Выделить всё
0bКод: Выделить всё
0b1010Поэтому в своем коде вы можете написать что-то вроде int x = 0xAB; или int y = 0b11;.
Разбор строк
Теперь предположим, что мы хотим преобразовать несколько строк в числа, например "123", "0755", "0x7F" и "0b1010". К счастью для нас, C и C++ предоставляют множество возможностей, таких как:
- Семейство strtol[2] поскольку (по крайней мере) C99 и C++98 (или C++11 для вариантов ll)
- Семейство std::stol[2] поскольку С++11
- по крайней мере C++17.
Код: Выделить всё
std::from_chars
strtol
Код: Выделить всё
long strtol( const char* restrict str, char** restrict str_end, int base );Интерпретирует целочисленное значение в байтовой строке, на которую указывает str.
Отбрасывает любые пробельные символы (определяемые вызовом isspace) до тех пор, пока не будет найден первый непробельный символ, затем берет как можно больше символов для формирования допустимого представления целочисленного числа по основанию n (где n = основание) и преобразует их в целочисленное значение. Допустимое целочисленное значение состоит из следующих частей:
- (необязательно) знак плюс или минус
- (необязательно) префикс (0), указывающий восьмеричную базу (применяется только тогда, когда основа равна 8 или 0)
- (необязательно) префикс (0x или 0X), указывающий шестнадцатеричную базу (применяется только тогда, когда база равна 16 или 0)
- последовательность цифр
Дополнительные числовые форматы могут быть приняты установленной в данный момент локалью C.
Если значение базы равно 0, числовая база определяется автоматически: если префикс равен 0, база является восьмеричной, если префикс 0x или 0X, база является шестнадцатеричной, в противном случае база – десятичное.
На справочной странице для std::stol указано, что он вызывает strtol внутри, поэтому его поведение фактически идентично.
Асимметрия преобразования
И все семейства функций strtol и std::stol были добавлены в стандарты C и C++ в C99 и C++14, соответственно, и до включения двоичных целочисленных литералов в C23 и C++17.
Кроме того, в C++ теперь есть std::format, начиная с C++20, а спецификация стандартного формата поддерживает двоичный формат b, например {:b. ~~Интересно, что printf не поддерживает сопоставимый тип формата %b. (В ссылке ничего не указано)~~ EDIT: Неважно, похоже, что по крайней мере начиная с C23 printf действительно поддерживает %b. См. §7.23.6.1 n3220. Не знаю, когда он был добавлен, поскольку я не проверял предыдущие стандарты C или C++.
Это означает, что вы можете столкнуться со следующей асимметрией:
Код: Выделить всё
#include
#include
#include
#include
int main() {
unsigned bin = 0b1010; // 10
unsigned oct = 0755; // 493
unsigned dec = 123; // 123
unsigned hex = 0x7F; // 127
auto text = std::format("0b{:b}, 0{:o}, {:d}, 0x{:x}", bin, oct, dec, hex);
puts(text.c_str());
char str[] = "0b1010 0755 123 0x7F";
char* token = strtok(str, " ");
while (token) {
unsigned val = strtoul(token, nullptr, 0);
printf("parsed string '%s' as integer '%d'\n", token, val);
token = strtok(nullptr, " ");
}
return 0;
}
Код: Выделить всё
0b1010, 0755, 123, 0x7f
parsed string '0b1010' as integer '0'
parsed string '0755' as integer '493'
parsed string '123' as integer '123'
parsed string '0x7F' as integer '127'
Примечания
[1] Технически это «целочисленные константы» в C и «целочисленные литералы» в C++, но для этого это не важно вопрос.
[2] Для краткости я рассматриваю strtol, strtoll, strtoul и strtoull как одно и то же. За исключением типа возвращаемого значения и различий со знаком/без знака, в остальном они идентичны. Аналогично для std::stoi, std::stol, std::stoll, std::stoul и std::stoull.
[3] https://godbolt.org/z/EETe1eYGz
Подробнее здесь: https://stackoverflow.com/questions/798 ... g-literals
Мобильная версия