Чтение Hex из файла, printf %x показывает начальные значения ff

У меня есть файл data.dat с 26 шестнадцатеричными значениями:

22 49 E1 09 62 18 42 8C 66 10 B0 11 84 9C 00 FF E0 40 1F F8 60 07 FE 2C 03 FF

Я пытаюсь прочитать их на С++ и распечатать шестнадцатеричные значения на терминале с помощью:

const int msgbuflen = 26;                
char message [msgbuflen];  

const char* filenameIn = "data.dat";  

FILE* fpIn = fopen(filenameIn, "rb");

if (!fpIn) {
  perror("ERROR: INPUT FILE CANNOT BE OPENED\n");
  exit(EXIT_FAILURE);
}

for (i=0; i<msgbuflen; i++){  // clear the buffer
    message[i] = '0';
}

size_t ret_code = fread(message, sizeof(unsigned char), msgbuflen, fpIn);
for(int i = 0; i < msgbuflen; i++){
  printf("message[%d] : 0x%x\n", i, message[i]);
}

fclose (fpIn);

При запуске вывод для некоторых байтов имеет 3 ведущих значения ff:

message[0] : 0x22
message[1] : 0x49
message[2] : 0xffffffe1
message[3] : 0x9
message[4] : 0x62
message[5] : 0x18
message[6] : 0x42
message[7] : 0xffffff8c
message[8] : 0x66
message[9] : 0x10
message[10] : 0xffffffb0
message[11] : 0x11
message[12] : 0xffffff84
message[13] : 0xffffff9c
message[14] : 0x0
message[15] : 0xffffffff
message[16] : 0xffffffe0
message[17] : 0x40
message[18] : 0x1f
message[19] : 0xfffffff8
message[20] : 0x60
message[21] : 0x7
message[22] : 0xfffffffe
message[23] : 0x2c
message[24] : 0x3
message[25] : 0xffffffff

Почему появляются эти начальные f? например сообщение[2] : 0xffffffe1

Я попытался отформатировать вывод printf hex %x с помощью 0x%01x, но это не имеет значения для вывода терминала. Проверяя размер каждого элемента в массиве символов, они по-прежнему равны 1 байту, как и ожидалось:

printf("sizeof(message[2]) : %ld\n", sizeof(message[2]) );

%> sizeof(message[2]) : 1

Теперь мне интересно, если это проблема форматирования? Кажется, что в каждом элементе сообщения не более 1 байта (как и ожидалось).

Использование std::hex с cout приводит к той же проблеме.

Знаете ли вы, что когда вы вызываете древнюю библиотечную функцию C, такую ​​как printf, любое значение параметра char автоматически получает int. Что вы получаете за sizeof(int)?

Sam Varshavchik 30.01.2023 18:18

попробуйте прочитать ваш файл с помощью std::ifstream и прочитать в буфер 8-битных переменных без знака, например. Буфер std::array<std::uint8_t,26>.

Pepijn Kramer 30.01.2023 18:23

@SamVarshavchik Я этого не знал. sizeof(int) дает 4 байта. std::cout также делает это? Поскольку проблема остается при использовании std::cout << std::hex вместо printf.

David Scott 30.01.2023 18:26

@SamVarshavchik В этой «древней» библиотеке стоит С++. Более 80% бинарных пакетов на типичном сервере центра обработки данных написано на «древнем» C, а на C++ — в лучшем случае 1-2%. Вы смотрите на все стандартные библиотеки C++, и все они под капотом вызывают стандартную библиотеку C.

Henrique Bucher 30.01.2023 18:46
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
4
77
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Это потому, что char является целым числом со знаком на вашем компьютере, поэтому 0xFF становится -1. Поскольку %x печатает int, которое является 32-битным, оно расширяется по знаку до 0xFFFFFFFF, которое является 32-битным -1.

Если вы храните как unsigned char, у вас не будет этой проблемы:

#include <cstdio>

int main() {
    unsigned char message[] = {0x22, 0x49, 0xE1, 0x09, 0x62, 0x18, 0x42, 0x8C, 0x66, 0x10, 0xB0, 0x11, 0x84, 0x9C, 0x00, 0xFF, 0xE0, 0x40, 0x1F, 0xF8, 0x60, 0x07, 0xFE, 0x2C, 0x03, 0xFF }; 
    int msgbuflen = sizeof(message)/sizeof(message[0]);
    for(int i = 0; i < msgbuflen; i++){
        printf("message[%d] : 0x%x\n", i, message[i]);
    }
}

