Newsgroup: it.comp.lang.c
Titolo: Utilità dei puntatori

Scusate, ma potreste spiegarmi l'esatta utilità dei puntatori?
So che essi contengono l'indirizzo della variabile alla quale vengono
puntati.
Allora perchè, mi domando, ad esempio nell'intestazione main, argv[] viene
dichiarato come un puntatore, col classico asterisco prima del nome di
variabile?
Grazie.
--
Sparviere
mail: 
spar...@ngi.it
 / icq: 57656305
web: 
http://novalis.too.it

variabile?
argv e' un vettore di puntatori.
La sintassi del C e' tale per cui :
int *a[]
      ^ e' un vettore...
    ^ ...di puntatori
-- 
Andrea Laforgia - walkietalkie(at)
libero.it
Spostare "Windows" nel cestino ?

nome di variabile?
argv[] è un array di puntatori, ogni suo elemento punta a una stringa
(ovvero un array di caratteri). 

Permettono di fare cose ben piu` divertenti di quelle descritte fino ad 
ora dai nostri colleghi. Il seguente codice C cambia il punto di ritorno 
  e il frame pointer di ritorno della funzione ret con quello della 
funzione che la chiama, cioe` la funzione copia. Questo permette di 
eseguire direttamente il codice del main senza ripassare (al ritorno da 
ret) per la funzione copia. Questo lo puoi ben vedere dal fatto che la 
printf della copia, che sta` dopo la chiamata a ret, non viene stampata 
a video.
#include <stdio.h>
void ret(int*);
void copia(int a){
         ret(&a-1);     /* passa a "ret" il  puntatore alla locazione
                          di memoria dello stack che memorizza
                       il punto di ritorno dalla "copia" */
         printf("Non viene stampata");
}
void ret(int *rtn){
         *(&rtn-1) = *rtn;  /* sostituisce il punto di ritrno dalla "ret" 			 
      con il punto di ritorno dalla "copia"  */
         *(&rtn-2) = *(--rtn);     /* sostituisce il frame pointer di 		
				  ritorno dalla "ret" con il frame
				pointer di ritorno dalla "copia",
                                cioe` con il frame pointer del main */
}
int main(){
         copia(20);
         printf("viene stampato solo questo\n");
}
questo tipo di cose sono alla base di molti attacchi hacker, virus e crack
Andrea
Studente di ing. Informatica (universita` di Benevento)
PS io ho usato il compilatore gcc, sistema operativo linux/kernel 2.4.16


         *(&rtn-2) = *(--rtn);
            ^^^^^^      ^^^^^
Illegali
Il codice che hai postato e' illegale (dove per "illegale"
si intende che, andando contro le indicazioni dello standard
del linguaggio, puo' portare a dei comportamenti
indefiniti), perche' utilizza l'aritmetica dei puntatori,
oltre il limite consentito.  L'aritmetica dei puntatori e'
definita solo per la zona di memoria allocata per il
puntatore (vedi le FAQ di comp.lang.c per maggiori
riferimenti).
-- 
Andrea Laforgia - walkietalkie(at)
libero.it
"Meglio cieco che negro" (Stevie Wonder)

Sei proprio sicuro di quello che dici!!! Il compilatore gcc (gnu cc), 
che ho usato io, segue lo standard C perfettamente, ed e` un signor 
compilatore tanto che e` il compilatore usato per compilare il kernel di 
linux. Sul fatto che il comportamento e` indefinito ti do` parzialmente 
raggione perche` dipende dalla macchina su cui si usa, infatti questo 
esempio funziona sicuramente su architetture IA32 e compatibili 
(386,486,Pentium e successivi piu` i vari cloni). Ed inoltre, dove sta` 
scritto che i puntatori possono  essere usati solo per qualche aria 
della memoria. Se leggi su "ANSI C" (e sottolineo ANSI, per i 
principianti la ANSI e` la societa` americana che ha standardizzato il 
C) il libro scritto da Kernighan uno dei padri del C, c`e` scritto che 
non c`e` limite all`uso dei puntatori, chi pone dei limiti (e questo non 
sta` scritto nel libro)all`uso dei puntatori sono i sistemi operativi 
moderni (al momento del run time, non alla compilazione)che danno dei 
"segmentation fault" quando un programma cerca di scrivere e/o leggere 
al di fuori della memoria ad esso assegnata, nelmio esempio io non esco 
dalla memoria associata al mio programma ma lavoro sullo stack del 
programma che si trova nella memoria assegnata dal sistema operativo al 
programma. Sottolineo sistemi operativi moderni perche` se usi il 
vecchio DOS (che non fa` nessun controllo sull`utilizzo memoria da parte 
dei programmi) e cerchi di scrivere sulla memoria di altri programmi 
compreso il sistema operativo non ti da nessun "segmentation fault" e 
puoi tranquillamente mandare in crash il sistema. Se quello che dici tu 
fosse vero all`ora perche` tutti i libri (seri) sul C consigliano sempre 
di fare attenzione a non sfondare gli array? Sai benissimo che il nome 
dell`array non e` altro che il puntatore al primo elemento dell`array. 
Supponiamo di avere un array cosi` definito : int Vett[10], sai 
benissimo che posso scrivere tranquillamente Vett[11] = 1000; al momento 
della compilazione il compilatore(degno di questo nome) non mi da` 
problemi, al massimo al momento del run time potrei causare un 
"segmentation fault" da parte del sistema operativo. Certo se non sai 
quello che fai puoi causare anche dei mal funzionamenti del tuo stesso 
programma, ma nel mio caso io so` quello che faccio anche perche` 
l`esempio che ti ho riportato e` un esercizio dato in uno dei miei corsi 
di laurea!!! Vorrei dare un consiglio a chi in questa mailing chiedeva 
di un libro su C, compratevi ANSI C jackson libri, possibilmente la 
versione inglese, perche` e` stato scritto da Kernighan che oltre ad 
essere uno dei padri del C e` anche uno che sa` quello che dice.
Giusto per curiosita` ti dico che a Kernighan risale il primo programma 
che stampa "Hello World" che poi e` diventanto il primo esempio che 
tutti noi programmatori abbiamo scritto e scriviamo ogni volta che 
impariamo un nuovo linguaggio.
Spero di essere stato chiaro oltre che prolisso.
Andrea
Studente di ing. Informatica (universita` di Benevento)


