Newsgroup: it.comp.lang.c
Titolo: unire due variabili "unsigned 8 bit" in una "unsigned 16 bit"

ciao,
ho il numero 600 salvato in pippo16bit.
lo divido in pippo8bithi e pippo8bitlo cosi'
pippo8bi5hi = pippone >> 8;
pippo8bitlo = pippone & 0xff;
se volessi riunirlo in pippo16bit come devo fare ?
grazie
Pippo


pippone = (pippo8bi5hi<<8) | pippo8bitlo;
in ogni caso dividere così le variabili non è una buona idea nel caso
avessi voglia di portare il tuo codice su più architetture.
Ciao!

fma...@gmail.com
 ha scritto:

Grazie Fmassi
Ciao
Pippo

 > avessi voglia di portare il tuo codice su più architetture.
Cioe'?
Se pippo8bit e' uint8_t e pippo16bit e' uint16_t, dove'e' il problema?
Se intendi muoverti su architetture a 8 bit che non hanno quantita' a
16 bit... uint16_t e' molto probabilmente indefinito.
Questo codice funziona anche su varie architetture a 32 e 64 bit (OS
X, Linux, AIX...):
#include <stdint.h>
#include <stdio.h>
int main(void)
{
    uint8_t a, b;
    uint16_t c, d;
    c = 660;
    a = c >> 8;
    b = c ;
    printf("a = %hhu(%hhx), b = %hhu(%hhx), c = %hu(%hx)\n", a, a, b,
b, c, c);
    d = a << 8 | b;
    printf("d = %hu(%hx)\n", d, d);
    return 0;
}
E stampa questo:
a = 2(2), b = 148(94), c = 660(294)
d = 660(294)


http://en.wikipedia.org/wiki/Endianness
sempre che associata ai byte della variabile ci siano dei significati,
cosa probabile.


Quello e' un problema a parte.
Le operazioni di bitshifting funzionano alla stessa maniera su
qualunque piattaforma C, sia essa big o little endian.
Il bit shifting e' proprio il modo di evitare i problemi relativi
all'endianness.
Se invece di spezzare un intero (diciamo 4 byte) usando i bit shifting
e memorizzandolo nell'ordine che definisco io, lascio che il computer
faccia una semplice memcpy, allora ho problemi perche' non sono io,
con il bit shifting, a controllare l'ordine dei byte. E' il computer
stesso, quindi se vado su una piattaforma con un ordinamento diverso,
non ho controllo sull'ordine di combinazione dei byte.
Usare il bit-shifting e' il metodo migliore per rendere il codice
portabile.

 portabile.
Infatti, come ho riportato in un post precedente, quel codice funziona
su AIX invariato. AIX e' bigendian.


Fino a qui ci siamo, ma qualcosa con questi byte ci dovrai fare!
Certo, potresti anche non utilizzare tutte le funzioni che spostano
memoria, ma a rigor di logica direi che usare delle hton_/ntoh_
all'entrata e all'uscita dei dati (sia che provengano da file o da
rete) è di gran lunga la soluzione più comoda e logica.

 rete) è di gran lunga la soluzione più comoda e logica.
Mi hai perso...

> Mi hai perso...
Diciamo che il tuo programma ha due necessità:
- operare su dei dati che possono arrivare da macchine con qualsiasi
endianness
- poter girare su macchine con qualsiasi endianness
In questo scenario io userei le macro htonX e ntohX (che sono standard
POSIX) sui dati in entrata e in uscita, portando i dati da host a
network byteorder, qualsiasi ordine sia. In questo modo nel resto del
codice lavorerò *sempre* con il byteorder che mi aspetto di trovare, e
con uno sforzo minimo.
Non mi sembra una pratica molto strana, comunque.
Ciao!


Nel tuo scenario, c'e' una terza necessita':
- che i dati in ingresso (vedasi punto uno) siano stati scritti nello
stesso ordine che si aspetta il tuo programma.
Se tu mi puoi indicare il motivo per cui la tua soluzione sia piu'
portabile della mia, mi farebbe piacere conoscerla.
Anche perche' non riesco a capire come tu possa parlare di standard
Posix e portabilita', senza sottolineare che lo standard Posix *non*
e' disponibile su alcune piattaforme.
Mentre le operazioni di bit shifting che ho presentato sono
disponibili e funzionano nella stessa maniera su qualunque piattaforma
con un compilatore C standard.

 con un compilatore C standard.
