Newsgroup: it.comp.lang.c++
Titolo: Se io assegno ad un puntatore.....

... un indirizzo di memoria non allocato, è una cosa molto brutta,
d'accordo, però finchè non provo a leggere o scrivere (dereferenziare)
nell'indirizzo puntato il programma non genera nessun tipo di errori, è
corretto?
Esempio, se io ho un array a di 10 elementi e mi posiziono in a[10] con un
puntatore, a meno che io non legga o scriva in quella locazione di memoria
il programma ha comportamento definito e semanticamente corretto vero?
O non è così?

 memoria il programma ha comportamento definito e semanticamente corretto
Questo č un caso particolare che ha invero un comportamento definito. Lo
Standard infatti prevede che il puntatore alla locazione successiva
(past-the-end) di un arrary debba essere valido, sebbene non derefenziabile.
In virtů di questo, č lecito, ad esempio, utilizzare con gli array i vari
algoritmi della libreria standard che prevedono come argomento il puntatore
past-the-end di una sequenza.
Max

 il programma ha comportamento definito e semanticamente corretto vero?
Un array è un caso particolare, ma al di là di questo dovresti esplicitare
con un esempio cosa intendi per "posizionarsi" su a[10]. Se intendi
semplicemente fare (supponendo a un puntatore) a+=10; non succede nulla
purchè non accedi a *a. 

 wrote in message
pan.2003.05.19....@despammed.com...

Ok tutto qui. Mi avete confermato quello che sapevo già, ma volevo esserne
sicuro.

> è una cosa molto brutta,
ma non genera mai comportamenti indefiniti.
Io sono sicuro quindi che Gabriele abbia ragione.
Tommy

 0 e' sempre un indirizzo non valido, eppure molti fanno cosi'!
Qui mi pare tu stia confondendo due cose diverse.
Il puntatore nullo e il puntatore past-the-end di un array (per tornare al
problema di Gabriele) sono puntatori *validi*, che perň non possono essere
dereferenziati. Un puntatore non inizializzato, un puntatore il cui valore č
stato passato a delete o free, un puntatore a un oggetto uscito di scope,
*questi* sono puntatori non validi.
Tornando alla questione dell'assegnamento. Lo Standard dice che un puntatore
non valido non puň essere "usato". Come questo sia da intendere, francamente
io non lo so, e non voglio impegnarmi in nessuna interpretazione, ma ti
assicuro che qualcuno su comp.lang.c++ ha sostentuto che in teoria per lo
Standard questo codice
 int main() {
        int *p, *q;
        p = q;
    }
avrebbe comportamento indefinito. (Nota che qui si parla di puntatori non
inizializzati, non puntatori inizializzati con 0 o NULL.)
 
http://groups.google.com/groups?th=75bcc6ee081c8a7b
 
http://groups.google.com/groups?th=977e910e74d0681f
Max

lo uso cosi' come e' che errori vuoi mai che mi dia?!?
Ci sono architetture in cui esistono registri speciali
dedicati ad essere puntatori. Con alcune di queste CPU
scrivere un valore assurdo in uno di questi registri
comporta una trap immediata; prima ancora cioe' che
l'indirizzo venga usato per leggere o scrivere.
Molto probabilmente la CPU del computer che stai usando
in questo stesso momento ha questo tipo di comportamento...
infatti questa cosa capita anche con i processori X86 quando
lavori con modelli di memoria segmentata: scrivere un
valore assurdo in un registro di segmento comporta una
trap immediata; anche se quel registro poi non lo usi.
Questo e' un estratto dal manuale Intel:
  MOV—Move (Continued)
  If the destination operand is a segment register
  (DS, ES, FS, GS, or SS), the source operand must
  be a valid segment selector. In protected mode,
  moving a segment selector into a segment register
  automatically causes the segment descriptor
  information associated with that segment selector
  to be loaded into the hidden (shadow) part of the
  segment register. While loading this information,
  the segment selector and segment descriptor
  information is validated (see the “Operation”
  algorithm below). The segment descriptor data is
  obtained from the GDT or LDT entry for the specified
  segment selector.
  A null segment selector (values 0000-0003) can be
  loaded into the DS, ES, FS, and GS registers without
  causing a protection exception. However, any
  subsequent attempt to reference a segment whose
  corresponding segment register is loaded with a
  null value causes a general protection exception
  (#GP) and no memory reference occurs.
Praticamente dice che i processori Intel ammettono
che tu in un registro di segmento ci possa mettere
il valore "nullo" (a patto che poi non usi quel
registro per provare ad accedere a memoria) oppure
un valore valido. Scriverci un valore a caso provoca
una exception immediata.
HTH
Andrea

     }
