Newsgroup: it.comp.lang.c++
Titolo: Array

Buon Natale a tutti.
Il gcc mi fa compilare ed eseguire correttamente (sembra)
il seguente codice:
    int a = foo();
    PIPPO pippo[a];
?????
ammettendo che faccia quello che ci si aspetti:
cioe':
    PIPPO* pippo = new PIPPO[a];
chi la fa poi la delete [] ?
Ciao
Gaetano
--
#exclude <windows>
printf("\t\t\b\b\b\b\b\b");.
printf("\t\t\b\b\b\b\b\b");
-- 
Posted from 
nts-03.mm.eutelsat.org
 [194.214.173.15] 
via Mailgate.ORG Server - 
http://www.Mailgate.ORG


Perchè ti aspetti questo? new alloca nello heap, mentre qui mi aspetto un
array instanziato nello stack e distrutto durante l'unwinding.
Max

 array instanziato nello stack e distrutto durante l'unwinding.
Gia anche io mi aspetterei questo pero' non mi piace il fatto che
lo stack occupato dipende da parametri a tempo di compilazione....
Mi spiego, se io ho:
void foo(int a, float b)
{
    int pippo;
    ......
    double ciao;
    ....
}
quando la foo viene chiamata viene liberato spazio sullo stack ( in realta'
viene mosso lo SP, per parlare in termini 'x86') per contenere i parametri
formali
e le variabili locali e tutto questo avviene quando il compilatore crea il
codice per
la foo (compile time).
se io avessi:
void foo(int a, float b)
{
    int pippo;
    ......
    double ciao;
    ....
    char cheSchifo[pippo];
   ......
    ....
}
quando verra' mosso lo SP ? e dalla dichiarazione dell'array di char in poi
che offset devo dare a allo SP per indirizzare le variabili locali e/o
formali ?
Capirai che in termini di performance per accedere alle variabili locali e'
un
disastro...
Mi sembra una bruttura senza uguali.


Mi sembra una bruttura senza uguali.
Boh... a me sembra una aggiunta ragionevole; fra l'altro e'
ora parte del C e quindi sono convinto che si tratti di una
feature che sara' incorporata nel prossimo standard C++.
Se non ti piace non usarla. Per fortuna non si tratta di
qualcosa che paghi anche se non vuoi (come invece sono
ad esempio le exceptions).
Andrea

    if (myvect.size()<n) myvect.resize(n);
Be le due cose sono diverse.
tra le altre cose il codice "ottimizzato" lo
sarebbe di piu se fosse:
//Codice "ottimizzato++"  //
//con la giusta ipotesi che n e' unsigned
static std::vector<int> myvect;
myvect.resize(n);
Ad ogni modo uno stack usato come un'heap
mi fa rabbrividire.


disastro...
   In architetture x86 gcc genera codice che copia esp in ebp
