Apa yang salah dengan casting ini dalam kode C untuk AVR?

8

Saya mendefinisikan dua variabel:

uint8_t a[2];
uint16_t b;

Selanjutnya saya ingin menggunakan asebagai variabel tipe uint16_t, mis

b = (uint16_t)a;

Tapi ini salah! Program saya tidak berfungsi dengan benar dengan kode tersebut. Semua adalah OK ketika saya mengganti bke uint8_t b[2]dan operasi penggunaan elementwise.

Mengapa?


sumber
5
Mengapa Anda tidak memasukkan beberapa nilai ke dalam contoh Anda dan memberi tahu kami apa yang Anda harapkan dari "benar" sehingga kami dapat benar-benar membantu tanpa berspekulasi tentang maksud semantik Anda.
vicatcu
1
Ini akan menjadi jauh lebih cocok untuk Stack Overflow.
sharptooth

Jawaban:

16

aadalah pointer ke array byte. Jika Anda melemparkannya ke uint16_t dan menetapkannya b, maka bakan berisi alamat basis array (tempat disimpannya) di SRAM. Jika Anda ingin memperlakukan dua byte array asebagai integer, maka gunakan union seperti yang disarankan oleh user14284, tetapi perlu diketahui bahwa union akan mewakili array byte dalam pemesanan byte memori arsitektur (dalam AVR yang akan sedikit -endian, yang berarti byte 0 adalah byte paling signifikan). Cara menulis itu dalam kode adalah:

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

Cara lain untuk melakukan ini tanpa menggunakan gabungan adalah dengan melemparkan ake pointer uint16_t dan kemudian dereferensi seperti itu:

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

Jika Anda menggunakan buffer untuk menyimpan data big endian (mis. Urutan byte jaringan), maka Anda perlu byte-swap untuk menggunakan salah satu dari teknik ini. Cara untuk melakukannya tanpa cabang atau variabel sementara adalah:

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

Kebetulan ini bukan AVR atau bahkan masalah yang hanya tertanam. Kode jaringan level aplikasi yang ditulis untuk PC biasanya memanggil fungsi-fungsi yang disebut htonl, htons(host to network, varian 32-dan 16-bit) dan ntohl, ntohs(network to host, varian 32-dan 16-bit) yang implementasinya bergantung pada arsitektur target, apakah mereka menukar byte atau tidak (dengan asumsi bahwa byte yang dikirimkan 'on the wire' selalu big-endian ketika mereka adalah bagian dari kata-kata multi-byte).

vicatcu
sumber
Ini jawaban yang bagus. Kuncinya adalah ' a' sendiri adalah sebuah pointer.
Jon L
2
"Cara lain untuk melakukan ini tanpa menggunakan penyatuan adalah dengan melemparkan a ke pointer uint16_t dan kemudian menggantinya" - Sebenarnya, jenis casting ini sering melanggar aturan aliasing yang ketat. Anda seharusnya tidak melakukannya kecuali Anda mengompilasinya -fno-strict-aliasing.
Jim Paris
3

Jika maksud Anda adalah menggabungkan dua variabel 8-bit menjadi variabel 16-bit, gunakan a union. Jika Anda ingin memasukkan satu anggota ake dalam b, maka tentukan elemen larik mana yang ingin Anda gunakan.

Joe Hass
sumber
2

Pada kode Anda, Anda hanya melemparkan pointer ke array.

Anda harus memberikan nilai yang ditunjukkan a.

b = (uint16_t)*a;

Saya tidak pernah menggunakan AVR tetapi jika Anda bekerja dengan arsitektur 16 bit Anda harus memastikan bahwa kata tersebut selaras. Gagal melakukan ini dapat menghasilkan pengecualian.

Bruno Ferreira
sumber
1
... ini sama sekali bukan maksudnya ... ia ingin b dikaitkan dengan kedua elemen a (ini akan sepenuhnya mengabaikan [1]), juga tidak ada pengecualian atau batasan pada apa yang dapat Anda gunakan dalam avr-gcc
vicatcu
0

Setiap anggota yang merupakan nomor 8-bit. Itu tidak bisa menampung sesuatu yang lebih besar. Casting ke 16-bit tidak melakukan apa pun untuk a . Itu hanya mengekstraksi nilai apa pun yang mungkin bisa dimiliki, dan mengubahnya menjadi 16 bit sehingga cocok dengan format b ketika nilai disimpan di sana.

Anda bahkan tidak merujuk ke anggota a . Anda harus menggunakan [0] atau [1] (dan bukan [2]!). Jika Anda menggunakan sebuah dengan sendirinya, Anda hanya mendapatkan alamat itu. (Tangkapan bagus, Bruno).

Mendeklarasikan sebuah menjadi array dua angka 8-bit tidak membuat sejumlah 16-bit, baik. Anda dapat melakukan beberapa hal secara terprogram untuk menyimpan dan mengambil nilai 16-bit menggunakan urutan 8-bit, tetapi tidak seperti yang Anda pikirkan.

Gbarry
sumber
0

Jika Anda ingin mengubah byte amenjadi nilai 16 bit, dan representasi tersebut adalah little-endian (8 bit nilai yang lebih rendah berasal dari byte pertama), lakukan

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

Untuk representasi big-endian lakukan

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

Hindari menggunakan cast pointer atau serikat pekerja untuk melakukan ini, karena itu mengarah pada masalah portabilitas.

starblue
sumber