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


8

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

uint8_t a[2];
uint16_t b;

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

b = (uint16_t)a;

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

Почему?

avr  c 

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-разрядные варианты) , чьи реализации целевые зависят от архитектуры , как ли они поменять местами байты или нет (при условии, что байты, передаваемые «по проводам», всегда имеют порядковый номер, если они являются частью многобайтовых слов).


Это отличный ответ. Ключ « 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-битные последовательности, но не так, как вы думали.


0

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

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

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.