Я пытаюсь выдавить 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, некоторые имеют обратный порядок байтов. Это означает, что при использовании такого unionb1 может быть младшим байтом или старшим байтом. Более портативно маскировать и сдвигать (например, 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.
Прежде чем ожидать слишком многого от своей программы, включите дополнительные параметры предупреждений при компиляции и исправьте их все. пример