У меня есть файл 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 приводит к той же проблеме.
попробуйте прочитать ваш файл с помощью std::ifstream и прочитать в буфер 8-битных переменных без знака, например. Буфер std::array<std::uint8_t,26>.
@SamVarshavchik Я этого не знал. sizeof(int)
дает 4 байта. std::cout также делает это? Поскольку проблема остается при использовании std::cout << std::hex
вместо printf
.
@SamVarshavchik В этой «древней» библиотеке стоит С++. Более 80% бинарных пакетов на типичном сервере центра обработки данных написано на «древнем» C, а на C++ — в лучшем случае 1-2%. Вы смотрите на все стандартные библиотеки C++, и все они под капотом вызывают стандартную библиотеку C.
Это потому, что 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]);
}
Это было так очевидно задним числом, большое спасибо! Пометка как решенная.
@remylebeau не подписан ли символ согласно стандарту?
@NoleKsum: Это должно ответить на ваш вопрос: Является ли char подписанным или неподписанным по умолчанию? Я предполагаю, что вопрос C также относится к C++.
@NoleKsum «не подписан char в соответствии со стандартом» - нет, он определяется реализацией независимо от того, подписан он или нет, в соответствии с разделом 3.9.1 [basic.fundamental] стандарта C++.
@RemyLebeau wtf, это для меня новость. Есть ли какая-либо основная платформа, которая не является древней, которая показывает char как неподписанный?
@NoleKsum: Согласно ссылке, которую я разместил, gcc на Android NDK имеет неподписанный char
. Кроме того, я считаю, что некоторые другие платформы ARM используют неподписанные char
.
@NoleKsum Большинство компиляторов имеют возможность позволить пользователю решать, является ли char
подписанным или беззнаковым. У MSVC есть /J
, у GCC и Clang есть -funsigned-char
/-fsigned-char
и т. д.
Или в несколько более современном С++ (без использования массивов стиля "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 станет доступным.
Я использую его постоянно: 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) << " ";
Знаете ли вы, что когда вы вызываете древнюю библиотечную функцию C, такую как
printf
, любое значение параметраchar
автоматически получаетint
. Что вы получаете заsizeof(int)
?