Производит:

Program returned: 0
message[0] : 0x22
message[1] : 0x49
message[2] : 0xe1
message[3] : 0x9
message[4] : 0x62
message[5] : 0x18
message[6] : 0x42
message[7] : 0x8c
message[8] : 0x66
message[9] : 0x10
message[10] : 0xb0
message[11] : 0x11
message[12] : 0x84
message[13] : 0x9c
message[14] : 0x0
message[15] : 0xff
message[16] : 0xe0
message[17] : 0x40
message[18] : 0x1f
message[19] : 0xf8
message[20] : 0x60
message[21] : 0x7
message[22] : 0xfe
message[23] : 0x2c
message[24] : 0x3
message[25] : 0xff

Годболт: https://godbolt.org/z/oxPqnYqMq

Кроме того, вы можете просто преобразовать каждый char в unsigned char:

for(int i = 0; i < msgbuflen; i++){
    printf("message[%d] : 0x%x\n", i, (unsigned char)message[i]);
}

Это было так очевидно задним числом, большое спасибо! Пометка как решенная.

David Scott 30.01.2023 18:32

@remylebeau не подписан ли символ согласно стандарту?

Henrique Bucher 31.01.2023 02:27

@NoleKsum: Это должно ответить на ваш вопрос: Является ли char подписанным или неподписанным по умолчанию? Я предполагаю, что вопрос C также относится к C++.

Andreas Wenzel 31.01.2023 02:31

@NoleKsum «не подписан char в соответствии со стандартом» - нет, он определяется реализацией независимо от того, подписан он или нет, в соответствии с разделом 3.9.1 [basic.fundamental] стандарта C++.

Remy Lebeau 31.01.2023 02:32

@RemyLebeau wtf, это для меня новость. Есть ли какая-либо основная платформа, которая не является древней, которая показывает char как неподписанный?

Henrique Bucher 31.01.2023 03:21

@NoleKsum: Согласно ссылке, которую я разместил, gcc на Android NDK имеет неподписанный char. Кроме того, я считаю, что некоторые другие платформы ARM используют неподписанные char.

Andreas Wenzel 31.01.2023 03:22

@NoleKsum Большинство компиляторов имеют возможность позволить пользователю решать, является ли char подписанным или беззнаковым. У MSVC есть /J, у GCC и Clang есть -funsigned-char/-fsigned-char и т. д.

Remy Lebeau 31.01.2023 06:28

Или в несколько более современном С++ (без использования массивов стиля "C") и четко определенном беззнаковом 8-битном типе данных. (char может быть как подписанным, так и неподписанным в зависимости от платформы, на которой вы работаете)

#include <array>
#include <iostream>
#include <sstream>
#include <cstdint>
#include <format>

// simulated opening of a std::ifstream
auto open_file()
{
    static std::array<std::uint8_t,26> data
    { 
        0x22, 0x49, 0xE1, 0x09, 0x62, 0x18, 0x42, 0x8C, 0x66, 0x10, 
        0xB0, 0x11, 0x84, 0x9C, 0x00, 0xFF, 0xE0, 0x40, 0x1F, 0xF8, 
        0x60, 0x07, 0xFE, 0x2C, 0x03, 0xFF
    };

    std::istringstream is(std::string{ data.begin(), data.end() });
    return is;
}

int main()
{
    std::array<std::uint8_t, 26> buffer;

    auto is = open_file();
    
    // get doesn't have an overload for std::uint8_t*
    // so check if sizeof char matches sizeof std::uint8_t
    static_assert(sizeof(char) == sizeof(std::uint8_t));
    is.get(reinterpret_cast<char*>(buffer.data()), buffer.size());

    for (const auto byte : buffer)
    {
        std::cout << std::format("0x{:x} ", byte);
    }

    return 0;
}

Насколько мне известно, ни в одном выпуске нет std::format. На данный момент только магистраль gcc/clang. Пройдут годы, прежде чем std::format станет доступным.

Henrique Bucher 30.01.2023 18:44

Я использую его постоянно: Visual Studio 2022. язык C++20 (en.cppreference.com/w/cpp/utility/format/format ) также упоминает C++20. Поэтому я не знал, что это еще не было доступно на clang/gcc. В противном случае все еще есть github.com/fmtlib. Но альтернатива: std::cout << "0x" << std::hex << static_cast<unsigned>(byte) << " ";

Pepijn Kramer 30.01.2023 18:49

Другие вопросы по теме