Newsgroup: it.comp.lang.c
Titolo: type punning

Ciao a tutti,
volevo sapere in che contesti e in qual misura è possibile accedere ad un
oggetto tramite un'espressione l-value con un tipo differente rispetto a
quella dichiarato per l'oggetto.
Molti di questi esempi sono stati riportati da un libro di testo "Linguaggio
C/C++" e vorrei discutere la loro aderenza con lo standard.
Ci sono diversi casi che vorrei analizzare con voi, a prescindere dalla loro
effettiva utilità:
_______________________________________________
1) int a = 10;
    char* ptr = (char*) &a;
    *ptr = 0; // modifico il primo byte di a
    *(ptr + 1) = 0; // modifico il secondo byte di a
Secondo il testo, è possibile accedere ai byte di una variabile di qualsiasi
tipo mediante un'espressione con l-value di tipo character type (char,
signed char, unsigned char). Qualsiasi altro accesso con l-value differente
rispetto a quello dichiarato per l'oggetto in questione è indefinito.
2) const int a = 10;
    int* ptr = (int*) &a;
    int b = *ptr; // accedo in sola lettura
Secondo il testo, è possibile accedere ad oggetto di tipo CONST P con un
l-value di tipo P a condizione che l'accesso rimanga di sola lettura. La
modifica della variabile costante decade in un undefined.
3) volatile int a = 10;
     int* ptr = (int*)&a;
     int b = *ptr;
Secondo il testo, questo codice è errato in quanto qualsiasi accesso ad una
variabile di tipo VOLATILE P mediante un l-value di tipo P decade in un
undefined.
4) L'unica eccezione alla regola è data  quando:
    signed TIPO a = 10;
    unsigned TIPO *ptr = (unsigned TIPO* ) &a;
    *ptr = 10;
    int b = *ptr;
    dove TIPO è un qualsiasi integer type (char, int) ed il valore letto
dall'oggetto o scritto nell'oggetto è rappresentabile sia nella
rappresentazione con segno sia nella rappresentazione senza segno.
________________________________________________________
Se esistono effettivamente casi del genere, sapete per caso se ne esistono
altri?
Un'altra cosa.
Il libro in questione indica che è possibile la conversione da un puntatore
ad un oggetto ad un altro è un'operazione possibile solo se il puntatore
risultante è correttamente allineato con l'oggetto puntatore. Che vuol dire?
Grazie, Ciao.



 dire?
Mandare una mail all'autore (o al traduttore), potrebbe aiutare :-)
HTH
C:\>iao Angelo
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

risultante è correttamente allineato con l'oggetto puntatore. Che vuol dire?
Detta in questi termini, la frase mi è molto oscura.
-- 
Andrea Laforgia
"640kb of memory should be enough for anyone." -- Bill Gates.

 Detta in questi termini, la frase mi è molto oscura.
Penso che indendesse che  la conversione da T* a P* e`  valida solo se i
puntatori hanno  gli stessi requisiti di  allineamento o se passo  ad un
allineamento meno restrittivo. Ed anche questa frase e` vera.
Supponiamo  di  avere una  macchina  dove  i  char possono  allineati  a
qualunque byte, gli int  devono essere allineati a 2 byte,  i double a 4
byte. Non sono possibili accessi a dati non allineati.
char *c;
int *i
double *d;
Questi 3  sono tutti corretti.  /c/ non ha particolari  requisiti, posso
assegnargli qualunque  puntatore. /i/  deve essere  allineato a  2 byte,
visto che gli assegno un puntatore a double che e` allineato a 4 byte la
condizione e` sicuramente soddisfatta.
c = (char*)d;
c = (char*)i;
i = (int*)d;
In tutti  gli altri casi,  passo a  una condizione di  allineamento piu`
stringente e quindi  cado in un undefined behaviour. Ad  esempio, un int
e` allineato  a 2,  quindi potrebbe  anche essere  allineato a  4byte ma
questo non e` sicuro: il risultato e` quindi undefined.
Ah,  allineato a  N byte  significa  che il  dato puo`  iniziare solo  a
indirizzi di memoria multipli di N.
Inoltre lo  standard assicura  che un  puntatore a void  e` in  grado di
soddisfare i requisiti di allineamento piu` stringenti. Quindi:
int *a, *b;
void *v;
a = b;
v = a;
a = v;
sicuramente (a == b).
Aggiungo  alcune  piattaforme (es.  x86)  possono  accedere a  dati  non
allineati, anche  se con una  perdita di prestazioni. Su altre  (mi pare
sugli  Sparc),  l'accesso  a  dati non  allineati  causa  una  exception
hardware.
ciao
Kronos
-- 
Home: 
http://kronoz.cjb.net
"Di tutte le perversioni sessuali, la castita` e` la piu` strana".
Anatole France

