Newsgroup: it.comp.lang.c
Titolo: Puntatori ed array

Dato che (per il momento) non ho impegni di lavoro strabilianti, mi
sono messo a ripassare un po' di programmazione "lean & mean".
La mia documentazione (Linux Application Development - Johnson/Troan)
riporta esplicitamente:
"to directly access the environment variables, the char *environ[]
array is provided".
Problema, se io faccio la cosa seguente:
extern char *environ[];
char *c;
int i=0;
while( (c=environ[i]) != NULL ) {
	printf("%s\n",c);
	i++;
}
ottengo schifezze, se invece faccio:
extern char **environ; /* << WATCH IT! */
char *c;
int i=0;
while( (c=environ[i]) != NULL ) {
	printf("%s\n",c);
	i++;
}
ottengo esattamente cio' che dovrei ottenere... Pero' io sapevo
che le notazioni char *xxxx[] e char **xxxxx dovrebbero essere
la stessa cosa...
Qualcuno mi illumina?
Davide

ottengo esattamente cio' che dovrei ottenere...
Le due notazioni sono equivalenti.
Difatti, il seguente codice :
#include <stdio.h>
char *env[] = { "ciao", "a", "tutti" } ;
char **env2 = env ;
int main()
{
    int i ;
    for (i = 0 ; i < 3 ; i++ )
    {
        printf( "%s = %s\n", env[i], env2[i] ) ;
    }
    return 0 ;
}
da' i risultati voluti con C++ Builder 5.0 Enterprise.
Prova a scrivere :
extern char *environ[] ;
char** envp = environ ;
e prova a visualizzare gli elementi usando quest'ultimo.
Vedi qual e' il risultato.
--
Andrea Laforgia (ICQ 87230455)
http://www.geocities.com/AndreaLaforgia
"ci sono tre tipi di persone nel mondo : quelli che
 sono bravi in matematica e quelli che non lo sono."

 Vedi qual e' il risultato.
Eccoti il risultato:
[davide@dbianchi test]$ prova
 o¿‘¿›¿¦¿´¿Ð¿¿¿ etc.etc.
'prova' e' il nome del programma...
se invece uso extern char **environ; ottengo:
[davide@dbianchi test]$ prova
LESSOPEN=|/usr/bin/lesspipe.sh %s
USERNAME=
COLORTERM=
HISTSIZE=1000
etc. etc.
Non mi pare che il gcc 2.91.66 abbia dei bachi simili...
Altre idee?
Davide

Dire
   extern char *environ[];
e dire
   extern char **environ;