L'unico motivo per cui potrebbe dare qualche problema e' per quello che dice
Andrea Griffini, ma sinceramente lo considero un problema del compilatore.
Io non ho ancora trovato nessuna fonte ufficiale che mi dica che tale codice
sia scorretto e provochi un undefined behavior. Quindi, non e' per
cattiveria, ma faccio fatica a cambiare parere...
Sul fatto che il codice sopra sia privo di senso e assolutamente NON un buon
esempio di programmazione siamo tutti d'accordo. Sul fatto che produca un
undefined behaviour... mah... non voglio piu' esprimere pareri....
Tommy

 Andrea
Interessante. Quello che dici ha senso e per essere sincero ad alcune delle
cose che hai detto non avevo pensato.
Tuttavia devo ancora riuscire a scrivere un codice in C++ con puntatori non
inizializzati che mia dia problemi ;-) In assembly ok, ma in C++ lo trovo
molto difficile.
A mio avviso un compilatore che combina i pasticci da te descritti nel caso
in cui uno non inizializzi dei puntatori e' un pessimo compilatore e
francamente non ne conosco nessuno.... (come del resto e' una pessima
abitudine non inizializzare i puntatori!)
Ma siamo sicuri che lo standard non garantisca nulla? Nel documento dello
standard e' pieno di esempi dove ci sono puntatori non inizializzati. Tra le
altre cose lo standar parla di undefined behavior per "dereferencing the
null pointer" ma mai per avere (e non usare) puntatori non inizializzati.
Sullo standard leggo:
"After the declaration of an uninitialized pointer x (as with int* x;), x
must always be assumed to have a singular value of a pointer."
Non e' che dica molto, ma non dice nemmeno che da un undefined behavior!
In pratica Andrea il tuo discorso e' un discorso molto interessante e che ho
seguito con molto interesse e ti ringrazion per questo. Sei sicuro pero' che
in C++ porti ad un undefined behaviour. Fantasie aparte di alcuni su
comp.lang.c++, c'e' qualche cosa di piu' ufficiale a riguardo?
Tommy

 dica che tale codice sia scorretto e provochi un undefined behavior.
Lo Standard dice (
3.7.3.2/4
):
 The effect of using an invalid pointer value (including passing it
 to a deallocation function) is undefined
Quindi, tutto sta nell'interpretare quell'"using".
Max

comp.lang.c++, c'e' qualche cosa di piu' ufficiale a riguardo?
Non conosco cosi' bene lo standard da dirti da dove si
puo' iniziare una discussione riguardo a valori singolari
e ai pointer non inizializzati.
E' pero' stato semplice trovare un paragrafo (expr.add,
5.7/5 sul miei doc non ufficiali) in cui viene detto
esplicitamente che se scrivi:
   char x[20];
   char *p = x;
   p = p+21;
hai gia' un undefined behaviour, anche se nel seguito
non dereferenzi p.
Andrea

cattiveria, ma faccio fatica a cambiare parere...
Io da una rapida ricerca (quelle pre-colazione, per intenderci)
ho trovato sul draft solo questo in [lib.iterator.requirements]
24.1/5:
  ...
  Iterators can also have singular values that are not
  associated with  any  container.   [Example: After the
  declaration of an uninitialized pointer x (as with int* x;),
  x must always be assumed to have a singular value of
  a pointer. ]
  Results of most expressions are undefined for singular
  values; the only exception is an assignment of a
  non-singular value to an iterator that holds a singular
  value.  In this case the singular value is overwritten
  the same way as any other value. Dereferenceable and
  past-the-end values are always non-singular.
In pratica viene fatto riferimento ai puntatori tracciando
un parallelo con gli iterator e si dice riguardo agli
iterator che l'unica cosa concessa e' assegnare ad un
iterator contenente un valore "singular" un valore
"non-singular". Questo riportato ai pointer vorrebbe dire:
   void *a,*b;
   void *c = 0;
   a = b; // Undefined behaviour
   b = c; // Permesso
Dove queste cose stiano scritte esplicitamente per i
puntatori pero' non lo so. Ho trovato veramente poche
occorrenze della parola "singular" sui miei documenti.
Fra l'altro credo che il testo fra quadre non faccia
nemmeno formalmente parte dello standard C++ (o che tutta
questa roba sia cambiata nel documento finale che non ho).
Il fatto che si possano veramente avere problemi scrivendo
   void *a,*b;
   a = b;
potrebbe secondo me essere comprensibile solo se a
venisse allocato in registri e b invece in memoria;
con conseguente trap al momento del trasferimento di
cavolate nel registro puntatore.
Su PC questi discorsi non hanno molto senso oggi, visto
che praticamente tutti usano la modalita' "flat" 32 bit
che non tocca mai i registri di segmento.
HTH
Andrea

 senza avere per questo un segmentation fault in quella riga.
