Newsgroup: it.comp.lang.c
Titolo: Accodare puntatore ad array di puntatori

Ciao a tutti, sto sviluppando una semplice applicazione per il dump
degli header elf in linux. Non ho voglia di utilizzare librerie e cose
del genere, perchè non è il risultato che mi importa ma il fatto di
esserci riuscito da solo :P
Ora... senza addentrarmi nel discorso, ho bisogno di avere un array di
puntatori dinamico, ma, visto che in c queste cose non esistono, ho
cercato di scrivere una semplice funzione che mi permetta di ottenere
lo stesso risultato.. Solo che non capisco dove stia sbagliando! Mi va
in segfault.
void *append_pointer(void *main, int size, void *to_append) {
	void *new_p = calloc(sizeof(void *), size+1);
	memcpy(new_p, main, size);
	memcpy(new_p+size+(sizeof(void *)+1), to_append, sizeof(void *));
	free(main);
	return new_p;
}
Args:
main = puntatore principale all'array di puntatori
size = dimensione dell'array iniziale
to_append = puntatore da aggiungere alla lista
Non programmo da molto in C, potete aiutarmi a trovare l'errore?


Il problema base è che usi l'aritmetica dei puntatori con i void*.
Se ragioni a bytes ti conviene usare degli unsigned char * (oppure
meno elegantemente fare dei cast a unsigned char *).
In ogni caso due commenti:
- non serve usare calloc() e memcpy(), basta usare realloc().
- non è mai una buona idea usare forme del tipo sizeof(tipo). Se vuoi
la dimensione della variabile *p, usa sizeof(p).
Ciao!
P.S. Una soluzione alternativa ad un array variabile è una lista. Per
l'header elf (a suo tempo ho scritto anch'io un loader) direi che
nessuno dei due è necessario, in quanto puoi sapere prima di allocare
il vettore quanto ti servirà grosso, leggendo prima il blocco che ti
interessa.

 Non programmo da molto in C, potete aiutarmi a trovare l'errore?
Dovresti spiegare la logica alla base del tuo codice.
Mi sono permesso di riscriverti il codice:
#include <stdio.h>
#include <stdlib.h>
typedef void T;
void append_pointer(T ***list_ptr, size_t *list_count_ptr, T *ptr) {
    T **new_list = realloc(*list_ptr, (*list_count_ptr+1) * sizeof
*new_list);
    if (new_list != 0) {
       *list_ptr = new_list;
       (*list_ptr)[(*list_count_ptr)++] = ptr;
    }
}
void dump_ptr_list(T **list, size_t list_count) {
    size_t i=0;
    puts("lista puntatori:");
    while (i<list_count) {
        printf("p[%d] = %pn", i, list[i]);
        i++;
    }
}
int main() {
    T **ptr_list = 0;
    size_t ptr_list_count = 0;
    T *p1 = malloc(sizeof *p1);
    T *p2 = malloc(sizeof *p2);
    T *p3 = malloc(sizeof *p3);
    append_pointer(&ptr_list, &ptr_list_count, p1);
    append_pointer(&ptr_list, &ptr_list_count, p2);
    append_pointer(&ptr_list, &ptr_list_count, p3);
    dump_ptr_list(ptr_list, ptr_list_count);
    return 0;
}
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 la dimensione della variabile *p, usa sizeof(p).
No: sizeof *p



No. sizeof(*p) se vuoi la dimensione del tipo puntato da p.
char *p;
sizeof(p) /* 4, sulla mia macchina */
sizeof(*p) /* 1 */

 sizeof(*p) /* 1 */
Appunto. Se vuoi allocare memoria per un puntatore devi considerare la
grandezza del tipo puntato, non del puntatore stesso.
Dato un tipo T:
T *p;
L'allocazione va fatta con sizeof *p oppure con sizeof T, NON con sizeof p:
p = malloc( 10 * sizeof *p ); /* corretto */
p = malloc( 10 * sizeof p );  /* errato */


 L'allocazione va fatta con sizeof *p oppure con sizeof T, NON con sizeof p:
Errata corrige: sizeof (T)
L'uso senza parentesi � consentito solo per le variabili:
p = malloc( 10 * sizeof *p );  /* corretto */
p = malloc( 10 * sizeof (T) ); /* corretto */