Ringrazio innanzitutto per le risposte, però vorrei ancora il vostro parere
su questi paragrafi presi dallo standard
        "
       Certain object representations  need  not  represent  a
       value  of the object type.  If the stored value of an object
       has such a representation  and  is  accessed  by  an  lvalue
       expression  that  does not have character type, the behavior
       is undefined.  If such a representation  is  produced  by  a
       side  effect  that modifies all or any part of the object by
       an lvalue expression that does not have character type,  the
       behavior is undefined.35)  Such a representation is called a
       trap representation.
       "
       "
       If the  function  is
       defined  with  a type that does not include a prototype, and
       the  types  of  the  arguments  after  promotion   are   not
       compatible with those of the parameters after promotion, the
       behavior is undefined, except for the following cases:
         -- one promoted type is a signed integer type,  the  other
            promoted  type  is  the  corresponding unsigned integer
            type, and the value is representable in both types;
         -- one type is pointer to void and the other is a  pointer
            to a character type.
      "
Se l'intercambiabilità è valida in questo contesto, allora penso che essa
sia valida anche in un contesto
come:
      signed int variabile1 = 10;
      unsigned int* ptr = (unsigned int*) ptr;
      unsigned int variabile2 = *ptr; // accesso corretto
      *ptr = 11; // accesso corretto
      // e viceversa;
      // oppure
      char a = 'a';
      char b = 'b';
      void* ptr1 = &a;
      char**ptr2 = (char**) &ptr1;
      char* ptr3 = *ptr2;
      *ptr2 = &b;
Però leggendo in seguito viene detto:
      "
      An object shall have its stored value accessed only  by
       an lvalue expression that has one of the following types:61)
         -- a  type  compatible  with  the  effective  type  of the
            object,
         -- a qualified version  of  a  type  compatible  with  the
            effective type of the object,
         -- a   type   that   is   the   signed  or  unsigned  type
            corresponding to the effective type of the object,
         -- a  type  that  is   the   signed   or   unsigned   type
            corresponding  to  a qualified version of the effective
            type of the object,
         -- an aggregate or union type that  includes  one  of  the
            aforementioned  types  among  its  members  (including,
            recursively, a member of a  subaggregate  or  contained
            union), or
         -- a character type.
        "
Alla luce di questo sembra che accedere ad un void* interpretandolo come un
char* risulti essere un errore; ma nelle chiamate a funzioni è possibile
passare un void* a un char* .
Viene anche detto che è possibile interpretare come unsigned T un valore di
tipo signed T, eppure nelle chiamate a funzioni si può incorrere in un
comportamento indefinito.
La frase
          "
          The  same  representation  and alignment requirements are
          meant  to  imply  interchangeability  as   arguments   to
          functions,  return  values from functions, and members of
          unions.
       "
sembra dar ragione a quanto viene esposto nel paragrafo relativo alla
chiamata di funzione. Un unsigned T occupa la stessa memoria e ha gli stessi
requisiti di allineamento di un signed T, però non ha la medesima
rappresentazione se non per i valori che possono essere rappresentati da
entrambi. Con questo si spiega la restrizione aggiuntiva.
Però lo standard specifica altri casi di oggetti che hanno la medesima
rappresentazione e il medesimo allineato. Ad esempio viene detto:
       "
       A pointer to void shall have the  same  representation
       and alignment requirements as a pointer to a character type.
       Similarly, pointers to qualified or unqualified versions  of
       compatible  types  shall  have  the  same representation and
       alignment requirements.27)
       All pointers to structure  types
       shall   have   the   same   representation   and   alignment
       requirements as each other.  All  pointers  to  union  types
       shall   have   the   same   representation   and   alignment
       requirements as each other.  Pointers to  other  types  need
       not  have the same representation or alignment requirements.
       "
e, come si può notare, alcuni di questi rientrano negli undefined behaviour
esposti dai precedenti paragrafi.
In definitiva mi chiedo in quali casi e secondo quali regole è possibile
leggere e/o scrivere su oggetto mediante un l-value di un tipo che
differisce da quello dichiarato.
Sarà anche come dice Angelo un argomento troppo difficile per un comune
mortale :-), però quando si ha un dubbio mi sembra inutile non documentarsi
e continuare a ragionare col proverbio "tanto funziona".
Grazie dell'attenzione. Ciao.

 ha scritto nel messaggio
news:b3gdpl$2vn$1...@dreamland.darkstar.lan...