impariamo un nuovo linguaggio.
Grazie di avercelo detto ;)


Mi rendo conto che avevo esagerato perche`, facendomi prendere dalla 
foga, sono solito in cattedra, posto che evidentemente non mi compete. 
Per quanto riguarda il comportamento non definito di un certo uso dei 
puntatori sono d`accordo con te e te ne ho gia` dato conto nella 
precedente discussione. Pero` vorrei farti notare che io ho scritto alla 
fine della prima mail che ho usato il gcc come compilatore e linux come 
s.o. mi ero dimenticato(errore grave lo ammetto) di dire che il 
l`architettura era la IA32(per i principianti Pentium e compagnia)cosa 
che ho specificato nella seconda mail,e ti posso assicurare che con 
queste carattetristiche quello che ho scritto io e` lecito(se vuoi posso 
farti dei riferimenti a libri), dopo questo lo scopo del mio primo 
messaggio non era quello di chiarire l`uso dei puntatori in C ma di far 
vedere che esistono altri modi +/- "legali" per usarli, modi che 
(secondo il mio modesto parere) sono piu` divertenti di quelli 
"classici". Cmq questa e` una mia opinione che puo` essere +/- 
condivisa. Mi ha fatto piacere discutere cosi` accanitamente alla mia 
prima visita su questa mailing.
Grazie
Andrea


farti dei riferimenti a libri), 
Non c'e' bisogno dei riferimenti, se mi citi un particolare
compilatore, con un particolare sistema operativo, su una
particolare macchina, sono d'accordo con te.  Lo standard
non afferma che i comportamenti indefiniti debbano restare
indefiniti.  Ci sono piattaforme che danno un significato a
questi comportamenti (vedi il caso di fflush(stdin) che
secondo standard e' indefinito, ma con qualche compilatore
funziona).  E' che si stava parlando dell'utilita' dei
puntatori, in generale.  Ammetto, comunque, convenendo con
te, che la potenza dei puntatori e' tale da consentire di
fare operazioni molto sofisticate.


linux. Sul fatto che il comportamento e` indefinito ti do` parzialmente 
Vorrei aggiungere una nota... non riguarda direttamente i puntatori,
ma quello che dice Andrea (non Laforgia).
Il GCC supporta fedelmente lo standard, ma questo non vuol mica dire
che non possa implementare cose in piu' (ovviamente non in contrasto
con lo standard).
Se il GCC non supporta qualcosa puoi ragionevolmente dedurre che lo
standard non lo supporti; non vale pero' il contrario.
Es. la snprintf(): da parecchio in uso dal GCC, standard solo dal c99.
Attenzione che e' molto pericoloso procedere per tentativi e dedurre
lo standard dai tentativi.
Molti (tra cui io) sono caduti miseramente sulla funzione fflush(),
usata sui buffer di input. Sotto dos e sotto solaris(GCC, non il CC)
funzionava, poi sotto linux non funzionava (stiamo parlando sempre del
GCC). Perche' lo standard dice che il comportamento della fflush()
sugli stream di input non e' definito.
Questo potrebbe anche significare che 999 compilatori su 1000 la
implementano in modo che funzioni, ma 1 potrebbe associare alla
fflush(stdin) il rituale di evocazione dei demoni nasali :P e sarebbe
perfettamente legittimato a farlo perche' lo standard dice "fate come
vi pare".
Mox (viva le FM :P )

"segmentation fault" da parte del sistema operativo. 
il fatto che a pag.130 del k&r2 si trova
"... (Esiste un'eccezione: l'indirizzo del primo elemento dopo la fine
di un vettore puň essere utilizzato nell'aritmetica dei puntatori)"
significa dire che Vett[11]=1000 sě puň fare per lo standard?

Mi fa` piacere aver cousato un cosi` accesa discussione sullo standard 
C, pero` le mie intenzioni erano molto piu` modeste. Cmq volevo 
rispondere all`amico Mox che nella creazione del mio esempio non ho 
proceduto per tentativi, anche perche` sarebbe stata una vera follia, ma 
mi sono dovuto studiare l`assembly prodotto dal gcc e debbuggare piu` 
volte per vedere l`uso che il mio sistema faceva dello stack e della 
memoria, oltre ad un ovvio supporo "cartaceo". Sul fatto che sulle cose 
non definite dallo standard ognuno (compilatori, librerie ...) faccia un 
  po` a cacchi suoi sono d`accordo con voi, pero`, lo ribadisco, fin 
dalla mia prima mail la mia intenzione  era quella di far vedere un uso 
dei diverso da quello tradizionale puntatori (non standard e/o +/- 
"illegale", sono d`accordo con voi) che quel esempio funziona solo su un 
particolare sistema l`ho avevo gia` scritto(in modo non compleo e forse 
non abbastanza evidenziato) alla fine della mia prima mail.
Andrea