Oltretutto non è detto che il codice debba essere portabile per essere
buono.


Qui ti stati contraddicendo: se il dato da file/rete e', come dici tu
"in un ordine specificato", nel tuo programma puoi benissimo usare
operazioni di bit shifting nell'ordine specificato.
Se vuoi ottimizzare il codice, puoi allora confrontare l'ordine dei
bytes che arrivano con quello che usa la macchina in memoria, e se
combacia, puoi leggere i dati direttamente.
Ma se non vuoi ottimizzare ed essere portabile, le operazioni di bit
shifting sono invariate da macchina a macchina: nella macchina C le
operazioni sui bit, come ho detto, funzionano sempre alla stessa
maniera.
Se tu puoi portare un programma completo che presenti il problema
possiamo parlarne. Altrimenti non riesco a capire dove sia il problema.

 possiamo parlarne. Altrimenti non riesco a capire dove sia il problema.
Ho un file "test" che contiene un solo intero a 32 bit, memorizzato
big endian.
Lo voglio leggere, incrementare di uno e riscrivere.
Una copia di questo programma deve girare su più macchine e sullo
stesso file.
Io lo scriverei così:
#include <stdio.h>
#include <stdint.h>
int main()
{
    FILE *in;
    uint32_t i;
    if ((in = fopen("test", "r+"))==NULL)
        perror("fopen");
    fread(&i, 4, 1, in);
/* in questo punto io farei */
i = ntohl(i);
    printf("%lu\n", i);
    i = i + 1;
/* e in questo */
i = htonl(i);
    rewind(in);
    fwrite(&i, 4, 1, in);
    fclose(in);
    return 0;
}
con lo shift come faresti? :)


Mi sono permesso di fare un programm leggermente piu' robusto.
Il programma, cambiando il typedef iniziale bigint, e' in grado di
usare interi a 8, 16, 32, 64 etc... bit.
Si lascia come esercizio all'utente di sostituire l'uso dei tipi
dichiarati da stdint con i tipi standard, rendendo il codice ancora
piu' portabile, ma meno facile da manutenere.
Testato su Linux Intel x86(Slackware), Linux Intel x86_64
(Bluewhite64) e IBM AIX 5 PowerPC.
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
typedef uint64_t bigint;
#define BUFSZ   sizeof(bigint)
int
main( void )
{
        size_t j, t;
        FILE *in;
        uint8_t buf[BUFSZ];
        bigint i;
        if( ( in = fopen( "test", "r+" ) ) == NULL )
        {       perror( "fopen" );
                exit( EXIT_FAILURE );
        }
        if( BUFSZ > fread( buf, 1, BUFSZ, in ) )
        {       fputs( "fread failed\n", stderr );
                exit( EXIT_FAILURE );
        }
        for( i = 0, j = 0, t = BUFSZ-1; j < BUFSZ; ++j, --t)
        {       i |= (bigint)buf[j] << (8*t);
        }
        ++i;
        for( j = 0, t = BUFSZ-1; j < BUFSZ; ++j, --t )
        {       buf[j] = i >> (8*t);
        }
        rewind( in );
        if( BUFSZ > fwrite( buf, 1, BUFSZ, in ) )
        {       fputs( "fwrite failed\n", stderr );
                exit( EXIT_FAILURE );
        }
        fclose( in );
        return 0;
}
P.S.
Nel tuo programma, ti sei dimenticato di includere <arpa/inet.h> per
l'uso di htonl().


0x12345679 - chissà che valore rappresenta.
Sbaglio io o non vedo il trucco?

 Sbaglio io o non vedo il trucco?
Sbagli tu.
Non c'e' nessun trucco.
Stai confondendo 'valori' e 'rappresentazioni in memoria'.
Se proprio vuoi una prova, inserisci una printf di i in base dieci,
prima e dopo l'incremento, e confronta i risultati di due macchine.
Le operazioni C sui bit intervengono sui loro valori, non sulle loro
rappresentazioni in memoria.
Mi sarei aspettato come minimo, prima di rispondere, che tu ti fossi
andato a studiare l'argomento.

 0x12345679 - chissà che valore rappresenta.
Ecco il tuo errore: tu stai confondendo il valore e la
rappresentazione: quello e' un valore, e non rappresenta null'altro
che se stesso.

 andato a studiare l'argomento.
Non ti posso dare torto - sarà che non programmo spesso su big endian
e certe cose non mi vengono naturali. In ogni caso adesso lo provo :)