Newsgroup: it.comp.lang.c
Titolo: offset di un campo in una struttura

ciao a tutti
qualcuno di voi conosce un modo per ottenere l'offset di un campo di una
struttura come fosse una costante intera?
mi spiego meglio con un esempio:
typedef struct
{
  char a;
  int  b;
  long c;
} s;
#define fieldoffset( struct,field )  ???? /* come la faccio questa  macro?
*/
int  main ( void )
{
  return  fieldoffset( s,c ); // voglio sapere l'offset di c in una
struttura tipo s
}
i miei pensieri mi hanno portato ad una sola soluzione, che non mi soddisfa,
perche' il risultato non e' una costante numerica ma un calcolo:
#define fieldoffset( struct,field ) ( (char*)&struct.field -
(char*)&struct )
int  main ( void )
{
  s dummy;
  return  fieldoffset( dummy,c );
}
aiutatemi
ciao
donCiccio

struttura come fosse una costante intera?
Guardati "offsetof", e' standard.


grazie 1000!
ciao
donCiccio

 Guardati "offsetof", e' standard.
Cavolo! Non la conoscevo. Grazie di avermela fatta "incontrare".
EMG

 Guardati "offsetof", e' standard.
Scusate se mi intrometto.
offsetof(s,p) e' definita come
    (int)(&((s *)0)->p)
o qualcosa del genere.
Qualcuno mi puo' speigare come diavolo puo' funzionare una
cosa del genere?
Grazie e ciao a tutti.

cosa del genere?
Come la cosa viene implementata dipende dal singolo compilatore...
quello che mostri e' comunque abbastanza ragionevole: supponendo
di avere
    typedef struct POINT
    {
      double x,y;
    } Point;
allora offsetof(Point,y) diventa
   (int)(&((Point *)0)->y)
(Point *)0 costruisce un puntatore nullo per il tipo Point;
questo puntatore nullo viene utilizzato per ottenere il membro
cercato (con "->y"); l'indirizzo di questo membro una volta
trasformato in intero e' il valore finale.
Immaginando una architettura in cui il puntatore nullo e'
composto da tutti bit a zero e in cui gli interi sono grandi
quanto i puntatori (e le trasformazioni int<->pointer non
alterano i bit) potrebbe essere una possibile implementazione.
Queste trasformazioni sono ovviamente dipendenti dalla
piattaforma e non portabili. Quello che e' garantito e'
che se usi offsetof(...) il codice che ottieni e' invece
portabile (le diverse implementazioni avranno definizioni
se necessario diverse di offsetof).
Andrea
--
Andrea Griffini, Programmer
http://www.netservers.com/~agriff

 (Point *)0 costruisce un puntatore nullo per il tipo Point;
La cosa che mi dava i crampi allo stomaco era
l'istruzione
((Point *)0)->y
che per un puntatore nullo e' un'operazione
alquanto "problematica".
Ma chiaramente quello che mette le coe a posto
e' "&". Dato che il compilatore conosce come e'
fatta la struttura e' sempre in grado di generare
il codice corretto per
  struct nomeStruct testStruct;
  &(testStruct->campo);
qualunque sia il valore di testStruct.
Grazie & Ciao.

 alquanto "problematica".
perche' mai, infondo 0 e' un indirizzo come tutti gli altri, anche nel
tuo PC esiste memoria all'indirizzo 0!
prendendo spunto da come ho iniziato il thread:
- se ho una struttura definita come:
typedef struct
{
  char a;
  int  b;
  long c;
} s;
- e un variabile di questo tipo:
s var;
se scrivo
&var
ho l'indirizzo di inizio della struttura intera, che coincide con il
primo campo, mentre se scrivo
&var.c
ho l'indirizzo di inizio del campo 'c'.
se sottraggo i 2 indirizzi
&var.c - &var  >>> ci vogliono almeno 2 cast a char*, ma per ora
semplifichiamo
ottengo l'offset tra il campo 'c' e l'inizio della struttura, ovunque
essa sia in memoria.
Ora supponi che la variabile 'var' sia proprio all'indirizzo 0, cioe'
che &var=0, cosa ottieni?
ottieni che
0.c - 0 = offset di 'c'
aggiungi i dovuti cast, ed ottieni la macro offsetof
geniale!
non la conoscevo, e ancora grazie a Laforgia/Talkie.
ciao
donCiccio