Quindi anche:
char* p=0xDEADBEEF
non produce nessun "danno". Sinceramente non capisco tutta le discussione 
che si è aperta... 
-- 
Luc.

 Quindi, tutto sta nell'interpretare quell'"using".
sono molto ignorante di C++, quindi prendete questa osservazione
con beneficio d'inventario...
usando la STL, per esempio, per ordinare un array A lungo 10,
uno tipicamente scrive
std::sort(A,A+10);
ora, A+10 dovrebbe essere un puntatore che punta ad una zona di memoria
non allocata, visto che l'array termina a A[9].
A+10 in questo contesto viene considerato valid or not ?
ciao,g

 Quindi, tutto sta nell'interpretare quell'"using".
se dice anche: including passing it to a deallocation function
credo quindi che using voglia dire dereference e farne uso, non avrelo li
inizializzato e magari assegnargli solo un valore non valido. Passarlo ad
una funzione di deallocazione e' grave!
Ad ogni modo capisco benissimo quello che dici.
Dipende come interpreti quell'"using" e persone piu' autorevoli di me lo
hanno interpretato i modo diverso dal mio.
Quindi con un pochino di diffidenza, ma terro' presente questa cosa ;-)
Grazie.
Tommy

 occorrenze della parola "singular" sui miei documenti.
Gia'!
Tommy

 non dereferenzi p.
In pratica quello che dice lo standard secondo me e' che non e' detto che
p+21 non causi un overflow.
Io concordo con lo standard, nel senso che:
char x[20];
char *p = x;
p = p+1000000000;
potrebbe benissimo causare un overflow!
Ho alzato i valori per renderlo piu' probabile, ma.... secondo lo standard
anche il tuo p+21 puo' causare overflow.
Cio' tuttavia non dimostra il fatto che:
char *p1, *p2;
p1=p2;
Abbia un undefined behavior.
Questo e' quello di cui stavamo parlando, nol risutato di addizioni su
puntatori (ed overflow) ;-)
Ad ogni modo se ha tuo avviso ho interpretato male lo standard fammi sapere.
Tommy

 cavolate nel registro puntatore.
Questo e' esattamente l'esempio che ricordo di aver letto su c.l.c++.m .
Non ricordo piu' se James Kanze o Andrew Koenig (o qualcun altro ancora)
aveva citato
un'architettura sulla quale il semplice caricamento di un puntatore non
valido in un registro di indirizzamento provocava una trap. E' questo il
senso della restrizione presente nello standard.
Ciao,
Nicola
-- 
Posted via Mailgate.ORG Server - 
http://www.Mailgate.ORG


wrote:

Il puntatore nullo e' un caso speciale; come e' un caso
speciale il fatto che e' garantito che e' possibile puntare
al "primo elemento dopo l'ultimo" di un array (anche se non
e' garantito cosa succede provando ad accedere all'elemento).
Diverso e' il caso puntatori non inizializzati o di calcoli
in aritmetica di puntatori che superano i limiti ammessi.
In questi due casi infatti esistono piattaforme con cui e'
possibile avere problemi anche *senza* tentare di accedere
all'elemento puntato.
Andrea
PS: Il codice che hai scritto comunque nemmeno compila...

ciao,g
Un puntatore puo' puntare a un elemento di un array o
anche al primo elemento *dopo* l'array.
Un puntatore che punti a questo ipotetico elemento puo'
essere usato ed e' garantito che si comporti "bene" per
quanto riguarda l'aritmetica dei puntatori e i confronti
con altri puntatori che puntano elementi dell'array.
Quello che non e' permesso pero' con questo puntatore
e' accedere all'elemento. Quindi
  int x[10];
  int *A = x;
  A+10;  // Nessun problema
  A[10]; // Non permesso
HTH
Andrea

 PS: Il codice che hai scritto comunque nemmeno compila...
Ok:
char* p=(char *)0xDEADBEEF;
Dai... non perdiamo tempo... :)
-- 
Luc.

puntatori (ed overflow) ;-)
La descrizione relativa all'aritmetica dei puntatori e'
piu' esplicita e semplice da trovare. Qui la cosa era un
po' piu' difficile da reperire per me perche' il termine
"singular" non e' citato. Resta fermo il fatto che secondo
me questo passaggio non lascia dubbio alcuno sul frammento
di codice che hai scritto sopra. Fare una cosa del genere
e' (per il C++) undefined behaviour. Punto.
Che poi una determinata piattaforma (o anche il 94% delle
piattaforme) non abbiano problemi se non di decenza con
codice di quel tipo e' un discorso *completamente* diverso.
HTH
Andrea