significa dire che Vett[11]=1000 sě puň fare per lo standard?
No, significa che Vett[10] si puo' fare.


 No, significa che Vett[10] si puo' fare.
neanche!
ho imparato tempo fa da te, che si puo' fare solo &vett[10] o (vett+10)
ciao
donCiccio

ho imparato tempo fa da te, che si puo' fare solo &vett[10] o (vett+10)
A me risulta che si possa fare dato che lo fa anche il K&R
-- 
Lawrence "Rhymes" Oluyede (
rhy...@myself.com
)
"Per cambiare, per diventare un'altra
cosa dobbiamo prima sapere cosa siamo"
(Bruce Lee)

 No, significa che Vett[10] si puo' fare.
Veramente significa che si puņ fare Vett[9]
Maurizio.

ho imparato tempo fa da te, che si puo' fare solo &vett[10] o (vett+10)
Non vedo dove sia il problema in cio' che ho detto:
*(vett+10) e' equivalente a vett[10].
Indirizzare vett[10] non produce un comportamento
indefinito, secondo lo standard.


Veramente significa che si può fare Vett[9]
No.
"l'indirizzo del primo elemento DOPO LA FINE
di un vettore può essere utilizzato nell'aritmetica dei
puntatori"
Se il vettore e':
int Vett[10];
Il primo elemento dopo la fine e' Vett[10], perche' il
vettore va da Vett[0] a Vett[9].


 puntatori"
Scusa, ma dove sta scritto tutto questo? E in ogni caso mica vuol dire che
lo spazio che ha come indirizzo il primo elemento dopo la fine possa essere
utilizzato dal vettore.
Come si sipega allora il comportamento di questo programma?
#include <stdio.h>
void main(void)
{
int Vett[10];
int i = 10;
printf("%d\n", Vett[10]);
Vett[10] = 32;
printf("%d\n", i);
}
L'istruzione printf("%d\n", Vett[10]) dà come risultato 10. Questo sta ad
indicare che Vett[10] usa lo stesso spazio della variabile i.
L'istruzione printf("%d\n", i) invece dà come risultato 32.
Dimmi se è un'anomalia del mio compilatore.
Maurizio.