tuo PC esiste memoria all'indirizzo 0!
Il valore 0, inteso come valore del puntatore nullo, potrebbe essere
diverso dall'indirizzo 0.
Lo 0 indicante il puntatore nullo viene tradotto in cio' che il
sistema operativo intende con puntatore nullo.
Alcuni sistemi operativi (vedi Windows) hanno una fascia di indirizzi
che rappresentano il "null pointer".

 che rappresentano il "null pointer".
per me non e' vero.
Il null pointer vale sempre 0.
in stddef.h e' definito cosi'
#define NULL ((void *)0)
sia in DOS sia in Windows
ho provato a scrivere con VC++5.0
char * p=0;
p++;
se lo esamini in debug, p vale 0 e poi 1
se poi continui con
*p=0
si arrabbia windows :-)
ciao
donCiccio

 leggi o scrivi c'e' memoria all'indirizzo 0.
Questo mi risulta strano. Sotto Linux
char* charP = NULL;
 *charP;
dovrebbe dare un bel SIGSEGV anche se sei root. :-)
Cosa analoga sotto Windows.
C:\>iao Angelo

 dovrebbe dare un bel SIGSEGV anche se sei root. :-)
certamente!
ho scritto "se hai i diritti di accesso per quell'indirizzo", che non
va inteso come utente, ma come programma, nel senso che se fosse per
esempio il kernel a eseguire quell'istruzione non darebbe errori
(forse).
ciao
donCiccio

sia in DOS sia in Windows
Aridaglie... non vuol dire nulla lo 0 assegnato col linguaggio, con
cio' che viene rappresentato internamente.  Lo 0 viene castato (ovvero
proiettato) in cio' che e' il null pointer void.
Leggi le FAQ di comp.lang.c sui "Null Pointers", scoprirai qualcosa di
nuovo (specie la 5.17).

"Andrea Laforgia" <xx...@xxxxx.xxx> ha scritto nel messaggio
news:nh3lht4m5ufllvctr...@4ax.com...

dove trovo la 5.17 nelle faq?
e poi calma!
partiamo da come funziona il modo protetto su una cpu 386 e superiore,
cioe' tipo il mio PC (e forse anche il tuo).
La memoria fisica non e' direttamente indirizzabile dal tuo programma,
a meno che non sia il kernel, e si autoimposti il suo descriptor in
modo da piazzare il segmento di memoria a indirizzo 0 fisico, e fin
qui forse sarai daccordo.
Un qualsiasi indirizzo nel tuo programma e' rappresentato da un offset
su 32bit come un normalissimo valore intero (ripeto sto parlando di un
386).
Questo valore fa pero' riferimento a cosa dice il selector attivo in
quel momento.
Il selector e' un numero a 16 bit che identifica la struttura del
descriptor per il segmento di memoria che stai usando.
Nel descriptor qualcuno a priorita' piu' alta del tuo programma ci ha
scritto, dove inizia quel segmento nella memoria fisica, il diritti di
accesso read/write per data/code/stack, e il range che l'offset puo'
assumere e forse anche qualcos'altro che ora non mi ricordo.
Tutto qui. Anche in Windows.
I tuoi pointer sono quindi valori interi, ma non corrispondono ad un
indirizzo fisico della ram, devi passare attraverso selector e
descriptor e start address prima di arrivare a dove punta realmente,
ma questo non vuol dire che nella tua variabile ci sono scritti numeri
strani, convertiti in chi sa quale modo, se scrivi char*p=0; in p c'e'
proprio scritto ZERO.
ciao
donCiccio

proprio scritto ZERO.
Non e' cosi'.
Vai a leggere la sezione delle FAQ che descrive i "Null Pointers" per
cortesia e non insistere oltre.