quel registro.
   Concordo pienamente. Non mi ero mai posto il problema, per lo meno
lavorando in C o C++.
   Direi che la sensibilita` sull'argomento e` bassa per vari motivi,
intanto e` pessimo stile di programmazione. Poi non se ne vede
immediatamente il male perche`, di fatto, succede che nessun
compilatore moderno si sogna di generare il codice per mettere il tuo
valore arbitrario in un registro (di indirizzamento, di segmento o
quant'altro), se poi non ne fai uso. Anche l'assegnazione `p = q;',
tra due puntatori non utilizzati, dovrebbe al limite copiare il
contenuto dalla locazione di memoria che contiente il valore casuale
di q, alla locazione di memoria che contiene il valore di p.
Difficilmente un compilatore moderno promuoverebbe un `int *' non
utilizzato a `register int *'.
   In pratica, nella maggioranza dei casi, direi che, nell'attimo in
cui fai `*p = 123;', la sottile differenza sta tra l'avere un errore
mentre si sposta il valore di p su un registro, e l'averlo mentre si
scrive 123 in una locazione di memoria a caso.
   Per me il comitato di standardizzazione ha fatto un'ottima scelta a
bollarlo undefined, tanto non serve a niente, e puo` solo essere
nocivo.
-- 
S.L.

 Questo e' vietato per
Quindi secondo come interpreti lo standard non puoi nemmeno scrivere:
int i1, i2;
i1=i2;
anche in i1 e i2 semplici int e nemmeno puntatori?
Il fatto che non abbia senso farlo e' ovvio, ma davvero non capisco perche'
dovrebbe mai dare problemi?
Ad ogni modo ok. Tanto non ho mai scritto corbellerie del genere ;-)
Il C++ e' il mio linguaggio preferito, pero' ammettiamo che lo standard e'
spesso (magari non in questo caso) non troppo chiaro e libero ad
interpretazioni. Non esiste inoltre una *vera* definizione formale del
linguaggio. Credo sia anche colpa del linguaggio stesso (e del suo standard)
se ci sono pochi (nessuno) compilatori che lo rispettano in pieno!
Ti ringrazio per la pazienza che hai avuto. Ci siamo quasi trovati del tutto
come opinioni (ho ancora qualche *minima* divergenza filosofica, ma non vale
la pena di approfondire credo).
Tommy

dovrebbe mai dare problemi?
   Magari su una macchina ebcdic, dove non tutte le disposizioni di
bit sono interi validi, una cosa del genere potrebbe dare problemi.
Sicuramente lo darebbe su alcune macchine, nel caso di float o double.
Be', magari non proprio nel caso `i1 = i2;', ma gia` un `i1 = i2 + 1;'
potrebbe funzionare (nel senso di andare in crash/eccezione).
-- 
S.L.

se ci sono pochi (nessuno) compilatori che lo rispettano in pieno!
Io credo che il problema principale sia la complessita'.
Riguardo alla definizione del linguaggio questa esiste...
ovviamente non e' perfetta e in alcuni punti secondo me
ci sono buchi filosofici grossi come una casa.
La situazione e' pero' da questo punto di vista migliore
che con ad esempio Java.
Riguardo ai compilatori secondo me le cose ora non vanno
tremendamente male; basta stare alla larga dagli angoli
bui del linguaggio (qualche anno fa la situazione era
diversa). Chiaro anche oggi che se cominci a farti ad
esempio megapippe mentali di metaprogrammazione template
usando uno strumento ampiamente al di la' di quello per
cui era stato originariamente pensato allora le grane te
le stai cercando tu. Pian piano i compilatori arriveranno
anche li' ... ma per ora qualche problema c'e'.
Andrea

Scusate ancora una  piccola cosuccia:
Avete detto che il puntatore all'elemento successivo ad un array e il
puntatore null sono validi.
Del puntatore all'elemento precedente all'array invece non se ne parla
giusto?
Nella mia piccola reimplementazione di vector ho un reverse iterator,
vector.rbegin punta all'ultimo elemento, vector.rend *dovrebbe* puntare al
elemento precedente dell'inizio. Questo potrebbe essere un puntatore *non
valido* oltre che *non dereferenziabile*?

 
Perchè scusa? Se io faccio char *pippo=10; è un errore?
E quando fai char *prova=NULL; oppure 0?

 E quando fai char *prova=NULL; oppure 0?
NULL (oppure 0) e` un puntatore valido.
       Mauro

 *non valido* oltre che *non dereferenziabile*?
Lo č senz'altro, a meno che tu non abbia appositamente allocato uno "slot" in
piů all'inizio.
Ti suggerisco di dare uno sguardo all'implementazione di
std::reverse_iterator<>.
Max