Newsgroup: it.comp.lang.c
Titolo: Fare dei calcoli... "portabili"

Ho un array
  unsigned char uc[4];
Da questo devo calcolare un numero unsigned a 32 bit, considerando uc
[0] l'MSByte.
Supponiamo che l'architettura permetta un unsigned long a 32 bit
(forse è obbligatorio, non so), ma che non sappia se l'unsigned int
sia a 8, 16 o 32 bit. Che espressioni dovrei scrivere per avere
contemporaneamente portabilità del codice e massima ottimizzazione?
Scrivendo:
  unsigned long result = 0x1000000 * uc[0] + 0x10000 * uc[1] + 0x100 *
uc[2] + uc[3];
avrei un risultato non corretto su un'architettura a 16 bit (0x100 * uc
[2] potrebbe andare in overflow).
Scrivendo:
  unsigned long result =
      (unsigned long)0x1000000 * uc[0] +
      (unsigned long)0x10000 * uc[1] +
      (unsigned long)0x100 * uc[2] +
      uc[3];
sicuramente funzionerebbe, ma non so se è il massimo
dell'ottimizzazione su tutte le piattaforme.
Un'altra domanda. Supponendo che uc non sia allineato ai 32 bit su una
architettura a 32 bit. Potrei sempre scrivere:
  unsigned long result = *(unsigned long *)uc;
accettando peggiori prestazioni, oppure su alcune architetture
potrebbe non funzionare?
Naturalmente in questo caso so che l'architettura è Big Endian.

 potrebbe non funzionare?
Su alcune architetture potrebbe non funzionare, per esempio Itanium.
Ho appena adesso scritto un piccolo programmino che dimostra, su
Itanium, che se allinei i dati in maniera errata, ti becchi un Bus
error (core dump).
Compilalo con GCC (e' scritto in standard C89).
Il programma accetta due parametri in input, il primo e' una lettera
(s, i, l) che determina il tipo su cui vuoi fare l'operazione, il
secondo e' un numero che rappresenta l'allineamento all'interno
dell'array 'core'.
Il programma usa un puntatore per puntare ad un indirizzo specifico di
un particolare tipo: se la lettura da quell'indirizzo non funziona, il
programma fa core dump.
Per esempio:
$ ./a.out s 3
Bus error (core dumped)
$./a.out s 2
$./a.out s 1
Bus error (core dumped)
$
----main.c
#include <stdlib.h>
int
main(int argc, char **argv)
{
        char core[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14, 15, 16};
        unsigned short *s;
        unsigned int *i;
        unsigned long *l;
        unsigned long r;
        char type;
        int offset;
        if (argc != 3)
                return 1;
        type = argv[1][0];
        offset = atoi(argv[2]);
        if ('s' == type) {
                s = core + offset;
                r = *s;
        } else if ('i' == type) {
                i = core + offset;
                r = *i;
        } else if ('l' == type) {
                l = core + offset;
                r = *l;
        }
        return 0;
}
----

 Su alcune architetture potrebbe non funzionare, per esempio Itanium.
Hmm... interessante. Anche se, alla fine, mi sembra un problema di
compilatore che potrebbe tranquillamente fare i calcoli riallineando
ciò che è disallineato.

 ciò che è disallineato.
No, il compilatore non puo' fare nulla al riguardo: se tu dai
l'inidirizzo ad un puntatore, il compilatore non puo' inventarsene un
altro in base all'uso.
L'inidirizzo di un simbolo viene calcolato in base alla sua
dichiarazione non al suo uso.
Faccio sviluppo su varie piattaforme a 32 e 64 bit (incluso Itanium),
e il codice che ti ho dato ci ha gia' dato problemi in alcuni prodotti
non pensati in maniera portabile. Non e' un peccato, ma nemmeno un
gran bel codice, specialmente se il codice non e' specifico per una
determinata architettura.