all'ingresso della routine, sposta esp per creare spazio per le
variabili locali, e poi utilizza ebp per indirizzare i parametri
[%ebp+XXXX], le variabili locali [%ebp-XXXX], e qualunque altra cosa
venga messa sullo stack. In pratica, per allocare nuovo spazio sullo
stack, viene spostato esp, ma si continua ad accedere agli oggetti
utilizzando ebp, quindi gli offset non cambiano. Gli unici cicli di
clock sprecati sono quelli per sommare e sottrarre quantita` al
registro esp. Per altre architetture non saprei.
   Per inciso lo stesso comportamento si ottiene anche quando utilizzi
variabili locali molto "ingombranti" all'interno di spezzoni di codice
(eventualmente condizionali), es:
   if (a > 1000) {
      int b[1000];
      ...
   }
-- 
Totuccio.

mi fa rabbrividire.
Veramente i vettori a dimensione variabile non
significano usare lo stack come un'heap, ma
semplicemente non dover essere *obbligati* ad
usare (e pagare) un'heap quando basta uno stack.
Andrea

 utilizzando ebp, quindi gli offset non cambiano.
Non cambiano se sono noti a compile time, vediamo cosa succede con il
seguente
codice:
void pluto() {
    int a = rand();
    int b = 0;
    int c[a];
    int d;                    // qui che offset ha d rispetto a ebp ???
   d  = a;
   c[3] = a;
}
pushl   %ebp                                    //salvo ebp
movl    %esp, %ebp                         //copio sp in ebp
pushl   %ebx                                    //salvo ebx
subl    $20, %esp                            //faccio spazio alle variabili
locali ????
movl    %esp, %ebx                        //salvo in bx lo sp
call    rand                                       //chiamo rand che lascia
il valore in ax
movl    %eax, -8(%ebp)                  //metto il valore restituito da rand
in a
movl    $0, -12(%ebp)                    //metto 0 in b (offset -12)
movl    -8(%ebp), %eax                  //questa non l'ho capita (dovrebbe
essere
                                                       //inutile.....
sall    $2, %eax                                //?????
subl    $4, %eax                               //questa e la seguente
sembrano annullarsi ??
addl    $4, %eax
addl    $15, %eax                            //????
shrl    $4, %eax                               //?????
sall    $4, %eax                                //??????
subl    %eax, %esp                         //spazio per c ma quanto ?
movl    %esp, %edx                        //metto in edx l'offset 0 di C
movl    -8(%ebp), %eax                //metto in ax il valore di a
movl    %eax, -16(%ebp)              //copio ax in d ( sembra che lo spazio
per c
                                                    // occupi solo una
posizione sullo stack!!!!!
movl    -8(%ebp), %eax                //metto in ax il valore di a
movl    %eax, 12(%edx)               //metto ax nel 4 elemento di C
movl    %ebx, %esp                    //ripristino lo sp salvato in bx
movl    -4(%ebp), %ebx             //rimetto in bx il valore salvato sullo
stack
movl    %ebp, %esp                    // ripristino sp
popl    %ebp                                //ripristino ebp
ret
Mi sembra di capire che
a abbia offset -8
b abbia offset -12
c abbia offset ????
d abbia offset -16
PS. E da un po che non tocco codice ASM.
Ho rifatto lo stesso esperimento con l'array C conosciuto
a compile time e le istruzioni ASM sono diventate
la meta', ma c'e' ancora qualche cosa che mi sfuge:
void pluto() {
    int a = 0;
    int b = 0;
    int c[4];
    int d = 0;
   c[0] = 0;
   c[1] = 0;
   c[2] = 0;
   c[3] = 0;
}
        pushl   %ebp
        movl    %esp, %ebp
        subl    $56, %esp                    // 56 ??????????
        movl    $0, -12(%ebp)            // a = 0
        movl    $0, -16(%ebp)            // b = 0
        movl    $0, -44(%ebp)            // d = 0
        movl    $0, -40(%ebp)            // c[0] = 0
        movl    $0, -36(%ebp)            // c[1] = 0
        movl    $0, -32(%ebp)            // c[2] = 0
        movl    $0, -28(%ebp)           // c[3] = 0
        movl    %ebp, %esp
        popl    %ebp
        ret
perche' il buco tra b e c[3] ?
perche' sp spostato di 56 e non di 48 ?


 usare (e pagare) un'heap quando basta uno stack.
Paghi comunque il codice per mettere l'array sullo
stack, che non e' il semplice spostare lo SP, guarda a proposito
il codice ASM che ho postato a Totuccio su questo
stesso thread.


perche' sp spostato di 56 e non di 48 ?
   Non lo so... Si direbbero politiche di allineamento degli array,
forse per ottimizzare l'efficienza quando si usano indici, come in
c[i]? (o, a livello di processore, quando ci sono riferimenti come
12(%edx)?).
-- 
Totuccio.

questo stesso thread.
<< Inizio specifico x86 >>
Il codice puo' avere lo stesso costo di allocare un
array statico sullo stack. Gli indirizzi per le variabili
locali in uno stesso scope possono essere ordinati in
modo da mettere tutte le variabili con dimensione nota
all'inizio senza che il tuo programma noti una differenza.
Per ogni array "dinamico" allochi "staticamente" 2 dword:
una per l'indirizzo dei dati e una per il numero di
elementi. Quando arrivi al punto di inizializzazione della
variabile devi solamente memorizzare il numero di elementi,
spostare SP della dimensione e salvare come indirizzo di
inizio SP; il costo aggiuntivo e' quindi risibile (non
considero il calcolo del numero di elementi o della sua
dimensione un costo "aggiuntivo" ... quello e' ovviamente
parte *essenziale* degli array dinamici).
Anche la chiamata dei costruttori e' un costo "essenziale"
(anche se credo che il C non fornisca array inizializzati
per i tipi nativi e lo stesso spero fara' il C++ ... per
emulare la cosa con il template che ti avevo mostrato
credo sia necessario specializzare per tutti i tipi nativi).
L'unico eventuale accorgimento aggiuntivo potrebbe
essere il salvare SP all'ingresso di uno scope per
evitare di dover fare calcoli all'uscita.
<< fine specifico x86 >>
Trovo molto piu' fastidioso (dal punto di vista della
pulizia logica) che sizeof in C applicato ad array dinamici
diventi una espressione non costante.
Un'altra asimmetria sarebbe l'impossibilita' di usare
array dinamici come membri di struttura.
Potrebbe invece aver senso creare array multidimensionali
in cui solo alcune delle dimensioni sono dinamiche...
Andrea

la meta', ma c'e' ancora qualche cosa che mi sfuge:
Si e' anche perso il piccolo grado di liberta' aggiuntivo
di avere l'array a dimensione variabile :D .
Per fare un paragone serio prova a contare quante
istruzioni *eseguite* comporta usare std::vector.
Il codice asm che hai mostrato inoltre non sembra essere il
risultato di una compilazione con ottimizzazioni... questo
fa una *enorme* differenza con il gcc (positivamente
come tempo di esecuzione e negativamente come tempo di
compilazione).
Andrea