cioe' tipo il mio PC (e forse anche il tuo).
...
Trovo questa discussione e' abbastanza assurda...
Come sia implementata offsetof e' un problema di chi realizza il
compilatore. Se su 386 in 32-bit flat mode con o meno memoria
virtuale, con o meno Win32 o Linux o QNX e' un problema abbastanza
poco interessante (in it.comp.lang.c).
Su piattaforme in cui il puntatore nullo non ha tutti i bit a zero
chi ha realizzato la macro avra' usato un altro sistema... dove
sta il problema ???


 Andrea
il problema non e' un problema di linguaggio C, sono OffTopic e me scuso.
Ho dato a Darok una mia spiegazione della macro offsetof, per sitemi
DOS/windows, visto che per lui (Darok) il pointer null era qualcosa di
"problematico", poi e' arrivato Andrea a puntualizzare cose non vere...
(almeno per processori x86).
fine (per quel che mi riguarda) del thread
ciao
donCiccio

"problematico", poi e' arrivato Andrea a puntualizzare cose non vere...
Ehi bello, datti una calmata, qui chi dice cose NON VERE sei solo e
soltanto tu... vatti a studiare le FAQ e qualche buon libro
sull'organizzazione della memoria sotto DOS e Windows, poi ne
riparliamo.  Ma tu guarda quanta pazienza che bisogna avere !

 "problematico",
Non so tu, ma io ho sempre un mucchio di problemi quando scrivo
   ptr->mbr;
e ptr == NULL, indipendente da quanto vale fisicamente NULL. :-)
Non so per quale motivo, ma guardando la definizione di
offsetof mi sono fissato su ((char*)0->mbr) che ovviamente
e' priva di senso (nei programmi normali, non mi sono mai occupato
di kernel & device driver): e' questo che mi dava qualche grattacapo.
Ragionandoci meglio (e grazie alle risposte avute) mi sono accorto
che la chiave del ragionamento era nell'"&" che stava prima.
Anche se (IMHO obviously e magari sbaglio) e' perfettamente
palusibile avere una struttura memorizzata a partire dall'indirizzo
0 (sempre che abbia senso parlare di indirizzo 0x0, cioe' che non
sia un range di indirizzi considerati 0x0), i S.O. utilizzano
questo indirizzo per trappare gli errori, pertanto e' un indirizzo
inutilizzabile per la memorizzazione: questo non esclude
che comunque possa essere usato per fare operazioni di differenza
((char*)ptr - (char*)0 non provoca nessun errore di compilazione o di
esecuzione) o calcolare un offset. A capire quest'ultimo concetto
ho impiegato un po' piu' di tempo.
Non avevo la minima intenzione di "dare fuoco alle polveri" e mi
scuso se ne sono stato causa. :-)
Bye Bye da Darok il cui motto e' Smile you are on USENET :-).

 Bye Bye da Darok il cui motto e' Smile you are on USENET :-).
:-)
ciao
donCiccio

indirizzo non valido, e NULL e' uno di questi.
Questo non e' esatto. Lo standard dichiara come undefined behaviour
*qualunque uso* di un indirizzo non corretto; incluso il semplice
confronto con NULL. Per farti un esempio se scrivo...
   int v[10]; /* Array di 10 elementi */
   int *pv=v; /* Puntatore al primo elemento */
   pv+=11;    /* Punta al di fuori dell'array: BUM! */
Questo perche' ad esempio l'aritmetica dei puntatori deve essere
esercitata senza mai uscire dai limiti consentiti... anche il
solo confronto con NULL di un puntatore che contiene un indirizzo
non valido e' da considerarsi come operazione dal risultato
imprevedibile (i famosi demoni che escono dalle narici di cui
avrai tanto sentito parlare).
Infatti su alcuni hardware potresti avere errori di protezione
anche solamente facendo "p++" se p per colpa di questa operazione
dovesse uscire dai limiti consentiti (questo rischio in
particolare c'e' con i sistemi che prevedono registri dedicati
per gli indirizzi e in cui il semplice impostare un registro di
indirizzo con valori inaccettabili attiva una trap).
Ad un puntatore e' consentito puntare dal primo di un array
sino al "successivo dell'ultimo" (es. v+10 nel caso di prima)
a patto pero' di non provare a leggere/scrivere a questo
indirizzo "accettabile" ma fuori dall'array.
Ogni altro uso dei puntatori, come il sottrarre puntatori che non
appartengono alla stessa area contigua e' undefined behaviour.
HTH


 appartengono alla stessa area contigua e' undefined behaviour.