Grazie a tutti per le risposte. Grazie Andrea Laforgia per il codice
riscritto, avrò modo di studiarci su!
Mi serve questo array dinamico per gestire i nomi delle sezioni, per
farlo pulito volevo suddividere la string table ( del tipo: abc\0efg
\0\0 ) in un array ( del tipo: [0]->abc\0 [1]->efg\0 );
Io sapevo che sizeof ti ritorna la dimensione del tipo di dato... E
visto che stiamo parlando di un array di puntatori ero convinto anche
io di dover allocare 4byte ( giro su x32 ) per ogni puntatore... Non
ho ben capito del perchè dovrei invece usare la dimensione del tipo
puntato.
Non sapevo inoltre che la logica del puntatori comuni fosse diversa da
quella dei void, pensavo fosse uguale per tutti, vedrò di leggere
qualcosa a riguardo. Grazie ancora per gli interventi

 puntato.
Allocare memoria significa riservare spazio per un certo numero di oggetti
di un certo tipo. Se vuoi riservare spazio per 10 elefanti devi calcolare
la dimensione come 10 * sizeof(elefante). Lo stesso vale per qualsiasi
altra cosa, puntatori compresi. 


 qualcosa a riguardo. Grazie ancora per gli interventi
char *cp;
int *ip;
/* .. */
(cp + 1)  /* la locazione "fisica" alla quale cp punta viene aumenta
di un byte */
(ip + 1) /* la locazione "fisica" alla quale ip punta viene aumenta di
sizeof(int), ovvero il +1 ti sposta dello spazio necessario per
l'elemento successivo) */
E' logico quando ci pensi bene :)
Ciao!

 punta non ti serve.
Non � cos�. Che cosa significa "salvare"? Un puntatore � una variabile
come tutte le altre e la sua dimensione, ovvero quanto occupa in memoria
la variabile stessa, NON C'ENTRA NULLA con la dimensione della memoria da
allocare e a cui quella variabile punter�. Per allocare memoria per un
puntatore SI DEVE usare la dimensione del tipo puntato, NON quella del
puntatore stesso. Queste sono le basi della programmazione con i puntatori
in C e bisognerebbe avercele chiare in mente prima di avventurarsi.
Se si ha: 
int *p; 
e si vuole allocare memoria, l'unit� da considerare per l'allocazione � la
dimensione del tipo int, ovvero del tipo puntato da p. Usare sizeof p �
errato.


 del genere, perch� non � il risultato che mi importa ma il fatto di


 ma...usare una realloc?
L'ho sostituito, mi ero dimenticato di quella funzione!
Ora comunque il programma gira, grazie ai vostri suggerimenti. Anche
grazie alle info sui puntatori void, ora ho capito a quando ti
riferivi per il fatto dei byte ed al fatto di usare gli unsigned char.
Il fatto è che... Avevo cercato di implementarlo prima ma mi deva un
errore ( Dereferencing void pointer bla bla bla ) ma era perchè lo
facevo in modo sbagliato, seguendo lo script di andrea ho capito come
cambiare il mio.
Ho invece più confusione sul fatto di allocare i puntatori... Facendo
come dicevo io ( allocando 4 byte nel mio caso ) funziona... A quanto
ne so io:
unsigned char -> 1 byte
unsigned char * -> 4 byte ( 32/8 = 4 )
unsigned int -> 4 byte
unsigned int * -> 4 byte
etc etc... Perciò se io devo allocare un PUNTATORE, e non il valore
puntato da esso, suppongo che si debbano allocare 4 byte ( 32bit )...
Non sapevo inoltre che effettivamente i puntatori possano avere
dimensioni diverse o.O
Come mai? Se io mi devo riferire ad un indirizzo virtuale devo usare
32bit se sono su x32 e 64 se giro su x64 o no? Come posso usare
dimensioni diverse?

 puntato da esso, suppongo che si debbano allocare 4 byte ( 32bit )...
La confusione nasce dal fatto che quando si parla di "allocare un
puntatore" ci si riferisce al riservare memoria il cui indirizzo iniziale
verr� messo nella variabile puntatore. Ma il discorso � sempre lo stesso:
T *p = malloc( 10 * sizeof *p );
Che T sia "int" o "void" o "int*" o "void*" o "void******" il discorso non
cambia.



