Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 18
  1. #1
    Utente di HTML.it
    Registrato dal
    May 2006
    Messaggi
    48

    [c++] vettori e passaggio parametro a funzione

    Ciao a tutti, ho una domanda da fare:
    Dato il codice:
    codice:
    using namespace std;
    #include <iostream>
    void cambia(int m, int n[7]){
        for(int i=0;i<7;i++) 
            cout << "n["<<i<<"]: " << n[i] <<endl;
            (*(n+m))--;
            m++;
            n--;
    }
    
    
    int main(){
        int vet[] ={4,2,3,2,4};
        cambia(vet[4], vet-1);
        cambia(0,&vet[4]);
        int i=0;
        for(i=0; i<5; i++)
            cout << vet[i];
        return 0;
        
    }
    faccendolo eseguire, noto che
    codice:
    vet[-1]=0
    ..?? non dovrebbe esserci memoria sporca?

    Tanto per darvi un idea: eseguendo il codice ho l'output:

    codice:
    bog@bog-desktop:~$ ./a.out n[0]: 0
    n[1]: 4
    n[2]: 2
    n[3]: 3
    n[4]: 2
    n[5]: 4
    n[6]: 32767
    n[0]: 4
    n[1]: 32767
    n[2]: 0
    n[3]: 0
    n[4]: 0
    n[5]: 0
    n[6]: -1924407835
    42313
    Poi vorrei fare un altra domanda:
    come potete vedere, nell'esercizio, passo un vettore di dimensione 5 ad un vettore di dimensione 7. Ok, immagino che compilera' i 5 campi con i valori passatigli e gli altri rimaranno memoria sporca. Ma se io passassi il mio vettore di 5 elementi ad uno di 2? i primi 2 come funzionerebbe?

    In realta', io so che vet[] non è altro che un puntatore al primo elemento dei valori inseritivi. quindi ... che io passi il mio vettore di 5 elementi ad un vettore di 10 o solo 1 ... non fa differenza, no ?
    se io so che dentro ci sono 5 elementi, io posso scrivere vettore[5] anche se il vettre è stato dichiarato ti dimensione 1. Giusto?

    GRazie a tutti
    BoG

  2. #2
    Utente di HTML.it
    Registrato dal
    Dec 2009
    Messaggi
    1,123
    Quote Originariamente inviata da BoG Visualizza il messaggio
    Ciao a tutti, ho una domanda da fare:
    Dato il codice:
    codice:
    using namespace std;
    #include <iostream>
    void cambia(int m, int n[7]){
        for(int i=0;i<7;i++) 
            cout << "n["<<i<<"]: " << n[i] <<endl;
            (*(n+m))--;
            m++;
            n--;
    }
    
    
    int main(){
        int vet[] ={4,2,3,2,4};
        cambia(vet[4], vet-1);
        cambia(0,&vet[4]);
        int i=0;
        for(i=0; i<5; i++)
            cout << vet[i];
        return 0;
        
    }
    faccendolo eseguire, noto che
    codice:
    vet[-1]=0
    ..?? non dovrebbe esserci memoria sporca?

    Tanto per darvi un idea: eseguendo il codice ho l'output:

    codice:
    bog@bog-desktop:~$ ./a.out n[0]: 0
    n[1]: 4
    n[2]: 2
    n[3]: 3
    n[4]: 2
    n[5]: 4
    n[6]: 32767
    n[0]: 4
    n[1]: 32767
    n[2]: 0
    n[3]: 0
    n[4]: 0
    n[5]: 0
    n[6]: -1924407835
    42313
    Dove dici vet[-1]? Non c'è nessun accesso alla locazione -1, ed un accesso simile provocherebbe sicuramente un errore di compilazione. Quel "vet-1" passa praticamente un indirizzo di memoria (quello di vet) sottraendo però -1.
    Ad esempio l'indirizzo di vet è: 0x28fef8
    Se passo vet-1 ottengo: 0x28fef4
    Se passo vet+1 ottengo: 0x28fefc

    Il primo valore che viene stampato è causato proprio da questo infatti.

    Poi vorrei fare un altra domanda:
    come potete vedere, nell'esercizio, passo un vettore di dimensione 5 ad un vettore di dimensione 7. Ok, immagino che compilera' i 5 campi con i valori passatigli e gli altri rimaranno memoria sporca. Ma se io passassi il mio vettore di 5 elementi ad uno di 2? i primi 2 come funzionerebbe?


    In realta', io so che vet[] non è altro che un puntatore al primo elemento dei valori inseritivi. quindi ... che io passi il mio vettore di 5 elementi ad un vettore di 10 o solo 1 ... non fa differenza, no ?
    se io so che dentro ci sono 5 elementi, io posso scrivere vettore[5] anche se il vettre è stato dichiarato ti dimensione 1. Giusto?

    GRazie a tutti
    Il problema è che rischi (anzi, non è un rischio, è una certezza) di sovrascrivere altro in memoria. Se tu hai memoria per 2 elementi e gli vai a passare l'indirizzo di un array con 5, tu puoi accedere tramite quell'array agli elementi di quello con 5 (avendo lo stesso indirizzo). Ma prima avevi riservato due posti in memoria, quindi rischi di andare a sovrascrivere i dati che ci sono dopo ai due elementi allocati con 3 aggiuntivi quindi.

    E' un riscontro che puoi avere anche sul tuo codice quello riguardante gli accessi:
    codice:
    using namespace std;
    #include <iostream>
    void cambia(int m, int n[2]){
        for(int i=0;i<5;i++) 
            cout << "n["<<i<<"]: " << n[i] <<endl;
         cout << "Indirizzo n "<< n;
    }
    
    
    int main(){
        int vet[] ={4,2,3,2,4};
        cambia(vet[4], vet);
        cout << "\nIndirizzo vet: "<<vet;
        return 0;
    
    }
    Output:
    codice:
    n[0]: 4
    n[1]: 2
    n[2]: 3
    n[3]: 2
    n[4]: 4
    Indirizzo n 0x28fefc
    Indirizzo vet: 0x28fefc

    Quindi di fatto sullo stack la differenza c'è...
    Ad ogni modo non mi sembra una pratica "necessaria" questa.


    PS: non so se mi sono espresso correttamente, al massimo riformulo.

  3. #3
    Utente di HTML.it
    Registrato dal
    May 2006
    Messaggi
    48
    Quote Originariamente inviata da Patrick Jane Visualizza il messaggio
    Dove dici vet[-1]? Non c'è nessun accesso alla locazione -1, ed un accesso simile provocherebbe sicuramente un errore di compilazione. Quel "vet-1" passa praticamente un indirizzo di memoria (quello di vet) sottraendo però -1.
    Ad esempio l'indirizzo di vet è: 0x28fef8
    vorrei chiedetri: perchè allora vet-1 mi da n[0]=0?
    BoG

  4. #4
    Utente di HTML.it L'avatar di oregon
    Registrato dal
    Jul 2005
    residenza
    Roma
    Messaggi
    36,466
    Quote Originariamente inviata da BoG Visualizza il messaggio
    vorrei chiedetri: perchè allora vet-1 mi da n[0]=0?
    Che vuoi dire?
    No MP tecnici (non rispondo nemmeno!), usa il forum.

  5. #5
    Utente di HTML.it L'avatar di Scara95
    Registrato dal
    Jul 2009
    residenza
    Zimella (VR)
    Messaggi
    2,590
    Quote Originariamente inviata da BoG Visualizza il messaggio
    vorrei chiedetri: perchè allora vet-1 mi da n[0]=0?
    Se accedi un indirizzo di memoria a caso leggendo un intero la probabilità che ci sia uno 0 è 1/(2^32) (supponendo che la dimensione di int sul tuo compuret sia 4 bytes)
    "Quid enim est, quod contra vim sine vi fieri possit?" - Cicerone, Ad Familiares

  6. #6
    Utente di HTML.it
    Registrato dal
    May 2006
    Messaggi
    48
    Quote Originariamente inviata da oregon Visualizza il messaggio
    Che vuoi dire?
    Intendo dire:
    quando chiamo la mia funzione
    codice:
    cambia(vet[4], vet-1);
    , quel
    codice:
    vet-1
    è un puntatore (perche' vet e' stato dichiarato array) alla cella di memoria (della dimensione di un intero, 4byte) prevedente l'inizio del vettore


    4 4 2 3 2
    vet[0] vet[1] vet[2] vet[3] vet[4]


    Mentre, una volta passato vet-1 a n[7] ho:

    mem.sporca 4 4 2 3 2
    n[0] n[1] n[2] n[3] n[4] n[5]

    perchè n[0] contiene sempre 0 ?
    BoG

  7. #7
    Utente di HTML.it L'avatar di oregon
    Registrato dal
    Jul 2005
    residenza
    Roma
    Messaggi
    36,466
    Continuo a non capire il tuo dilemma ...
    No MP tecnici (non rispondo nemmeno!), usa il forum.

  8. #8
    Utente di HTML.it
    Registrato dal
    Dec 2009
    Messaggi
    1,123
    Non mi è molto chiaro ciò che intendi... ma provo con un altra spiegazione.
    La cosa è un po' contorta e dovrebbe essere analizzata in un altro modo, ad un livello più basso.

    Quando chiami una funzione vengono passati i parametro, in questo caso viene sfruttato lo stack.

    Prendiamo il mio codice:
    codice:
    using namespace std;
    #include <iostream>
    void cambia(int m,int n[2], int l, int g){
        int i;
        for(i=0;i<5;i++) 
            cout << "n["<<i<<"]: " << n[i] <<endl;
         cout << "Indirizzo n "<< n<<endl;
         cout << "indirizzo m "<< &m<<endl;
         cout << "Indirizzo l "<< &l<<endl;
         cout << "Indirizzo g "<< &g<<endl;
         cout << "Indirizzo i "<< &i<<endl;
    }
    
    
    int main(){
        int vet[] ={1,2,3,4,5};
        cambia(vet[4],vet-5, 10,100);
        cout << "\nIndirizzo vet: "<<vet;
        return 0;
    
    }
    Output, giusto per avere le idee più chiare in seguito:
    codice:
    n[0]: 10
    n[1]: 100
    n[2]: 1991806165
    n[3]: -885658597
    n[4]: -2
    Indirizzo n 0x28fee8
    indirizzo m 0x28fee0
    Indirizzo l 0x28fee8
    Indirizzo g 0x28feec
    Indirizzo i 0x28fecc
    
    Indirizzo vet: 0x28fefc

    A basso livello accade questo:
    codice:
    MOV EAX,DWORD PTR SS:[ESP+2C]
    MOV DWORD PTR SS:[ESP+C],64
    MOV DWORD PTR SS:[ESP+8],0A
    LEA EDX,DWORD PTR SS:[ESP+1C]
    SUB EDX,14
    MOV DWORD PTR SS:[ESP+4],EDX
    MOV DWORD PTR SS:[ESP],EAX
    CALL asd.0040138C
    Non so se conosci assembly, ma per non prendere il giro troppo alla larga diciamo che la prima istruzione che vedi, con [ESP+2C] sta accedendo alla posizione vet[4] dell'array e quindi salva il contenuto in EAX (fai caso al fatto che ESP+2C è l'ultimo elemento, il primo quindi si trova a [ESP+1C], visto che sono ogni 4byte).
    Successivamente mette in ESP+C il valore 64 (che in decimale è il numero 100).
    Poi inserisce in EAX+8 l'elemento 0A.
    Poi come vedi accede a ESP+1C (primo elemento dell'array) ed in pratica prende tale indirizzo.
    Nell'istruzione successiva effettua un SUB, che è una sottrazione in pratica fa quindi EDX = EDX - 14. Il numero 14 è espresso in esadecimale, ed in decimale corrisponde al 20. Non è un caso, in quanto l'array lo passiamo come vet-5 (5 elementi da 4 byte ciascuno = 20byte).
    Le ultime tre istruzioni indicano appunto ESP+4 (indirizzo del primo elemento dell'array, 10, a cui viene assegnato l'indirizzo modificato poco sopra grazie alla sottrazione) ed il primo parametro che passi alla funzione, ovvero l'elemento dell'array preso precedentemente, ovvero 5.
    La successiva è la chiamata alla funzione cambia().

    Ora qui non lo puoi vedere, ma nello Stack dove sono presenti i parametri i dati sono i seguenti:

    codice:
    0028FEE8  |0000000A
    0028FEEC  |00000064
    0028FEF0  |76B88CD5  msvcrt.76B88CD5
    0028FEF4  |B346661C
    0028FEF8  |FFFFFFFE
    0028FEFC  |00000001
    0028FF00  |00000002
    0028FF04  |00000003
    0028FF08  |00000004
    0028FF0C  |00000005
    La colonna a sinistra indica gli indirizzi di memoria, quella a destra indica i dati contenuti.
    ESP+1C puntava prima della sottrazione all'indirizzo 28FEFC. Se vai a vedere sull'Output vedi che questo è in effetti l'indirizzo di vet.
    Dopo alla sottrazione quindi, vado indietro di 20byte (5 posizioni), e mi ritrovo all'indirizzo: 0028FEE8 che "casualmente" (l'ho fatto ovviamente apposta quando ho passato parametri ed array) contiene il valore 0A (10, in decimale; è il parametro della funzione passato dopo a vet-5).

    I primi due valori stampati sono quindi 10 e poi 100 (64 in esadecimale). Come vedi l'output successivo è "sporco" come dici tu.

    Renditi conto che se prendi il mio codice e lo esegui più volte vedrai che i valori "sporchi" non sono sempre uguali (guarda la posizione [3]).

    Altri due Output:
    codice:
    n[0]: 10
    n[1]: 100
    n[2]: 1991806165
    n[3]: 470907442
    n[4]: -2
    Indirizzo n 0x28fee8
    indirizzo m 0x28fee0
    Indirizzo l 0x28fee8
    Indirizzo g 0x28feec
    Indirizzo i 0x28fecc
    Output:
    codice:
    n[0]: 10
    n[1]: 100
    n[2]: 1991806165
    n[3]: 1217724272
    n[4]: -2
    Indirizzo n 0x28fee8
    indirizzo m 0x28fee0
    Indirizzo l 0x28fee8
    Indirizzo g 0x28feec
    Indirizzo i 0x28fecc
    Se passo come parametro vet-1 al posto del valore precedente, come output ottengo:
    codice:
    n[0]: -2
    n[1]: 1
    n[2]: 2
    n[3]: 3
    n[4]: 4
    Indirizzo n 0x28fef8
    indirizzo m 0x28fee0
    Indirizzo l 0x28fee8
    Indirizzo g 0x28feec
    Indirizzo i 0x28fecc
    E come vedi tutto torna: il valore -2 era presente anche prima, ma nella quinta posizione dell'array.

    Ometto il codice relativo alla funzione cambia() in quanto il ciclo non è di facile ed immediata comprensione, visto così; effettua comunque le operazioni utilizzando ESP come visto sopra.

    PS: ho usato MinGw come compilatore.
    Compila il mio codice e vedi che cosa ottieni. Passami se vuoi il file exe che ottieni.

  9. #9
    Quote Originariamente inviata da BoG Visualizza il messaggio
    faccendolo eseguire, noto che
    codice:
    vet[-1]=0
    ..?? non dovrebbe esserci memoria sporca?
    Per quanto concerne lo standard C++, stai sforando da un array, per cui si cade nell'"undefined behavior" - qualunque comportamento è lecito.

    Quello che in pratica accade è che sforando in negativo da un array stai andando a pescare valori in zone più alte dello stack (su architettura x86 lo stack cresce verso indirizzi più bassi), che a questo punto del programma probabilmente non sono ancora state toccate.
    Queste pagine di memoria partono in genere piene di zeri perché su praticamente ogni sistema operativo moderno le pagine di memoria "nuove" che vengono fornite ad un processo sono "bianche" (ovvero riempite di zeri), per evitare che un processo possa vedere dati riservati da pagine di memoria deallocate da altri processi.

    Va detto che diversi compilatori in modalità "debug" (nonché il "debug allocator" dell'heap manager di Windows) riempiono la memoria non inizializzata (su stack ed heap) di "valori sentinella" (va spesso per la maggiore 0xcc o 0xcd), in modo da consentire di individuare problemi di uso di memoria non inizializzata.
    Poi vorrei fare un altra domanda:
    come potete vedere, nell'esercizio, passo un vettore di dimensione 5 ad un vettore di dimensione 7. Ok, immagino che compilera' i 5 campi con i valori passatigli e gli altri rimaranno memoria sporca. Ma se io passassi il mio vettore di 5 elementi ad uno di 2? i primi 2 come funzionerebbe?
    Qui è la sintassi C che confonde. Quando in una dichiarazione di funzione tu scrivi
    codice:
    void funzione(int arr[50]);
    di fatto viene parsato come
    codice:
    void funzione(int arr[]);
    ovvero
    codice:
    void funzione(int *arr);
    Questo segue dal fatto che, per una serie di accidenti storici, in C gli array (1) non si possono passare per valore e (2) decadono facilmente in puntatori al primo elemento. In effetti, personalmente ritengo che sia buona norma semplicemente non specificare le dimensioni degli array passati come parametri a funzione, visto che sono sempre e comunque puntatori e scrivere delle dimensioni serve solo a confondere i principianti.

    (tieni conto comunque che questo vale solo per i parametri delle funzioni, se in una struct o da qualunque altra parte scrivi int arr[50]; stai effettivamente dichiarando un array di 50 interi)
    se io so che dentro ci sono 5 elementi, io posso scrivere vettore[5] anche se il vettre è stato dichiarato ti dimensione 1. Giusto?
    Sì ma questo è un artefatto di quanto appena detto, ovvero segue dal fatto che, se è un parametro ad una funzione, di fatto è un puntatore. Ovviamente se fai vettore[5] su un vettore che è stato effettivamente dichiarato come di 1 elemento vai in undefined behavior.
    Ultima modifica di MItaly; 21-12-2013 a 18:54
    Amaro C++, il gusto pieno dell'undefined behavior.

  10. #10
    Utente di HTML.it
    Registrato dal
    May 2006
    Messaggi
    48
    Forse mi sono espresso male...chiedo scusa.
    Magari possiamo risolverla così:
    Ora commento il codice del programma così mi dite dove sbaglio.

    codice:
    using namespace std;#include <iostream>
    void cambia(int m, int n[7]){
            (*(n+m))--; //n punta a vet[-1], memoria sporca, m = 4, quindi *(*(n+m)) punta a vet[-1+4] e decrementa il valore presente
            m++; //m=4 diventa m=5
            n--; //mi sposto nell'elemento precedente del vettore
    //ora, siccome, vet-1 è stato passato per indirizzo (perchè gli array sono passati come indirizzi) 
    //ad un altro puntatore (perchè un array, n[7], è un puntatore) avro' che le mie modifiche saranno
     //salvate sul vettore contenuto nel main: vet[]. Invece la mia variabile locale int m; 
    //non avendo il segno & davanti riceve una copia del valore contenuto in vet[4], quindi le modifiche sono perse.
    }
    
    
    int main(){
        int vet[] ={4,2,3,2,4};
        cambia(vet[4], vet-1);//passo alla funzione l'indirizzo del 4°elemento del vettore e lo metto in int m; 
    //poi prendo l'indirizzo della cella di memoria contenente vet, ci sottraggo 1 (cella) e passo questo indirizzo a n[]. 
    //è un po' come se gli avessi passato vet[-1]
    
        cambia(0,&vet[4]);
    //ora, chiamo di nuovo la funzione scambia passando un intero e l'indirizzo di memoria &vet[4]. Domanda: se passo come parametro vet[4] passo il valore contenuto in vet[4] oppure l'indirizzo
    //della cella di memoria vet[4]? e se avessi passato vet+4? oppure &vet+4? oppure &vet[4]? 
    //Provo a dare la mia idea:
    // vet+4: passo l'indirizzo del primo elemento + 4, quindi passo l'indirizzo del 5° elemento del vettore
    //vet[4]: passo il valore contenuto nella posizione vet[4], quindi un intero, nel mio caso =4
    //&vet+4: credo valga lo stesso discrorso di vet+4
    //&vet[4]: passo l'indirizzo di memoria del 5° elemento dell'array
    
    //in questo caso, l'ultimo elemento dell'array sara' decrementato di 1, perchè passando &vet[4] 
    //identifico l'ultimo elemento del mio array, gli sommo la variabile m che nel mio caso ha ricevuto 
    //come parametro il valore 0, poi con (*(n+m))-- decremento l'ultimo elemento del vettore. 
    //dopo di che fa anche un m++ ma non serve a molto e poi fa n-- e quindi sposta il puntatore 
    //indietro di un elemento dell'array (nulla di rilevante ai fini dell'alterazione dei valori nell'array).
    //siccome si parla ancora di vettori ho che sono stati passati per indirizzo, quindi le modifiche sono state salvate 
        int i=0;
        for(i=0; i<5; i++)
            cout << vet[i];
        return 0;    
    }
    Giusto?
    BoG

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.