C - преобразование массива без знака в массив без знака
У меня есть целое число без знака (2 байта), и я хочу преобразовать его в тип без знака. Из моего поиска я обнаружил, что большинство людей рекомендуют делать следующее:
unsigned int x;
...
unsigned char ch = (unsigned char)x;
Правильный ли подход? Я спрашиваю, потому что unsigned char равен 1 байту, и мы преобразуем данные из 2 байтов в 1 байт.
Чтобы предотвратить потерю данных, я хочу создать массив без знака char[] и сохранить отдельные байты в массив. Я застрял в следующем:
unsigned char ch[2];
unsigned int num = 272;
for(i=0; i<2; i++){
// how should the individual bytes from num be saved in ch[0] and ch[1] ??
}
Кроме того, как бы мы преобразовали unsigned char[2] обратно в unsigned int.
Большое спасибо.
7 ответов
Ты можешь использовать memcpy
в таком случае:
memcpy(ch, (char*)&num, 2); /* although sizeof(int) would be better */
Кроме того, как преобразовать unsigned char[2] обратно в unsigned int.
Точно так же, просто измените аргументы memcpy.
Как насчет:
ch[0] = num & 0xFF;
ch[1] = (num >> 8) & 0xFF;
Обратная операция оставлена в качестве упражнения.
Как насчет использования союза?
union {
unsigned int num;
unsigned char ch[2];
} theValue;
theValue.num = 272;
printf("The two bytes: %d and %d\n", theValue.ch[0], theValue.ch[1]);
Это действительно зависит от вашей цели: почему вы хотите преобразовать это в unsigned char
? В зависимости от ответа на это есть несколько разных способов сделать это:
Обрезать: это то, что было рекомендовано. Если вы просто пытаетесь сжать данные в функцию, которая требует
unsigned char
, просто снималиuchar ch = (uchar)x
(но, конечно, остерегайтесь того, что происходит, если ваш int слишком велик).Конкретный порядковый номер: используйте это, когда пункт назначения требует определенного формата. Обычно сетевому коду нравится все, что преобразовано в массивы с прямым порядком байтов:
int n = sizeof x; for(int y=0; n-->0; y++) ch[y] = (x>>(n*8))&0xff;
воля делает это.
Машина порядковая Используйте это, когда нет требования к порядку байтов, и данные будут появляться только на одном компьютере. Порядок массива будет меняться в зависимости от архитектуры. Люди обычно заботятся об этом с
union
s:union {int x; char ch[sizeof (int)];} u; u.x = 0xf00 //use u.ch
с
memcpy
:uchar ch[sizeof(int)]; memcpy(&ch, &x, sizeof x);
или с очень опасным простым приведением (что является неопределенным поведением и происходит сбой во многих системах):
char *ch = (unsigned char *)&x;
Конечно, массив символов, достаточно большой, чтобы содержать большее значение, должен быть точно таким же большим, как и само это значение. Таким образом, вы можете просто притвориться, что это большее значение уже является массивом символов:
unsigned int x = 12345678;//well, it should be just 1234.
unsigned char* pChars;
pChars = (unsigned char*) &x;
pChars[0];//one byte is here
pChars[1];//another byte here
(Как только вы поймете, что происходит, это можно сделать без каких-либо переменных, все просто приведение)
Вам просто нужно извлечь эти байты, используя bitwise & operator
, OxFF
шестнадцатеричная маска для извлечения одного байта Пожалуйста, посмотрите на различные битовые операции здесь - http://www.catonmat.net/blog/low-level-bit-hacks-you-absolutely-must-know/
Пример программы выглядит следующим образом:
#include <stdio.h>
int main()
{
unsigned int i = 0x1122;
unsigned char c[2];
c[0] = i & 0xFF;
c[1] = (i>>8) & 0xFF;
printf("c[0] = %x \n", c[0]);
printf("c[1] = %x \n", c[1]);
printf("i = %x \n", i);
return 0;
}
Выход:
$ gcc 1.c
$ ./a.out
c[0] = 22
c[1] = 11
i = 1122
$
Поддерживая предложение @abelenky, использование an было бы более надежным способом сделать это.
union unsigned_number {
unsigned int value; // An int is 4 bytes long
unsigned char index[4]; // A char is 1 byte long
};
Особенностью этого типа является то, что компилятор будет выделять память только для самого большого члена нашей структуры данных.
unsigned_number
, что в данном случае будет 4 байта , так как оба члена (значение и индекс) имеют одинаковый размер. Если бы вместо этого вы определили его как a, у нас было бы 8 байтов , выделенных в памяти, поскольку компилятор выполняет выделение для всех членов a.
struct
.
Кроме того, и здесь ваша проблема решена, члены
union
все структуры данных используют одну и ту же ячейку памяти , что означает, что все они ссылаются на одни и те же данные — подумайте об этом как о жесткой ссылке в системах GNU/Linux.
Итак, у нас было бы:
union unsigned_number my_number;
// Assigning decimal value 202050300 to my_number
// which is represented as 0xC0B0AFC in hex format
my_number.value = 0xC0B0AFC; // Representation: Binary - Decimal
// Byte 3: 00001100 - 12
// Byte 2: 00001011 - 11
// Byte 1: 00001010 - 10
// Byte 0: 11111100 - 252
// Printing out my_number one byte at time
for (int i = 0; i < (sizeof(my_number.value)); i++)
{
printf("index[%d]: %u, 0x%x\n", \
i, my_number.index[i], my_number.index[i]);
}
// Printing out my_number as an unsigned integer
printf("my_number.value: %u, 0x%x", my_number.value, my_number.value);
И выход будет:
index[0]: 252, 0xfc
index[1]: 10, 0xa
index[2]: 11, 0xb
index[3]: 12, 0xc
my_number.value: 202050300, 0xc0b0afc
И что касается вашего последнего вопроса, нам не придется преобразовывать unsigned char обратно в unsigned int , поскольку значения уже есть. Вам просто нужно выбрать, каким способом вы хотите получить к нему доступ
Примечание 1 : я использую целое число из 4 байтов, чтобы облегчить понимание концепции. Для представленной вами проблемы вы должны использовать:
union unsigned_number {
unsigned short int value; // A short int is 2 bytes long
unsigned char index[2]; // A char is 1 byte long
};
Примечание 2 : я назначил
byte 0
к
252
чтобы указать на беззнаковую характеристику нашего
index
поле. Был ли он объявлен как
signed char
, мы бы хотели иметь
index[0]: -4, 0xfc
как вывод.