Non hai capito il problema.
l'OP vuole allocare memoria *per il puntatore*, non "per ciò a cui
esso punterà".
Sta facendo bene e IMO lo stai anche un po' confondendo :)
Quello che vuole fare lui è, a partire da (mettiamo) un
void *a[10];
ovvero un puntatore di 10 void*, aggiungere spazio per un'altro
puntatore, in maniera che diventi di 11 elementi.
Deve aumentare lo spazio allocato di sizeof(void*).

 ovvero un puntatore di 10 void*
a � tutt'altro che un puntatore. Vedi che hai le idee confuse? io ho gi�
risolto il problema dell'OP postandogli il codice completo diverse ore fa.


 dimensioni diverse?
Sul fatto che un char* e un int* possano avere dimensioni diverse
lascia perdere: non è *mai* vero. Occorre dirlo per completezza, ma in
generale non esiste questo problema.
P.S. Nel caso esistesse (ovvero stai su una macchina in cui le
dimensioni sono diverse, macchina che personalmente non conosco)
sarebbe un problema nel caso tu faccessi qualcosa del tipo:
char *cp = /* qualcosa */
int *ip = (int*)cp;
char *cp2 = (char*)ip; /* <-- U.B. */
Le uniche conversioni "bidirezionali" tra puntatori definite sono
attraverso i void* (esistono apposta per evitare questo problema!).


Perfetto. Peccato che l'OP voleva scriverselo da solo e gli servivano
solo dei suggerimenti.
Sul codice niente da dire, a parte che vai fare un sizeof(void) che
non vedo cosa possa ritornare e perché.
T *p1 = malloc(sizeof *p1); /* con T definito come void */
(il mio compilatore infatti si stranisce non poco)

 non vedo cosa possa ritornare e perch�.
Su questo hai ragione. Ma d'altronde � sostituibile benissimo con una
dimensione qualsiasi.



ahaha :D sai, esistono le battute. E poi, mettiamola così, fino adesso
non lo è stato. Evidentemente il mio (scarso) umorismo comincia ad
esserlo.
Vabè - ormai siamo abbondantemente OT, lascio perdere ;-)

 strippato dal reply) mi sembrava semplice, chiaro e corretto.
Quello che hai scritto non � n� semplice, n� chiaro, n� corretto e ti ho
gi� spiegato il perch�. Hai detto pi� inesattezze e pi� di una volta. Io
non devo "strippare" niente dalle mie risposte, cerco di quotare in
maniera essenziale alla discussione. Metto un punto anche io alla
questione.


 già spiegato il perché. Hai detto più inesattezze e più di una volta.
Ammappela. Sei il primo che si lamenta. Sarò veramente io il problema?
E non t'offendere, che fa male alla salute. ;-)
Ciao!

Ciao Andrea, io ho letto e compreso il tuo codice, e ho applicato
quello che ho capito al mio problema, non ho riutilizzato il tuo
stesso codice, anche perché ho impostato la mia applicazione in modo
leggermente differente, cose di poco conto comunque.
Ad ogni modo:
void append_pointer(T ***list_ptr, size_t *list_count_ptr, T *ptr) {
    T **new_list = realloc(*list_ptr, (*list_count_ptr+1) * sizeof
*new_list);
    if (new_list != 0) {
       *list_ptr = new_list;
       (*list_ptr)[(*list_count_ptr)++] = ptr;
    }
}
Tu fai:
T **new_list = realloc... etc
Quindi new_list è un puntatore a un puntatore ( che in questo caso è
il primo di un array di puntatori )
Il parametro size del realloc:
(*list_count_ptr+1) * sizeof *new_list);
Ora... Se **new_list era un puntatore ad un puntatore, il *new_list
nel sizeof non è un puntatore sempre?
Quindi non stai applicando la stessa cosa che diceva FtM?
Probabilmente vi siete spiegati in modo diverso, ma a me il concetto
sembra lo stesso, dal basso della mia ignoranza.
Un'altra cosa... parlando del Kerninghan & Ritchie, fare il cast del
valore restituito dal malloc è male? :D L'ho sempre visto fare

 E non t'offendere, che fa male alla salute. ;-)
