Cosa c'è di sbagliato in questo casting in codice C per AVR?


8

Ho definito due variabili:

uint8_t a[2];
uint16_t b;

Successivamente voglio usare acome variabile di tipo uint16_t, ad es

b = (uint16_t)a;

Ma questo è sbagliato! I miei programmi non funzionano correttamente con tale codice. Tutto è OK quando sostituisco bad uint8_t b[2]e le operazioni di uso elementwise.

Perché?

avr  c 

5
Perché non getti alcuni valori nel tuo esempio e ci dici qual è la tua aspettativa di "corretto" in modo che possiamo effettivamente aiutare senza speculare sul tuo intento semantico.
vicatcu,

1
Questo sarebbe molto più adatto per Stack Overflow.
sharptooth,

Risposte:


16

aè un puntatore a una matrice di byte. Se lo si lancia in uint16_t e lo si assegna b, bconterrà l'indirizzo della base dell'array (dove è memorizzato) in SRAM. Se si desidera trattare i due byte dell'array acome un numero intero, utilizzare un'unione come suggerito dall'utente 14284, ma tenere presente che l'unione rappresenterà l'array di byte nell'ordinamento dei byte di memoria dell'architettura (in AVR che sarebbe piccolo -endian, che significa che il byte 0 è il byte meno significativo). Il modo per scriverlo nel codice è:

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

Un altro modo per farlo senza usare un'unione è quello di lanciare aun puntatore uint16_t e quindi dereferirlo in questo modo:

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

Se si utilizza il buffer per archiviare dati big endian (ad es. Ordine di byte di rete), sarà necessario scambiare byte per utilizzare una di queste tecniche. Un modo per farlo senza rami o variabili temporanee è:

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

Per inciso, questo non è un AVR o addirittura un problema solo incorporato. Livello di applicazione codice di rete scritto per PC tipicamente chiamate funzioni chiamate htonl, htons(host rete, 32 e 16 bit varianti) e ntohl, ntohs(rete all'host, 32 e 16 bit varianti) le cui implementazioni sono bersaglio architettura dipendente se essi scambiare i byte o meno (supponendo che i byte trasmessi "sul filo" siano sempre big-endian quando fanno parte di parole multi-byte).


Questa è un'ottima risposta La chiave essendo ' a' da sola è un puntatore.
Jon L

2
"Un altro modo per farlo senza usare un sindacato è quello di lanciare un puntatore su uint16_t e poi dereferirlo" - In realtà, questo tipo di casting spesso infrange rigide regole di aliasing. Non dovresti farlo a meno che non ti stia compilando -fno-strict-aliasing.
Jim Paris,

3

Se si intende concatenare le due variabili a 8 bit in una variabile a 16 bit, utilizzare a union. Se si desidera eseguire il cast di un singolo membro ain b, quindi specificare quale elemento dell'array si desidera utilizzare.


2

Sul tuo codice stai lanciando solo il puntatore alla matrice.

Devi lanciare il valore indicato da a.

b = (uint16_t)*a;

Non ho mai usato AVR ma se stai lavorando con un'architettura a 16 bit devi assicurarti che a sia allineato a parole. In caso contrario, potrebbe verificarsi un'eccezione.


1
... questo non è assolutamente il suo intento ... vuole che b sia correlato ad entrambi gli elementi di un (questo ignorerebbe totalmente un [1]), inoltre non ci sono eccezioni o restrizioni su ciò che puoi lanciare in avr-gcc
Vicatcu,

0

Ogni membro di a è un numero di 8 bit. Non può contenere nulla di più grande. Casting a 16 bit non fa nulla per una . Estrae semplicemente il valore che a potrebbe essere in grado di contenere e lo converte in 16 bit in modo che corrisponda al formato di b quando il valore è memorizzato lì.

Non hai nemmeno fatto riferimento a un membro di a . Devi usare uno [0] o un [1] (e non un [2]!). Se usi a da solo, ne ottieni l'indirizzo. (Buona cattura, Bruno).

Dichiara una di essere un array di due numeri a 8 bit non lo rende un numero a 16 bit, sia. Puoi fare alcune cose a livello di codice per memorizzare e recuperare valori a 16 bit usando sequenze di 8 bit, ma non nel modo in cui stavi pensando.


0

Se si desidera convertire i byte ain un valore a 16 bit e la rappresentazione è little-endian (gli 8 bit inferiori del valore arrivano nel primo byte), fare

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

Per una rappresentazione big-endian fare

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

Evita di usare cast di puntatori o sindacati per fare ciò, poiché ciò porta a problemi di portabilità.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.