sono ancora piegato al volere dello standard, ma per me rimane il fatto (che
ti posso solo provare su archittetture x86) che se un pointer ad un
indirizzo non valido, lo uso per tutto quel che mi pare eccetto
leggere/scrivere in quell'indirizzo, non si attivano eccezioni della cpu.
ciao
donCiccio

 ci siano array di mezzo, prendi ad esempio una malloc.
Una chiamata a malloc() alloca proprio un array, anche se senza nome.
EMG


dall'help del BC:
malloc allocates a block of size bytes from the memory heap. It allows
a
program to allocate memory explicitly as it's needed, and in the exact
amounts needed.
non parla di array, anzi proprio il void* di ritorno fa capire che per
malloc quell'area allocata puo' essere qualsiasi cosa.
o forse intendevi un array di byte?
bhe allora anche una variabile normale e' un array.
cmq, la frase "lo standard dice..." mi basta per dirvi: avete ragione.
ciao
donCiccio

leggere/scrivere in quell'indirizzo, non si attivano eccezioni della cpu.
Basta immaginare una architettura basata su x86 non flat, che
usi puntatori a 6 bytes per far crollare quanto stai dicendo.
Stai dicendo cose errate anche senza uscire dal tuo microscopico
mondo x86 che ti sei costruito e che non vuoi abbandonare nemmeno
semplicemente con il ragionamento.
La cosa mi intristisce :( ... questo non e' essere pragmatici.
E' impermeabilizzarsi il cervello.
In informatica forse piu' che in altri ambiti questo equivale
a suicidarsi dal punto di vista della professionalita'.


 cmq, la frase "lo standard dice..." mi basta per dirvi: avete ragione.
No, non so cosa dica esattamente lo standard al riguardo.
EMG

 semplicemente con il ragionamento.
hai colpito nel segno.
ho un grande difetto, sono cocciuto come san Tommaso.
e' tutto dovuto al fatto che sono 12 anni che lavoro su schede 286 e 386 e
non conosco quasi nulla del resto del mondo, che nel mio piccolo ho sempre
immaginato non tanto differente da cio' che conosco.
alzo la bandiera bianca in segno di resa, mi accorgo ora che sto difendendo
l'indifendibile.
spero che le mie scuse (ormai e' doveroso farle) vengano accettate anche da
Laforgia.
ciao
donCiccio

immaginato non tanto differente da cio' che conosco.
Malissimo... in giro c'e' tanta di quella roba e talmente diversa
che e' inconcepibile che entri tutta nella testa di una persona.
Tu hai conosciuto solo un granellino di sabbia... e da questo vuoi
dedurre come e' fatto tutto il pianeta; oltretutto obbiettando con
chi qualcosa d'altro lo ha visto e cerca solo di informarti (vedi
quelli del comitato dello standard).
In questo modo non si va molto lontano.


 confronto con NULL.
Forse non ho capito, ma stai forse dicendo che
  if(ptr == NULL)
    ...
potrebbe dare problemi?
In caso affermativo, come si puo' testare il valore
di ritorno di funzioni che restituiscono un puntatore
NULL in caso di fallimento?
Grazie e C:\>iao Angelo.

NULL in caso di fallimento?
Non ci sono problemi per il valore NULL e per i puntatori
validi. I puntatori non validi invece non devono essere
usati in nessun modo. Un puntatore non valido lo hai ad esempio:
 - non inizializzando una variabile auto:
   void foo(void)
   {
     void *p;     /* NON inizializzata */
     if (p==NULL) /* Anche il solo test puo' dare problemi */
      ...
   }
 - costruendo un puntatore con delle combinazioni
   di bit arbitrarie (con casting o tramite un altro
   puntatore usato per scrivere sul puntatore)
     int *p=(int *)12345;
     if (p==NULL) /* Questo test puo' dare problemi */
 - utilizzando l'aritmetica dei puntatori per uscire
   dai limiti ammessi:
     int v[10];
     int *p=v;
     p--; /* Questo decremento puo' dare problemi */
 - dopo aver fatto la free di un puntatore valido;
   ad esempio:
     int *p=malloc(10*sizeof(int));
     ...
     free(p);
     if (p==NULL) /* Questo test puo' dare problemi */



Se il puntatore vale NULL allora non contiene un indirizzo non corretto
e quindi è possibile confrontarlo con il valore NULL stesso. Se contiene
un valore diverso da NULL ottenuto tramite la chiamata a malloc() allora
contiene ugualmente un indirizzo corretto confrontabile con NULL e
utilizzabile.
EMG

  }
