Ответ 1
Вы имеете в виду что-то вроде этого:
char* a;
int i;
memcpy(&i, a, sizeof(i));
Вам нужно только беспокоиться о endianess, если источник данных находится на другой платформе, например, на устройстве.
Я хочу читать sizeof(int)
байты из массива char*
.
a) В каком сценарии нам нужно беспокоиться, нужно ли проверять проверку подлинности?
b) Как бы вы читали первые 4 байта, принимая во внимание или не соглашаться.
EDIT: Пронумерованные байты sizeof(int)
должны сравниваться с целым значением.
Каков наилучший подход для решения этой проблемы?
Вы имеете в виду что-то вроде этого:
char* a;
int i;
memcpy(&i, a, sizeof(i));
Вам нужно только беспокоиться о endianess, если источник данных находится на другой платформе, например, на устройстве.
a) Вам нужно только беспокоиться о "endianness" (т.е. обменивать байты), если данные были созданы на машине большого конца и обрабатываются на машине little-endian, или наоборот. Это может быть много, но вот несколько примеров.
В любом из этих случаев вам необходимо поменять байты на все числа, размер которых превышает 1 байт, например, шорты, ints, longs, double и т.д. Однако, если вы всегда имеете дело с данными из того же платформа, конечные вопросы не имеют никакого отношения.
b) Исходя из вашего вопроса, похоже, что у вас есть указатель char и вы хотите извлечь первые 4 байта в качестве int, а затем решать любые проблемы с Endian. Чтобы выполнить извлечение, используйте это:
int n = *(reinterpret_cast<int *>(myArray)); // where myArray is your data
Очевидно, это предполагает, что myArray не является нулевым указателем; в противном случае это приведет к сбою, поскольку он разыгрывает указатель, поэтому используйте хорошую схему программирования защиты.
Чтобы поменять байты на Windows, вы можете использовать функции ntohs()/ntohl() и/или htons()/htonl(), определенные в winsock2.h. Или вы можете написать несколько простых процедур для этого в С++, например:
inline unsigned short swap_16bit(unsigned short us)
{
return (unsigned short)(((us & 0xFF00) >> 8) |
((us & 0x00FF) << 8));
}
inline unsigned long swap_32bit(unsigned long ul)
{
return (unsigned long)(((ul & 0xFF000000) >> 24) |
((ul & 0x00FF0000) >> 8) |
((ul & 0x0000FF00) << 8) |
((ul & 0x000000FF) << 24));
}
В зависимости от того, как вы хотите их прочитать, я получаю ощущение, что вы хотите выделить 4 байта в целое число, делая это через сетевые потоковые данные, как правило, заканчивается примерно следующим образом:
int foo = *(int*)(stream+offset_in_stream);
Легкий способ решить это - убедиться, что все, что генерирует байты, делает это в согласованной конкретизации. Как правило, "порядок сетевого байта", используемый различными материалами TCP/IP, best: библиотечные процедуры htonl и ntohl работают очень хорошо с этим, и они обычно довольно хорошо оптимизированы.
Однако, если сетевой байтовый порядок не используется, вам, возможно, придется другими способами. Вам нужно знать две вещи: размер целого и порядок байтов. Как только вы это знаете, вы знаете, сколько байтов извлечь и в каком порядке поставить их вместе в int.
Некоторый пример кода, который предполагает sizeof (int), - это правильное количество байтов:
#include <limits.h>
int bytes_to_int_big_endian(const char *bytes)
{
int i;
int result;
result = 0;
for (i = 0; i < sizeof(int); ++i)
result = (result << CHAR_BIT) + bytes[i];
return result;
}
int bytes_to_int_little_endian(const char *bytes)
{
int i;
int result;
result = 0;
for (i = 0; i < sizeof(int); ++i)
result += bytes[i] << (i * CHAR_BIT);
return result;
}
#ifdef TEST
#include <stdio.h>
int main(void)
{
const int correct = 0x01020304;
const char little[] = "\x04\x03\x02\x01";
const char big[] = "\x01\x02\x03\x04";
printf("correct: %0x\n", correct);
printf("from big-endian: %0x\n", bytes_to_int_big_endian(big));
printf("from-little-endian: %0x\n", bytes_to_int_little_endian(little));
return 0;
}
#endif
Как насчет
int int_from_bytes(const char * bytes, _Bool reverse)
{
if(!reverse)
return *(int *)(void *)bytes;
char tmp[sizeof(int)];
for(size_t i = sizeof(tmp); i--; ++bytes)
tmp[i] = *bytes;
return *(int *)(void *)tmp;
}
Вы бы использовали его следующим образом:
int i = int_from_bytes(bytes, SYSTEM_ENDIANNESS != ARRAY_ENDIANNESS);
Если вы находитесь в системе, где отбрасывание void *
до int *
может привести к конфликтам выравнивания, вы можете использовать
int int_from_bytes(const char * bytes, _Bool reverse)
{
int tmp;
if(reverse)
{
for(size_t i = sizeof(tmp); i--; ++bytes)
((char *)&tmp)[i] = *bytes;
}
else memcpy(&tmp, bytes, sizeof(tmp));
return tmp;
}
Вам не нужно беспокоиться о endianess, если вы не читаете байты из источника, созданного на другом компьютере, например. сетевой поток.
Учитывая, что вы не можете использовать цикл for?
void ReadBytes(char * stream) {
for (int i = 0; i < sizeof(int); i++) {
char foo = stream[i];
}
}
}
Вы просите что-то более сложное?
Вам нужно беспокоиться о endianess, только если данные, которые вы читаете, состоят из чисел, размер которых превышает один байт. если вы читаете байты sizeof (int) и ожидаете интерпретировать их как int, то endianess имеет значение. по существу endianness - это способ, которым машина интерпретирует серию из более чем 1 байта в числовое значение.
Просто используйте цикл for, который перемещается по массиву в sizeof (int) chunks.
Используйте функцию ntohl
(найденную в заголовке <arpa/inet.h>
, по крайней мере, в Linux) для преобразования из байтов в сетевом порядке (порядок сети определяется как big-endian) в локальный порядок байтов. Эта функция библиотеки реализована для правильного преобразования между сетями для любого процессора, на котором вы работаете.
Зачем читать, когда вы можете просто сравнить?
bool AreEqual(int i, char *data)
{
return memcmp(&i, data, sizeof(int)) == 0;
}
Если вы беспокоитесь о endianness, когда вам нужно преобразовать все целые числа в некоторую инвариантную форму. htonl и ntohl - хорошие примеры.