NON e' assolutamente la stessa cosa. In particolare il man
di "environ" sulla mia installazione linux riporta questa
seconda forma (che giustamente hai verificato funzionare).
Dicendo "extern char *environ[];" stai comunicando al compilatore
che esiste un *array* di puntatori a carattere e che l'indirizzo
di questo *array* e' ottenibile col simbolo globale "environ".
Quando invece dici "extern char **environ;" stai comunicando che
esiste un puntatore ad un puntatore a carattere (oppure un
puntatore ad un array di puntatori a carattere: in C non c'e'
differenza fra un puntatore ad un elemento oppure un puntatore ad
un array di elementi) e che l'indirizzo di questo *puntatore* e'
ottenibile col simbolo globale "environ".
In pratica la forma "extern char **environ;" indica che deve essere
fatta una indirezione in piu'.
In altri contesti le forme "char *x[]" e "char **x" sono equivalenti
(ad esempio nei parametri di funzione: in C non e' ammesso passare
un array per valore... in un parametro dire array e dire puntatore
e' la stessa cosa) mentre nell'esempio scritto da Andrea il codice
funzionava solo perche' in molti contesti un array "decade"
nell'indirizzo del primo elemento:
   char *x[] = {"Andrea","Griffini",NULL};
   char **y=x;
nel secondo statement l'array x e' "decaduto" in un puntatore al
suo primo elemento. Ad esempio pero' sizeof(x) e sizeof(y) sono
diversi... x e y non sono per niente la stessa cosa.
Lo stesso "decadimento" avviene quando si scrive x[i] e quindi
questa espressione e' identica a y[i].
Se invece parliamo di parametri di funzione allora le cose cambiano:
  void foo(char *x[],char **y)
  {
     ...
  }
In questo caso x e' effettivamente un puntatore ad un puntatore a
carattere e un eventuale numero indicato fra parentesi e' totalmente
ininfluente (le dimensioni - ad esclusione della prima - contano solo
in caso di array multidimensionali perche' in questo caso
caratterizzano il tipo di elemento puntato... il parametro pero' resta
un puntatore e non un array). In particolare in questo caso risulta
anche che sizeof(x) e sizeof(y) sono identici.
HTH
Andrea
-- 
Posted from 
HA-server-a.cs.interbusiness.it
 [151.99.250.31] 
via Mailgate.ORG Server - 
http://www.Mailgate.ORG

 In altri contesti le forme "char *x[]" e "char **x" sono equivalenti
Hai una qualche documentazione sotto mano per sta' cosa? cioe', quando
sono equivalenti e quando non lo sono ?
Davide

sono equivalenti e quando non lo sono ?
Sono equivalenti nei parameti di funzione, sia nei prototype di funzioni
che nella dichiarazione di puntatori a funzione:
    int (*f)(int x[]);
    int (*g)(int *x);
    f=g; /* Nessun problema, il tipo e' identico */
Non sono equivalenti in altri contesti (anche se la sintassi di uso
e' identica). Nel caso di extern...
   /* file1.c */
   int x[100];
   /* file2.c */
   extern int *x; /* ERRORE: la cosa non funzionera' correttamente */
Nel tuo caso c'e' solo la complicazione ulteriore che il dato contenuto
e' a sua volta un puntatore...
   /* file1.c */
   char *environA[100];
   char **environP;
   /* file2.c */
   extern char **environA; /* ERRATO */
   extern char *environP[]; /* ERRATO */
Nota che il compilatore non e' tenuto a segnalare errori di questo
tipo (e' difficile farlo senza vedere tutti i moduli contemporaneamente
e il linker non conosce i tipi associati agli indirizzi).
Per evitare problemi si usano gli include file... in questo modo se scrivi
   /* file1.h */
   extern char *environ[];
   /* file1.c */
   char **environ;
il compilatore si arrabbiera' segnalando l'incompatibilita' fra quando
dichiarato nel .h e quanto definito nel .c.
HTH
Andrea
PS: Ma perche' il primo pensiero e' la presenza di un bug nel
    compilatore ? Quello dovrebbe essere l'ultimo dei tuoi dubbi.


 wrote:
<ZAP>
ALT! STOP! BLOCCATUTT!
Ieri sera (io sono cocciuto) ho provato la stessa cosa su un'altra
macchina (il mio PC di casa)... be' funziona... in entrambi i casi,
sia che io dichiari
char *environ[];
sia
char **environ;
ottengo risultati corretti... questa mattina ho provato su un'altra
macchina ed ottengo risultati sbagliati... a questo punto penso che
il problema sia da qualche parte nelle librerie o similari... il guaio
e' che non ho idea di COSA possa dare simili guai... il gcc e' lo
stesso e (che io sappia) anche le librerie... bo', ci studiero' sopra...
Davide

stesso e (che io sappia) anche le librerie... bo', ci studiero' sopra...
Ok. Guardati la cosa con calma e poi fammi sapere (eventualmente
per email). Sono proprio curioso.
Andrea
--
Andrea Griffini, Programmer
http://www.netservers.com/~agriff

per ottenere "environ[i]".
No. L'operazione e' sempre la stessa.
Si converte environ in puntatore (se non lo e' gia') e gli si aggiunge
lo spiazzamento.
Esempio :
environ (array)
!
!
!
!-> 1000 [ char * ]
    1004 [ char * ]
    1008 [ char * ]
environ (ptr a ptr)
!
!
!
!-> 2000 [ char ** ] ---> 1000 [ char * ]
                          1004 [ char * ]
                          1008 [ char * ] 
Quando scrivo environ[1], il compilatore considera *(environ + 1),
quindi environ (nel caso di array decade) vale 1000, gli si aggiunge 1
e si raggiunge quindi il char* puntato in 1004.
Nel secondo caso, il compilatore considera *(environ + i), quindi
environ (che e' gia' puntatore) non decade, vale 2000, gli si aggiunge
1 e si raggiunge ugualmente la posizione 1004.


   char **y=x;
In TUTTI i contesti (tranne 3 casi specifici : argomento di sizeof e
di & e literal stringa di inizializzazione), un array DECADE nel
puntatore al suo primo elemento.
Dire extern char *environ[] ;
e 
dire extern char **environ ;
e' equivalente, se si usera' la notazione mediante indice per accedere
agli elementi.


diverse "extern char *environ[];" e "extern char **environ;".
Stai confondendo.
Dipende che cosa intendi per "fare la stessa cosa".
Accedere agli elementi utilizzando la notazione con parentesi quadre
conduce ai medesimi risultati.
La condizione generale e' :
extern T environ[] ;
extern T *environ  ;
Sono *perfettamente equivalenti* da questo punto di vista.
Che poi da qualche parte nel codice ci sia environ definito come array
o come puntatore, influira' SOLO su calcoli tipo "sizeof" e nel tipo
di puntatore risultante come indirizzo dell'oggetto (uso di &).


di puntatore risultante come indirizzo dell'oggetto (uso di &).
Questa cosa te l'ha detta Schildt ? ;)
Se environ e' dichiarato come array di puntatori a char allora
e' *necessario* usare "extern char *environ[];". Se environ e'
dichiarato come puntatore a puntatore allora e' *necessario*
usare "extern char **environ;".
L'OP aveva le idee confuse su questa "equivalenza" (che non esiste),
sospetto che la ragione e' che si vede con pari frequenza:
    int main(int argc,char **argv);
e
    int main(int argc,char *argv[]);
In questo caso pero' le cose sono effettivamente equivalenti
ma solo perche' stiamo parlando di parametri di funzione.
Questa e' una asimmetria che a me non piace moltissimo... ma
c'e' e bisogna comprenderla.
Con "extern" non si tratta affatto di dichiarazioni equivalenti.
HTH


lo spiazzamento.
Qui ti sei risposto da solo... cosa intendi con "se non lo e' gia'" ?
INDOVINELLO:
  Come fa il compilatore a sapere se deve compilare
  o meno il codice necessario all'indirezione aggiuntiva ?
RISPOSTA:
  In base alla dichiarazione di extern: "extern char **environ;"
  indichera' che e' richiesta una indirezione in piu' che con
  "extern char *environ[];"


agli elementi.
BZZZZT. Risposta errata.
Ci sono nel linguaggio "C" casi di equivalenza (questi casi
pero' non hanno a che fare con "extern"); ad esempio scrivere:
   void foo(char **env);
e' assolutamente identico a scrivere
   void foo(char *env[]);
La ragione di questa "stranezza" e' che ad una funzione gli
array non vengono comunque mai passati. Viene sempre passato
solo un puntatore. L'aver introdotto l'equivalenza permette
di scrivere codice piu' leggibile in casi come:
   void InvertMat(double matrix[4][4]);
che e' equivalente al meno leggibile
   void InvertMat(double (*matrix)[4]);
All'interno della funzione in *entrambi* i casi sizeof
restituisce per matrix la dimensione di un "double *".
HTH


usare "extern char **environ;".
Si', e' vero.


Ci scommetteresti un giro con la tua morosa ?
Eh eh... no e' troppo preziosa per farlo...
Me la gioco alla roulette... ;)


char ** allora sono guai.
Si, hai ragione, questo caso particolare genera confusione perche' si
sta cercando di accedere alla stessa porzione di memoria usando due
costrutti semanticamente diversi.


Con "extern" non si tratta affatto di dichiarazioni equivalenti.
Hai ragione.
In realta' la mia confusione nasce dal fatto che non c'e' alcun 
decadimento dell'array in questo caso.
Semplicemente lo stesso oggetto viene trattato con 2 tipi differenti e
questo conduce a risultati impredicibili.
Cioe', se io dichiaro :
char *arr[] = { "Andrea", "Laforgia" } ;
Avro' in memoria una situazione del tipo :
     |  ADDRESS   |    VALUE    |
     |------------|-------------|
     |  4020C4    |   4020CC    |----> "Andrea"
     |  4020C8    |   4020D3    |----> "Laforgia"
Se, invece, ci accedo con un puntatore a puntatore a char, ottengo che
il valore del puntatore non e' 4020C4 (come ci si aspetta nel caso in
cui si assegni l'array (che quindi decade) al puntatore), ma il valore
dell'oggetto puntato, cioe' 4020CC e questo e' palesemente errato.
Difatti, volendo usare il puntatore a puntatore, il risultato corretto
si ha scrivendo :
extern char *environ[] ;
char **ptr_environ = environ ;
in questo caso, forzando il decadimento dell'array, si ha che
l'accesso a ptr_environ[0] restituisce correttamente l'elemento
contenuto nell'array.
Grazie per i chiarimenti e scusate per la confusione ;)


 Grazie per i chiarimenti e scusate per la confusione ;)
Scherzi? E' proprio per chiarire cose come queste che seguo il
newsgroup.
EMG

 la stessa cosa...
Nel primo caso il _simbolo_ environ si riferisce ad un indirizzo _statico_ 
e che quindi *dovrebbe* ( in teoria ) essere risolto a compile-time; nel 
secondo caso il _simbolo_ environ si riferisce ad una variabile che 
contiene l'indirizzo di un blocco di memoria.
Il problema e' che l'indirizzo del blocco delle variabili di ambiente viene 
ridimensionato a run time, per cui il 1o approccio falla. 
Questa comunque e' una cosa molto OS/compiler dependent, per cui possono 
giustificarsi comportamenti diversi.
Credo che sipossa concludere che la 2a forma e' quella corretta, mentre la 
1a A VOLTE non funziona ( ripeto OS/compiler dependent )

 1a A VOLTE non funziona ( ripeto OS/compiler dependent )
Ritiro quello che ho detto...