Uh? offendermi? per cosa? mi sembra d'averti risposto sul piano tecnico.
Ciao.


 valore restituito dal malloc è male? :D L'ho sempre visto fare
Non *è male*, però è sconsigliabile.
Il motivo dipende semplicemente dal fatto che senza il cast il
compilatore darebbe errore in caso la malloc() non fosse definita,
ovvero scordandosi di includere la <stdlib.h> o cose simili. Il cast
non è comunque necessario per la conversione tra void* (il ritorno
della malloc()) e un altro tipo di puntatore.
Si vede fare spesso perché da quanto ne so in C++ questa conversione
non è invece possibile senza un cast (ma per il c++ meglio chiedere ad
un altro NG).
Ciao!

 valore restituito dal malloc � male? :D L'ho sempre visto fare
E hai sempre visto fare male. Castare il valore di ritorno di malloc() �
inutile e potenzialmente dannoso (per esempio, dimenticando di includere
stdlib.h). Qui trovi un riferimento a quanto sto dicendo:
http://c-faq.com/malloc/mallocnocast.html
Esiste un errata corrige specifico per il K&R che corregge quanto detto
dagli autori prima della pubblicazione dello standard C89 (il K&R II
edizione fu pubblicato all'epoca poco prima che lo standard venisse
ratificato):
142(�6.5, toward the end): The remark about casting the return value of
malloc ("the proper method is to declare ... then explicitly coerce")
needs to be rewritten. The example is correct and works, but the advice is
debatable in the context of the 1988-1989 ANSI/ISO standards. It's not
necessary (given that coercion of void * to ALMOSTANYTYPE * is automatic),
and possibly harmful if malloc, or a proxy for it, fails to be declared as
returning void *. The explicit cast can cover up an unintended error. On
the other hand, pre-ANSI, the cast was necessary, and it is in C++ also. 
Tieni conto, comunque, che in C++, diversamente dal C, il cast sul valore
di ritorno di malloc() � OBBLIGATORIO, semplicemente perch� in C++ non c'�
conversione esplicita del tipo void* verso puntatori tipizzati.


 Non *� male*, per� � sconsigliabile.
No, � male perch� inutile e potenzialmente pericoloso.



Grazie anzitutto per le altre risposte che non ho quotato e per
l'appunto sul realloc per l'eventuale memory leak.
Per quanto riguarda questo pezzo... potresti correggermi se sbaglio?
sizeof *p sarebbe 4 no? ( cioè la dimensione del tipo INT )
Se io però volessi salvare in memoria un puntatore ad un INT, allora
farò
int **p = malloc( 10 * sizeof *p ); // alloco memoria per 10 puntatori
a interi.
Se è così ( e dovrebbe essere così perchè il mio programma in questo
modo compila e va ) allora con FtM ci sono stati solo problemi di
incomprensioni :)
PS: Mi si suggeriva di usare le liste per gestire la string table...
Ma seguire una lista fino a raggiungere l'indice cercato non è molto
più dispendioso?
PS2: Quando dicevi di allocare a blocchi ( 2->10 , 15->20 ) a quale
tipo di ottimizzazione ti riferivi? In quel modo risparmierei tempo in
esecuzione, però non sprecherei molto spazio? Cosa è meglio?
Grazie!


Suvvia, adesso sfioriamo il ridicolo. Dimmi un caso (ad eccezione di
quello che ho citato, ovvero quando ci si scorda di includere stdlib)
in cui è pericoloso.

 Suvvia, adesso sfioriamo il ridicolo. 
Temo che nessuno sfiori il ridicolo tranne te.



In generale, con un sistema operativo sufficientemente avanzato (per
x86 che hai citato direi qualunque) la realloc() già da sola svolge
questo compito. Salveresti solo il tempo della chiamata alla funzione,
tempo che non sarà mai importante nei casi comuni.
Per compiti specifici, se conosci l'algoritmo di allocazione della
libreria standard e sai a priori che non ti soddisfa, puoi non
chiamare la realloc() e usare direttamente la malloc() e riscriverti
un allocatore tutto tuo. Quasi sicuramente è tutto tempo sprecato.
Come sempre, prima fallo funzionare, poi (se è lento) ottimizzalo.
P.S. nel tuo caso ripeto, ti conviene ottenere prima il numero di
entrate e allocare tutto insieme, tanto il tempo che occorre per
leggere la tabella delle stringhe e scorrere una volta in anticipo il
buffer è quasi sicuramente inferiore al tempo necessario ad un
algoritmo con inserimento del tipo che stiamo discutendo!


