Преобразование указателя C - PullRequest
0 голосов
/ 18 сентября 2018

Предположим, у меня есть буфер байтов:

char buffer[] = {  0x11, 0x00, 0x00, 0x02, .... };

Что я хочу сделать, это взять qword из определенного смещения.Смещение будет рассчитано в байтах.Вот что я сделал:

unsigned long long *ptr = &buffer[16];
unsigned long long myqword = *ptr;

Мне кажется, это работает.Я пропускаю 16 байтов с начала и затем беру 8 байтов.

У меня вопрос, почему я получаю это предупреждение:

warning: initialization of ‘long long unsigned int *’ from incompatible pointer type ‘char *’ [-Wincompatible-pointer-types]
   unsigned long long *ptr = &buffer[16];

Ответы [ 3 ]

0 голосов
/ 18 сентября 2018

Вы бы сами соединили байты в целое число.

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

/** De-serialize a uint64_t from a byte array in big endian format.
 * @param r The byte array to containing the integer in. Must be at least of size 4.
 * @param return The deserialized integer from the byte array
 */
static inline uint64_t uc_unpack_64_be(const uint8_t *r)
{
    uint64_t v;

    v  = (uint64_t)r[0] << 56;
    v |= (uint64_t)r[1] << 48;
    v |= (uint64_t)r[2] << 40;
    v |= (uint64_t)r[3] << 32;
    v |= (uint32_t)r[4] << 24;
    v |= r[5] << 16;
    v |= r[6] << 8;
    v |= r[7];

    return v;
}

Если вы в данный момент находитесь на машине с прямым порядком байтов, используйте эту:

/** De-serialize a uint64_t from a byte array in little endian format.

 * @param r The byte array to containing the integer in. Must be at least of size 8.
 * @param return The deserialized integer from the byte array
 */
static inline uint64_t uc_unpack_64_le(const uint8_t *r)
{
    uint64_t v;

    v  = r[0];
    v |= r[1] << 8;
    v |= r[2] << 16;
    v |= (uint32_t)r[3] << 24;
    v |= (uint64_t)r[4] << 32;
    v |= (uint64_t)r[5] << 40;
    v |= (uint64_t)r[6] << 48;
    v |= (uint64_t)r[7] << 56;

    return v;
}

Используйте его, например. as uint64_t myqword = uc_unpack_64_le (& buffer [16]);

Обратите внимание, что то, используете ли вы одну из uint64_t uc_unpack_64_le или uint64_t uc_unpack_64_le функций, зависит от того, отформатировали ли вы данные в вашем buffer как с прямым или младшим порядком байтов, а не от того, работает ли ваш код на машине с прямым или младшим порядком байтов.

Если вы настаиваете на том, чтобы использовать текущие типы long и char, измените код соответствующим образом, но я рекомендую вместо этого использовать типы uint16_t и uint64_t из заголовка <stdint.h>.

0 голосов
/ 18 сентября 2018

Здесь 4 основных проблемы:

  • unsigned long long* несовместимо с char*.Это является причиной ошибки компилятора.
  • Чтение группы char через unsigned long long* нарушило бы правило строгого алиасинга , вызвав неопределенные ошибки поведения.
  • Чтение связки char через unsigned long long* также может привести к смещению доступа, вызывая неопределенные ошибки поведения.
  • Использование char для хранения целочисленных значений - очень плохая идея, так как char имеет реализацию-определена подпись и может быть подписана на некоторых компьютерах.Вместо этого используйте uint8_t.

Вместо этого вам следует использовать memcpy:

size_t n = sizeof(unsigned long long);
memcpy(&myqword, &buffer[i * n], n);

Если вам действительно нужен доступ к содержимому определенной области памятибез его копирования у вас нет другого выбора, кроме как создать какой-то новый тип, например, объединение:

#include <inttypes.h>
#include <stdio.h>

typedef union
{
  uint8_t  byte [8];
  uint64_t qword;
} qword_t;

int main (void)
{
  qword_t q = {.byte = {0x11, 0x00, ...} };
  printf("%"PRIu64, q.qword);
}

Но, пожалуйста, обратите внимание, что этот код зависит от поддержки ЦП и является непереносимым.

0 голосов
/ 18 сентября 2018

Помимо строгого нарушения правила псевдонимов (хорошо освещенного в комментариях к вопросам), еще одна причина, на которую стоит обратить внимание, это то, что вы можете в конечном итоге получить доступ к невыровненным данным.

На некоторых архитектурах этобудет медленнее, на других ваше приложение может не пережить его.

Если вы приведете его явно, оно закроется.Но я бы также сказал компилятору выровнять массив char по размеру unsigned long long (а также для ясности переключился бы на uint8_t и uint64_t.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...