Я пытаюсь выдавить 4 значения символов из одного целого числа. Во всех моих попытках я получаю одинаковые результаты. Младшие байты старшего и младшего слова читаются правильно, но старшие байты расширяются до целых, а добавленные байты заполняются единицами. Где я делаю ошибку?
Вот пример кода моих попыток. Я компилировал его с помощью GCC:
#include <stdio.h>
#define PRINT printf("b1 %u\n", bb1);\
printf("b2 %u\n", bb2);\
printf("b3 %u\n", bb3);\
printf("b4 %u\n", bb4)
typedef union{
int integer;
struct{
char b1;
char b2;
char b3;
char b4;
};
}bytes;
typedef struct{
char b1:8;
char b2:8;
char b3:8;
char b4:8;
}bitfield;
int main()
{
int a;
printf("int size = %u\n", a=sizeof(int));
printf("char size = %u\n", a=sizeof(char));
bytes in_union;
bitfield in_struct;
// values stored in bytes
// hword.hi 240 hword.lo 15
// lword.hi 129 lword.lo 126
int value = 4027548030;
// #1 solution with union
in_union.integer = value;
char bb1 = in_union.b1;
char bb2 = in_union.b2;
char bb3 = in_union.b3;
char bb4 = in_union.b4;
PRINT;
// #2 with pointer to union
char* uptr;
uptr = (char*)&in_union;
bb1 = *uptr;
bb2 = *(uptr+1);
bb3 = *(uptr+2);
bb4 = *(uptr+3);
PRINT;
// #3 with void pointer to union
void* vptr;
vptr = &in_union;
bb1 = *(char*)vptr;
bb2 = *(char*)(vptr+1);
bb3 = *(char*)(vptr+2);
bb4 = *(char*)(vptr+3);
PRINT;
// #4 with pointer to value variable
char* ptr = (char*)&value;
bb1 = *ptr;
bb2 = *(ptr+1);
bb3 = *(ptr+2);
bb4 = *(ptr+3);
PRINT;
// #5 with bitfield
in_struct.b1 = (char)value;
in_struct.b2 = (char)(value>>8);
in_struct.b3 = (char)(value>>16);
in_struct.b4 = (char)(value>>24);
bb1 = in_struct.b1;
bb2 = in_struct.b2;
bb3 = in_struct.b3;
bb4 = in_struct.b4;
PRINT;
return 0;
}
Все дает одинаковые результаты:
bb1 = 126
bb2 = 4294967169
bb3 = 15
bb4 = 4294967280
Ожидаемые значения:
bb1 = 126
bb2 = 127
bb3 = 15
bb4 = 240
Вам нужно использовать unsigned char
.
Кроме того: это // #3 with void pointer to union
не будет компилироваться в MSVC: ошибка C2036: 'void *': неизвестный размер показывает, что вы не можете использовать арифметику указателей с указателем void*
без нестандартного компилятора.
Хотя большинство процессоров, которые вы будете использовать, имеют прямой порядок байтов в стиле Intel, некоторые имеют обратный порядок байтов. Это означает, что при использовании такого union
b1
может быть младшим байтом или старшим байтом. Более портативно маскировать и сдвигать (например, i >> 8 & 0xff
, чтобы получить второй младший байт).
int value = 4027548030;
изначально не имеет никакого смысла. Используйте тип, соответствующий присвоенному ему значению. Это приводит к принудительному преобразованию, определяемому реализацией, из некоторого большого знакового типа (long/long long) в int. Программа не гарантированно справится с этим корректно, но может выдать исключение сигнала.
Две проблемы в вашем коде:
Первый:
Вы используете тип char
для представления 4
байтов int
в объявлении объединения bytes
. Каждый бит 8
может представлять значение, которое может превышать максимальное значение, которое тип char
может хранить в вашей системе.
Из стандарта C № 5.2.4.2.1p2 [выделено автором]
2 Если значение объекта типа char при использовании в выражении рассматривается как целое число со знаком, значение CHAR_MIN должно быть таким же, как и значение SCHAR_MIN, а значение CHAR_MAX должно быть таким же, как и значение SCHAR_MAX. В противном случае значение CHAR_MIN должно быть равно 0, а значение CHAR_MAX должно быть таким же, как и значение UCHAR_MAX.20) Значение UCHAR_MAX должно быть равно 2CHAR_BIT - 1.
Вместо типа unsigned char
лучше использовать char
.
Второй:
В макросе PRINT
передается неверный спецификатор формата %u
в print()
для печати char
type.
В вашем коде происходит то, что bb1
, bb2
, bb3
и bb4
повышаются до int
, а передаваемый вами спецификатор формата — %u
. Это неопределенное поведение.
Из стандарта C № 7.21.6.1p9 [выделено автором]
9 Если спецификация преобразования недействительна, поведение не определено. 282) Если какой-либо аргумент не является правильным типом для соответствующей спецификации преобразования, поведение не определено.
Измените тип bb1
, bb2
, bb3
и bb4
на unsigned char
и используйте спецификатор формата %hhu
для их печати.
С этими изменениями для ввода int value = 4027548030;
получим следующий результат:
b1: 126
b2: 129
b3: 15
b4: 240
Обратите внимание, что в вашем коде есть еще несколько проблем, но я указал на основные.
Ваше предполагаемое улучшение printf("int size = %u\n", a=sizeof(int));
плохое. Вместо того, чтобы рекомендовать %d
, следует printf("int size = %zu\n", sizeof(int));
@WeatherVane Я предлагаю макро PRINT
. В коде ОП есть множество проблем, я только что указал на основные проблемы.
Ну, я не понимаю, почему ваше решение выводит отрицательные числа. В сообщении Staging Ground ОП заявил, что unsigned char
решил проблему, о которой вы не упомянули.
@WeatherVane Обратите внимание, что в моем примере вывода я использовал значение 2027548030
.
Я видел это изменение. Ценность ОП 4027548030
была бы лучше. Их ожидаемый результат имеет неотрицательные значения, а не значения байтов со знаком, которые вы показываете,
@WeatherVane Сформулировал и внес исправления в пост.
UV для исправления ошибочного bb2
ожидаемого значения OP.
Прежде чем ожидать слишком многого от своей программы, включите дополнительные параметры предупреждений при компиляции и исправьте их все. пример