ti chiedo scusa se continuo a insistere, e' nella mia natura.
cmq per non crearmi un altro nemico in questo NG, cerchero' di non
replicare piu' accettando la gia' ormai conclamata sconfitta. :-)
ciao
donCiccio

 
Ok! Questo era quello che sapevo io. Avevo interpretato male la
risposta e mi stavo preoccupando per quel paio di milioni di
volte che avevo usato NULL == ptr. :-)
Grazie (anche ad EMG) per la risposta.
C.\>iao Angelo

replicare piu' accettando la gia' ormai conclamata sconfitta. :-)
Hehehe... boh... fa un po' come ti pare. Io non sono uno che sta
zitto solo per il quieto vivere; la rintengo una mancanza di
rispetto nei confronti dell'interlocutore (qualcosa del tipo
"quello ha torto ma e' troppo fesso per capirlo e tentare di
spiegarglielo lo farebbe solo incacchiare").


 zitto solo per il quieto vivere;
bene, questo vuol dire che non ti stai alterando, e quindi si puo'
continuare.
Eventualmente se siamo troppo OT, possiamo continuare in email.
ciao
donCiccio

continuare in email.
Non mi sto alterando affatto :) ... anzi ... trovo pero' questa
discussione offtopic e noiosa.
Potremmo concluderla cosi'...
Io dico "per scrivere codice portabile non devi fare questo
e quest'altro... pena undefined behaviour; non lo dico io ma
lo dice lo standard ANSI".
Tu rispondi "ma con il mio compilatore fanta-C della canistracci
software versione 3.1415 le cose funzionano alla perfezione
quindi penso che in realta' lo standard esageri e non esista
nessun computer al mondo in cui quella cosa puo' creare un problema".
Io dico "Vabbe'... contento tu contenti tutti. E' un peccato pero'
rinchiudersi (mentalmente) da soli in una bara. Il mondo e' bello
e vario e sarebbe meglio dargli (se possibile) un'occhiata."
Tu dici "Visto che siamo OT possiamo continuare per posta elettronica
a discutere di cosa fa la versione 3.1415 del mio fanta-C ?"
Io dico "Molto negativo. Non se ne parla proprio. Inoltre ti
aggiungo che:
 (Attenzione a non leggere quanto segue tutto d'un fiato... il
  veder crollare cosi' tante certezze potrebbe provocare
  scompensi emotivi)
  1) Non e' vero, in generale, che gli indirizzi siano di 32 bit
  2) La memoria video non inizia, in generale, all'indirizzo 0xA0000
  3) Il codice di "freccia in alto" non e', in generale, 0 + 72
  4) Non tutti i computer ripartono se premi CTRL-ALT-DEL. Alcuni
     manco hanno il tasto ALT. Alcuni non hanno neanche la tastiera.
  5) Non tutte le schede video sono compatibili VGA
  6) Per il mouse non si usa sempre l'int 33
  7) Nel wrestling fanno solo *finta* di combattere
"
Saluti :)



bye

 sistema... ma non sarei stupito che lo stack non lo usasse del tutto.
Lo standard quindi non ritiene valido ( nel caso di funzioni a numero 
variabile di parametri ) cose del tipo
f( int a, ... ){
        int b=*(&a+1) ...
}
Bisogna x forza usare le macro VA_* ?

Bisogna x forza usare le macro VA_* ?
Si... e queste funzionano *solo* con funzioni con un
numero variabile di argomenti: il compilatore deve essere
informato di cosa vuoi fare visto che in quel caso
potrebbe generare codice di chiamata differente (magari
non usato di solito perche' meno efficiente) per
permetterti l'accesso "dinamico" ai parametri.