Dimmi un caso (ad eccezione di quello che ho citato, ovvero quando ci


 esecuzione, per� non sprecherei molto spazio? Cosa � meglio?
S�, rischieresti di sprecare spazio, per� a volte potrebbe essere meglio
allocare grossi monoliti che frammentare troppo. Ovvio che nessuna
soluzione � migliore in assoluto, esistono vantaggi e svantaggi. 


 si scorda di includere stdlib) in cui � pericoloso.
Il caso in cui � pericoloso � proprio quello in cui ci si dimentica di
includere stdlib.h. E non l'hai citato tu, l'ho citato io. Ripeto: l'unica
cosa ridicola in questo thread � questo tuo ribattere continuamente. Come
gi� ampiamente detto, sia da me che dai riferimenti che ho riportato,
l'uso del cast sul valore di ritorno di malloc() ***E' MALE*** perch� in C
� inutile e pu� rivelarsi pericoloso. Punto.



Macchè male e male, non siamo sciamani: siamo programmatori (anche se
su di te inizio ad avere forti dubbi, vista l'attitudine e il modo di
parlare non sembri uno che lavora in una software house). Va bene
seguire delle regole, ma ogni cosa ha una ragione e un fondamento, che
occorre conoscere e spiegare.

 occorre conoscere e spiegare.
E' esattamente quello che ho fatto in questo thread. L'elemento di
disturbo, se non te ne sei reso conto, sei tu. Ora per� basta che sei
noioso. Ultima risposta. Ti saluto.


 Ora però basta che sei noioso. Ultima risposta. Ti saluto.
Non sono il tuo passatempo, che puoi giudicarmi noioso. Ma forse hai
troppo ego per essere socievole. In fondo mica tutti possono essere
simpatici.
P.S. Dai un'occhiata anche al tuo di karma - che a forza d'inclinarsi
sarà ormai orizontale! ;-)

     T *p1 = malloc(sizeof *p1);
Non puoi fare cosi': usare sizeof(void) porta a undefined behaviour
(IIRC void e' un tipo incompleto quindi non puoi calcolarne la
grandezza).
ciao
Giacomo

 Non puoi fare cosi': usare sizeof(void) porta a undefined behaviour
Gi� corretto nel thread.


 Già corretto nel thread.
Ops, scusa, non me ne ero accorto.
ciao
Giacomo

 
Bene. Meriteresti, per�, di esser trattato come fai con gli altri.

 Bene. Meriteresti, per�, di esser trattato come fai con gli altri.
Ne riparliamo quando avrai il coraggio di firmarti nome e cognome. Per tua
informazione io non ho problemi ad ammettere i miei errori, caro anonimo.


 
Non scherzare. Sono pi� che referenziabile :-). Inoltre non ho scritto 
maltrattato ma trattato; sei tu che hai la coscienza sporca.

 sei tu che hai la coscienza sporca.
Ma ci mancherebbe altro.


Ma sono tutti i thread o solo questo che ho aperto io che si
trasformano ad un'ammasso di flames? :/

 trasformano ad un'ammasso di flames? :/
Sono cose che capitano ogni tanto in qualsiasi newsgroup...
non e' di certo colpa del tuo thread stai pur tranquillo! ;)
Qui era gia' da qualche mese che era tutto tranquillo, prima
o poi doveva capitare di nuovo!
ciao
Giacomo

 trasformano ad un'ammasso di flames? :/
