Чтение байтов целочисленного размера из массива char *. - PullRequest
12 голосов
/ 13 февраля 2009

Я хочу прочитать sizeof(int) байтов из массива char*.

а) В каком сценарии нам нужно беспокоиться, если нужно проверить порядок байтов?

b) Как бы вы прочитали первые 4 байта, принимая во внимание порядок байтов или нет.

РЕДАКТИРОВАНИЕ: Прочитанные мною байты sizeof(int) необходимо сравнить с целочисленным значением.

Каков наилучший подход к решению этой проблемы

Ответы [ 9 ]

19 голосов
/ 13 февраля 2009

Вы имеете в виду что-то подобное?:

char* a;
int i;
memcpy(&i, a, sizeof(i));

Вам нужно беспокоиться о порядке байтов, только если источником данных является другая платформа, например устройство.

9 голосов
/ 13 февраля 2009

a) Вам нужно беспокоиться только об «порядке байтов» (т. Е. При обмене байтами), если данные были созданы на машине с прямым порядком байтов и обрабатываются на машине с прямым порядком байтов, или наоборот. Есть много способов, которыми это может произойти, но вот пара примеров.

  1. Вы получаете данные на компьютере Windows через сокет. В Windows используется архитектура с прямым порядком байтов, в то время как сетевые данные «должны» иметь формат с прямым порядком байтов.
  2. Вы обрабатываете файл данных, который был создан в системе с другим порядком байтов.

В любом из этих случаев вам нужно будет поменять местами все числа, которые больше 1 байта, например, шорты, целые, длинные, двойные и т. Д. Однако, если вы всегда имеете дело с данными из одного и того же платформа, порядковые номера не имеют значения.

b) Исходя из вашего вопроса, звучит так, будто у вас есть указатель на символ и вы хотите извлечь первые 4 байта в виде целого числа, а затем решить любые проблемы с порядком байтов. Чтобы сделать извлечение, используйте это:

int n = *(reinterpret_cast<int *>(myArray)); // where myArray is your data

Очевидно, это предполагает, что myArray не является нулевым указателем; в противном случае произойдет сбой, так как он разыменует указатель, поэтому используйте хорошую схему защитного программирования.

Чтобы поменять местами байты в Windows, вы можете использовать функции ntohs () / ntohl () и / или htons () / htonl (), определенные в winsock2.h. Или вы можете написать несколько простых процедур для этого в C ++, например:

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));
}
3 голосов
/ 13 февраля 2009

Как насчет

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;
}
3 голосов
/ 13 февраля 2009

Простой способ решить эту проблему - убедиться, что все, что генерирует байты, делает это в последовательном порядке байтов. Обычно «сетевой порядок байтов», используемый различными TCP / IP-компонентами, лучше всего: библиотечные процедуры 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
3 голосов
/ 13 февраля 2009

Зависит от того, как вы хотите их читать, у меня такое ощущение, что вы хотите преобразовать 4 байта в целое число, делая это по потоковым сетевым данным, как правило, что-то вроде этого:

int foo = *(int*)(stream+offset_in_stream);
1 голос
/ 13 февраля 2009

Зачем читать, когда можно просто сравнить?

bool AreEqual(int i, char *data)
{
   return memcmp(&i, data, sizeof(int)) == 0;
}

Если вы беспокоитесь о порядке байтов, когда вам нужно преобразовать все целые числа в некоторую инвариантную форму. htonl и ntohl являются хорошими примерами.

1 голос
/ 13 февраля 2009

Просто используйте цикл for, который перемещается по массиву в виде фрагментов sizeof (int).
Используйте функцию ntohl (находится в заголовке <arpa/inet.h>, по крайней мере, в Linux) для преобразования байтов в сетевом порядке (сетевой порядок определяется как big-endian) в локальный байтовый порядок. Эта функция библиотеки реализована для правильного преобразования сети в хост для любого процессора, на котором вы работаете.

1 голос
/ 13 февраля 2009

Вам следует беспокоиться о порядке байтов только в том случае, если данные, которые вы читаете, состоят из чисел, размер которых превышает один байт.
Если вы читаете байты sizeof (int) и ожидаете интерпретировать их как int, тогда endianess имеет значение. по существу endianness - это способ, которым машина интерпретирует последовательность из более чем 1 байта в числовое значение.

1 голос
/ 13 февраля 2009

Вам не нужно беспокоиться о порядке байтов, если вы не читаете байты из источника, созданного на другом компьютере, например, сетевой поток.

Учитывая это, вы не можете просто использовать цикл for?

void ReadBytes(char * stream) {
    for (int i = 0; i < sizeof(int); i++) {
        char foo = stream[i];
        }
    }
 }

Вы просите что-нибудь более сложное, чем это?

...