Что не так с этим приведением в C-код для AVR?

8

Я определил две переменные:

uint8_t a[2];
uint16_t b;

Далее я хочу использовать в aкачестве переменной типа uint16_t, например

b = (uint16_t)a;

Но это неправильно! Мои программы не работают правильно с таким кодом. Все в порядке , когда я заменить bна uint8_t b[2]и операции использования поэлементные.

Почему?


источник
5
Почему бы вам не добавить некоторые значения в ваш пример и сказать нам, что вы ожидаете от «правильного», чтобы мы могли реально помочь, не размышляя о вашем семантическом намерении.
Викатку
1
Это было бы намного лучше для переполнения стека.
Sharp

Ответы:

16

aуказатель на массив байтов Если вы приведете его к uint16_t и назначите его b, тогда он bбудет содержать адрес базы массива (где он хранится) в SRAM. Если вы хотите рассматривать два байта массива aкак целое число, используйте объединение, как предложено пользователем 14284, но имейте в виду, что объединение будет представлять байтовый массив в порядке байтов памяти в архитектуре (в AVR это будет мало -endian, что означает, что байт 0 является наименее значимым байтом). Способ написать это в коде:

union{
  uint8_t a[2];
  uint16_t b;
} x;

x.b[0] = 0x35;
x.b[1] = 0x4A;

// by virtue of the above two assignments
x.a == 0x4A35 // is true

Другой способ сделать это без использования объединения - привести aк указателю uint16_t, а затем разыменовать его следующим образом:

uint8_t a[2] = {0x35, 0x4A};
uint16_t b = *((uint16_t *) a);
b == 0x4A35; // because AVR is little endian

Если вы используете буфер для хранения данных с прямым порядком байтов (например, порядок байтов в сети), то вам нужно будет поменять местами, чтобы использовать любой из этих методов. Способ сделать это без каких-либо ветвей или временных переменных:

uint8_t a[2] = {0x35, 0x4A};
a[0] ^= a[1];
a[1] ^= a[0];
a[0] ^= a[1];

a[0] == 0x4A; // true
a[1] == 0x35; // true

Между прочим, это не AVR или даже встроенная проблема. Прикладной уровень сетевого код написан для ПК , как правило , вызовов функций , называемых htonl, htons(хоста к сети, 32- и 16-разрядные варианты) и ntohl, ntohs(сеть к хосту, 32- и 16-разрядные варианты) , чьи реализации целевые зависят от архитектуры , как ли они поменять местами байты или нет (при условии, что байты, передаваемые «по проводам», всегда имеют порядковый номер, если они являются частью многобайтовых слов).

vicatcu
источник
Это отличный ответ. Ключ « a» сам по себе является указателем.
Джон Л
2
«Другой способ сделать это без использования объединения состоит в приведении к указателю uint16_t и последующем разыменовании его» - на самом деле, этот тип преобразования часто нарушает строгие правила наложения имен. Вы не должны делать это, если вы не компилируете с -fno-strict-aliasing.
Джим Пэрис
3

Если вы хотите объединить две 8-битные переменные в 16-битную переменную, используйте a union. Если вы хотите привести единственный член aв b, укажите, какой элемент массива вы хотите использовать.

Джо Хасс
источник
2

На ваш код вы приводите только указатель на массив.

Вам нужно привести значение, на которое указывает a.

b = (uint16_t)*a;

Я никогда не использовал AVR, но если вы работаете с 16-битной архитектурой, вы должны убедиться, что слово выровнено. Невыполнение этого требования может привести к исключению.

Бруно Феррейра
источник
1
... это абсолютно не его намерение ... он хочет, чтобы b был связан с обоими элементами a (это полностью исключило бы a [1]), также нет никаких исключений или ограничений на то, что вы можете разыграть в avr-gcc
Викатку
0

Каждый член представляет собой 8-битное число. Он не может вместить ничего большего. Приведение его к 16-битам ничего не делает для a . Он просто извлекает любое значение, которое может содержать a , и преобразует его в 16 бит, чтобы оно соответствовало формату b, когда значение сохраняется там.

Вы даже не относятся к члену . Вы должны использовать [0] или [1] (а не [2]!). Если вы используете сам по себе, вы только получите адрес этого. (Хороший улов, Бруно).

Объявление быть массивом из двух 8-битных чисел не делает его 16-битовое число, либо. Вы можете делать некоторые вещи программно для хранения и извлечения 16-битных значений, используя 8-битные последовательности, но не так, как вы думали.

gbarry
источник
0

Если вы хотите преобразовать байты в a16-битное значение, а представление имеет младший порядок (младшие 8 битов значения входят в первый байт), выполните

uint16_t b = a[0] | (a[1] << 8);

Для представления с прямым порядком байтов

uint16_t b = (a[0] << 8) | a[1];

Избегайте использования приведения или объединения указателей для этого, так как это приводит к проблемам переносимости.

starblue
источник