Succede spessissimo, stai tranquillo. I thread di solito si trasformano in
flames in quei ng dove la rigorosit� degli argomenti � d'obbligo, come �
d'obbligo quando si parla di C e C++. Purtroppo troppa gente apre la bocca
e parla a vanvera. E poi la devi bacchettare. E quindi si risente. E ti
ribatte. E tu le devi spiegare come e in cosa si � sbagliata. Ma non
sempre � disposta ad accettarlo e quindi insiste nel punzecchiare,
arrampicandosi sugli specchi e dandoti del cafone e dell'arrogante.
Succede spessissimo. E succede anche perch� esistono persone come me che
(sbagliando perch� alla fine ne risentono tutti i frequentatori del ng)
non "lasciano correre" chi si ostina a ribattere per il puro spirito di
difendere le proprie sciocchezze. I flame nascono anche perch� ci sono
persone che in passato sono state bacchettate amorevolmente per i propri
errori ed ora, dietro nickname anonimi, non perdono occasione per dare
addosso al "bacchettatore", sai com'�... l'orgoglio ferito. L'importante,
in ogni caso, � che tu abbia chiarito i tuoi dubbi. Dal canto mio ignorer�
questo thread.


 doveva capitare di nuovo!
È ritornato Andrea Laforgia  :-)
Ciao
Giovanni
-- 
   A computer is like an air conditioner,
   it stops working when you open Windows.
   < 
http://giovanni.homelinux.net/
 >


:D

 trasformano ad un'ammasso di flames? :/
No, scusa, ho solo approfittato del tuo thread per far notare che c'� 
chi incorre sempre nella Sua arrogante supponenza. Mi piacerebbe che 
ricordasse che insegnare non significa sapere. Insegnare � la base del 
conoscere ma, spesso, non include una crescita professionale (sopratutto 
per chi usa i flag per uscire dai loop), Inoltre, sono anni che si 
ostina a non leggere attentamente le specifiche dello standard C 
asserendo che i cast alle malloc *non* (secondo lui) si fanno (non sono 
consigliate). Inoltre, invece di sollevare una questione riguardante le 
realloc() si ostina a proporre dei work-around per non perdere 
l'indirizzo delle aree di memoria allocate mentre dovrebbe preoccuparsi 
del fatto che la funzione sia stata mal costruita. Andrea, se una 
funzione ritorna un valore che pu� farti perdere l'indirizzo di un'area 
di memoria precedentemente allocata, interagisce col tuo codice 
(ritornando un puntatore non valido [0]). Se devi salvarti l'indirizzo 
dell'area di memoria precedentemente allocata dinamicamente, forse, � 
perch� la realloc non *pu�* e non *deve* 'toccarti' un dato. Forse 
avrebbe pi� senso che la funzione fosse del tipo int realloc(void **) e 
che il valore di ritorno ti dicesse se la ri/re ... allocazione � andata 
a buon fine oppure no. Discutere del fatto che si debba o meno eseguire 
il recast del valore ritornato dalle funzioni di allocazione dinamica 
serve giusto per darsi degli imbecilli (parlo sopratutto di me) quando 
si dimentica di includere stdlib (ne abbiamo discusso alla nausea). 
Questo, in ogni caso non � il motivo del mio post precedente. In questo 
contesto intendevo, semplicemente, farti notare il modo con cui tratti 
chi ti confuta affermazioni che, purtroppo, hanno senso solo in un 
ambito scolastico.

 ambito scolastico.
Anche qui do i miei 2 cents.
Chi non programma per lavoro non sa programmare, chi per lavoro
insegna a programmare tanto meno, e chi impara da questo tipo di
persone non può di certo imparare a programmare.
Basta vedere il rapporto di quanto imparano i neolaureati
all'università e in due mesi di lavoro/tirocinio.
Sicuramente servono entrambi gli aspetti, accademico e pratico, ma
sembra che il mondo accademico pretenda un rispetto unilaterale,
ghettizzandosi in un'elite che non accetta input esterni.
C'è anche da dire che del resto un "insegnante" di C non viene pagato
per essere un bravo programmatore, ma un bravo insegnante - e molto
spesso i due aspetti sono molto separati.
Ciao!

 consigliate). 
Scusa, ma, c'e' scritto da qualche parte che il cast *deve* essere fatto?
Altrimenti concordo con Andrea, il cast del valore di ritorno di malloc 
e' da evitare, e questo e' dettato dall'uso pratico del C.
Infatti fosse per me, il tipo void* potevano tenerselo che il char* 
andava benissimo.