indicare che Vett[10] usa lo stesso spazio della variabile i.
Lo standard dice che usare (vett+10) o *(vett+10) non
produce un comportamento indefinito (a prescindere da cosa
ci sia dopo gli elementi del vettore), mentre (vett-1) o
*(vett-1) si'.


 Difatti, nei miei messaggi non ho detto questo.
Se non dici questo vuol dire che non si puņ usare Vett[10], al massimo puņ
essere usato &Vett[10].
L'esempio dimostrava solo che Vett[10] non esiste.
Maurizio.

essere usato &Vett[10].
Io non ho detto che Vett[10] si puo' usare come elemento del
vettore, si sta parlando di comportamenti indefiniti e
l'aritmetica degli indirizzi non genera comportamenti
indefiniti se l'elemento coinvolto e' il primo dopo l'ultimo
del vettore.  Questo significa che un'eventuale assegnazione
non genera comportamento indefinito.  Gli effetti
collaterali, poi, ottenibili per la sovrascrittura della
memoria, costituiscono un altro discorso.


 
Sparviere dopo tutto questo parlare permettimi di dirti la mia, altrimenti
seguo il NG per niente...eh eh
i puntatori sono delle armi potentissime, altro che bomba nucleare,
questi strumenti ti permetto di rendere il programma VELOCISSIMO proprio
per la gestione della posizione reale del dato da manipolare,
come dicevano i compagni, quando passi un dato ad una funzione, questo dato
viene copiato, perciò quello che manipoli nella funzione, è una copia del
dato che hai passato...questo non succede quando usi i puntatori...
Adesso, un conto è parlare di 1 carattere, ma pensa a stringhe un po'
lunghette, o peggio, ad un array di strutture di dati anagrafici con
dimensione di 300 byte l'una...ops...copiarlo o passare la prima posizione
dell'array non è + la stessa cosa, non ti pare?
Ecco la VERA potenza dei puntatori...
ciao duido W il C...WC..;)
-- 
Posted via Mailgate.ORG Server - 
http://www.Mailgate.ORG

Sparviere wrote:

questa e' una domanda che non puo' essere risolta con una email. Prima di 
tutto, non capisci l'utilita' dei puntatori finche' non ne hai bisogno. 
*argv[] viene dichiarato in questo modo in quanto e' un array di puntatori. 
Questo perche' contiene, sottoforma di array, gli argomenti passati al 
programma. Sono puntatori perche' gli argomenti possono essere di vari 
dimensioni, quindi spettera' a qualcun'altro (la shell, il kernel o chi per 
lui) allocare la memoria necessaria per contenere gli argomenti, e poi far 
puntare il tuo array a queste aree di memoria. Inoltre i puntatori sono 
molto comodi per poter manipolare stringhe, alloracare dinamicamente la 
memoria. Puoi pensare ad un puntatore come ad un cursore che puo' essere 
spostato lungo la stringa, e puo' quindi leggere e modificare il dato a cui 
sta puntando, e questo molto semplicemente. 
char stringa[] = "cippalippa";
char *cursore;
cursore = &stringa[3];
*cursore = 'P';
ed otterrai la stringa "cipPalippa" per esempio
poi fai cursore++ e *cursore='A';
ed ottieni "cipPAlippa"
anche le stesse stringhe possono essere pensate come puntatori, ad esempio:
cursore = &stringa[3] equivale a cursore = stringa+3
insomma, c'e' parecchio su cui pensare.
-- 
 Claudio Martella
 Centro Informatico di Ateneo - CCR
 Politecnico di Milano
... Powered By Debian/GNU Linux

 indefinito, secondo lo standard.
eccome no!
l'array e' stato definito lungo 10, non puoi leggere/scrivere
nell'undicesima locazione, solo usarla come indirizzo valido per i conti
con i pointer.
ciao
donCiccio

 con i pointer.
Ribadisco per l'ennesima volta: INDIRIZZARE.
Nessuno sta dicendo che si puo' leggere e scrivere in 
quella posizione.



esatto! questo l'avevo capito.
Ma vett[10] non e' un indirizzo, e' un valore intero, cioe' il contenuto
della undicesima locazione, e dire che si puo' usare vett[10] (come tu
hai scritto nei post precedenti) e' fuorviante (anzi errato).
Perche non specifichi meglio dicendo che si puo' usare &vett[10]?
oppure (vett+10) (SENZA asterisco davanti)?
ciao
donCiccio