RISOLTO Puntatori

Stato
Discussione chiusa ad ulteriori risposte.

Matteo34

Nuovo Utente
104
3
CPU
i5-10500 3.2Ghz
Dissipatore
Non specificato
Scheda Madre
Non specificata
HDD
M.2 251GB e M.2 500GB
RAM
16GB DDR4 2666mhz
GPU
Grafica Intel® UHD 630
Audio
Non specificata
Monitor
1920x1080 27"
PSU
Non specificato
Case
Non specificato
Periferiche
Nono specificato
Net
Eolo
OS
Ubuntu
Ho questa domanda che mi tormenamta da mesi e adesso avró finalmente una risposta, su internet non ho trovato risposta e soprattutto non ho avuto modo di chiedere al mio professore.
La domanda è la seguente:

Visto che un Array viene gestito dal compilatore C come un puntatore ai primi n byte dell'Array allocati in memoria, volevo sapere se questo puntatore viene allocato in memoria come un semplice puntatore e quindi occuppa spazio oppure viene gestito da qualche registro, in giro ho sentito che non alloca spazio in memoria, ma in questi modo non sarebbe possibile tenere traccia degli indirizzi dell'Array, e quindi sarebbe impossibile accedere alla prossima locazione con (indirizzo prima cella + byte)?
Ditemi se sbaglio e vorrei un chiarimento su ciò grazie??
 
Ultima modifica:

Matteo34

Nuovo Utente
104
3
CPU
i5-10500 3.2Ghz
Dissipatore
Non specificato
Scheda Madre
Non specificata
HDD
M.2 251GB e M.2 500GB
RAM
16GB DDR4 2666mhz
GPU
Grafica Intel® UHD 630
Audio
Non specificata
Monitor
1920x1080 27"
PSU
Non specificato
Case
Non specificato
Periferiche
Nono specificato
Net
Eolo
OS
Ubuntu
@DispatchCode attento: il fatto che un vettore locale inizializzato non significa che sia nello stack, lo standard non lo dice e quindi dipende dal compilatore. Potrebbe benissimo essere nello heap (il che evita di essere caricato ogni volta che la funzione viene richiamata) e venga usato un puntatore nello stack per accedere ai suoi elementi.
Però in questo modo potrei avere accesso all'array anche al di fuori della funzione in cui l'ho dichiarato, facendo una cosa simile:
C:
/* Ho pensato di creare una funzione che ritorni l'indirizzo di un array, sarebbe inutile da utilizzare dentro la funzione main, dato che potrei scrivere direttamente "a" e avrei accesso al primo indirizzo dell'array, infatti la chiamo all'interno di un altra funzione tramite un puntatore a funzione, così ho accesso all'indirizzo anche nella funzione acess, e così facendo modifico il valore della 2 cella dell'array al di fuori della funzione dove l'ho dichiarato*/

#include<stdio.h>

int* getAdress(int *var)
{
    return var;
}

void access(int*(*fptr)(int*), int *var)
{
    int* adress = fptr(var);
    
    *(adress + 1) = 5;
}

int main(void)
{
     int a[3] = {1, 2 ,3};

     access(getAdress, a);

     return 0;
}
Così non avrebbe più senso la distinzione tra variabili globali e non.
Ho compilato il codice e tutto funziona, quindi potrei fare la stessa cosa se venisse allocato nello Heap l'array.
La scelta di alcuni compilatori di allocare nello Heap gli array locali a parer mio non ha molto senso, potrei sapere il perchè di questa scelta?
 

DispatchCode

Moderatore
Staff Forum
Utente Èlite
2,223
1,854
CPU
Intel I9-10900KF 3.75GHz 10x 125W
Dissipatore
Gigabyte Aorus Waterforce X360 ARGB
Scheda Madre
Asus 1200 TUF Z590-Plus Gaming ATX DDR4
HDD
1TB NVMe PCI 3.0 x4, 1TB 7200rpm 64MB SATA3
RAM
DDR4 32GB 3600MHz CL18 ARGB
GPU
Nvidia RTX 3080 10GB DDR6
Audio
Integrata 7.1 HD audio
Monitor
LG 34GN850
PSU
Gigabyte P850PM
Case
Phanteks Enthoo Evolv X ARGB
Periferiche
MSI Vigor GK30, mouse Logitech
Net
FTTH Aruba, 1Gb (effettivi: ~950Mb / ~480Mb)
OS
Windows 10 64bit / OpenSUSE Tumbleweed
Ci sono codici che se compilati con GCC funzionano e se compilati con MSVC invece crashano, in quanto provocano un overflow.

La tua obiezione in realtà è "slegata" dal fatto che verrà allocato sullo stack (cosa altamente probabile, specie se piccolo) o meno. Stai facendo quel discorso per un array, ma vale lo stesso identico discorso di una qualsiasi variabile. Se passi l'indirizzo di quella variabile, la procedura potrà attraverso quell'indirizzo modificare il valore in quella posizione.
Quindi ciò che hai fatto è corretto (e come dici, funziona).

Detto ciò, ci sono cose che si possono fare e sono corrette, ed altre che si possono fare ma non sono corrette (e sono causa di bug). L'esempio che fai tu, può essere un utilizzo scorretto, in quanto quando lo stack frame viene distrutto, non è detto che quella posizione abbia ancora il valore che gli hai assegnato tu.

Il C offre molte libertà, non tutto ciò che puoi fare però è corretto: se vuoi un altro esempio, restituire da una funzione un puntatore a una variabile o a un array dichiarato nella funzione. Puoi farlo, ma quasi certamente incappi in errori in runtime.
 
  • Mi piace
Reazioni: Andretti60

Matteo34

Nuovo Utente
104
3
CPU
i5-10500 3.2Ghz
Dissipatore
Non specificato
Scheda Madre
Non specificata
HDD
M.2 251GB e M.2 500GB
RAM
16GB DDR4 2666mhz
GPU
Grafica Intel® UHD 630
Audio
Non specificata
Monitor
1920x1080 27"
PSU
Non specificato
Case
Non specificato
Periferiche
Nono specificato
Net
Eolo
OS
Ubuntu
Ci sono codici che se compilati con GCC funzionano e se compilati con MSVC invece crashano, in quanto provocano un overflow.

La tua obiezione in realtà è "slegata" dal fatto che verrà allocato sullo stack (cosa altamente probabile, specie se piccolo) o meno. Stai facendo quel discorso per un array, ma vale lo stesso identico discorso di una qualsiasi variabile. Se passi l'indirizzo di quella variabile, la procedura potrà attraverso quell'indirizzo modificare il valore in quella posizione.
Quindi ciò che hai fatto è corretto (e come dici, funziona).

Detto ciò, ci sono cose che si possono fare e sono corrette, ed altre che si possono fare ma non sono corrette (e sono causa di bug). L'esempio che fai tu, può essere un utilizzo scorretto, in quanto quando lo stack frame viene distrutto, non è detto che quella posizione abbia ancora il valore che gli hai assegnato tu.

Il C offre molte libertà, non tutto ciò che puoi fare però è corretto: se vuoi un altro esempio, restituire da una funzione un puntatore a una variabile o a un array dichiarato nella funzione. Puoi farlo, ma quasi certamente incappi in errori in runtime.
Si ma potrei avere accesso sapendo l'indirizzo della prima locazione dell'Array se viene allocato nell'heap, inoltre la locazione dell'Array è sempre quella se viene allocato nello heap, non vedo come possa modificare se io non alloco altri elementi
 

DispatchCode

Moderatore
Staff Forum
Utente Èlite
2,223
1,854
CPU
Intel I9-10900KF 3.75GHz 10x 125W
Dissipatore
Gigabyte Aorus Waterforce X360 ARGB
Scheda Madre
Asus 1200 TUF Z590-Plus Gaming ATX DDR4
HDD
1TB NVMe PCI 3.0 x4, 1TB 7200rpm 64MB SATA3
RAM
DDR4 32GB 3600MHz CL18 ARGB
GPU
Nvidia RTX 3080 10GB DDR6
Audio
Integrata 7.1 HD audio
Monitor
LG 34GN850
PSU
Gigabyte P850PM
Case
Phanteks Enthoo Evolv X ARGB
Periferiche
MSI Vigor GK30, mouse Logitech
Net
FTTH Aruba, 1Gb (effettivi: ~950Mb / ~480Mb)
OS
Windows 10 64bit / OpenSUSE Tumbleweed
Leggendo prima Andretti devo aver frainteso ciò che voleva dire. Ho riletto ora: penso intenda che l'array viene allocato nell'heap ma localmente alla funzione l'accesso avvenga in maniera "trasparente" utilizzando un puntatore allo stack. Ma non mi è chiarissimo, aspetto quindi lumi da @Andretti60
Io ho sempre visto array allocati sullo stack, anche array di grandi dimensioni; e quando troppo grandi, anche se avviene la compilazione, si ha un crash in seguito.

Un esempio può essere questo:
C:
#include <stdio.h>

#define  SIZE  500000

int main() {
    int array[SIZE];
  
    for(int i=0; i<SIZE; i++)
        array[i] = i;
  
    printf("Hello World!");
  
    return 0;
}

Se compilato con GCC (MinGw) termina senza problemi stampando "Hello world", invece se compilato con MSVC crasha.
L'accesso avviene utilizzando lo stack. Penso che Andretti intenda dire quindi che la posizione puntata da ESP si trovi nell'heap e che venga usato un offset che punta allo spazio che gli è stato riservato (insomma, il puntatore si trova sullo stack).

Btw, la posizione nell'heap non cambierebbe comunque se non fai un free esplicito della memoria allocata (e se questa non viene successivamente occupata da altro). Ma di nuovo, se non è memoria che allochi tu con malloc/calloc non hai modo di sapere com'è stato gestito il tuo array.
 
Ultima modifica:

Andretti60

Utente Èlite
6,440
5,091
Scusate la confusione, il mio commento precedente si riferiva solo al fatto che come e dove sia allocato un vettore locale dipende strettamente dal compilatore, non ha nessun riferimento alla domanda originale.

Per rispondere alla tua domanda:
Un vettore occupa solo lo spazio del vettore, esattamente come qualsiasi variabile, in quanto la loro posizione in memoria è conosciuta dal compilatore. Infatti c’è una sostanziale differenza tra un vettore e un puntatore, è una domanda classica nei colloqui di assunzione.
Prendi questo codice per esempio:


Codice:
char *pippo = "stringa di caratteri";
char pluto[] = "una altra stringa";

apparentemente Pippo e Pluto pare sembrano allocate in memoria allo stesso modo, per cui in un altro file se scriviamo

Codice:
extern char *pluto;

uno pensa che in tale file la variabile pluto contenga “una altra stringa”. In realtà non è vero, il programma può perfino finire in crash.

lo lascio a voi come esercizio.

PS scusate la formattazione, sto usando il cellulare
 

pabloski

Utente Èlite
2,868
916
Si ma potrei avere accesso sapendo l'indirizzo della prima locazione dell'Array se viene allocato nell'heap, inoltre la locazione dell'Array è sempre quella se viene allocato nello heap, non vedo come possa modificare se io non alloco altri elementi

Prendendolo però dallo stack frame della funzione in cui l'array è stato definito. Alla fin fine, lo scope del puntatore all'area di memoria dell'array è sempre quello locale, non è globale.

L'unica differenza è che, allocando il vettore nello stack, quando la funzione termina e avviene l'unwinding del suo stack frame, il contenuto del vettore verrà sicuramente sovrascritto dalle nuove funzioni e relative variabili locali che saranno create.

Avendo invece il contenuto del vettore nell'heap, non è dato sapere se e quando verrà sovrascritto.

In pratica, usando il tuo esempio, una funzione chiamata da main e a cui sia passato il puntatore al vettore, potrebbe conservare tale puntatore in una variabile globale, che sarebbe successivamente usato da altre funzioni. Ma tutto ciò è sempre possibile perchè c'è una variabile globale coinvolta. In caso contrario, la funzione chiamata dal main terminerebbe prima del main, e il suo puntatore al vettore verrebbe distrutto prima di quello del main.

Il punto è che la visibilità non ha nulla a che fare col dove siano salvati i dati. Non c'è un automatismo che consenta a chiunque di conoscere ed accedere ad un dato salvato nell'heap. A meno di fare una scansione dello spazio d'indirizzamento ovviamente, cosa che però vale pure per lo stack.

Sul perchè si preferisca l'heap al posto dello stack, le ragioni principali sono (1) lo stack è una risorsa scarsa su certe architetture, (2) questioni di performance legate al modo di accedere allo stack ( alcune architetture hanno sostanzialmente solo push e pop ), (3) condividere dati automaticamente tra i thread ( ogni thread ha un suo stack e questo complica la condivisione di dati salvati nello stack ).
 

DispatchCode

Moderatore
Staff Forum
Utente Èlite
2,223
1,854
CPU
Intel I9-10900KF 3.75GHz 10x 125W
Dissipatore
Gigabyte Aorus Waterforce X360 ARGB
Scheda Madre
Asus 1200 TUF Z590-Plus Gaming ATX DDR4
HDD
1TB NVMe PCI 3.0 x4, 1TB 7200rpm 64MB SATA3
RAM
DDR4 32GB 3600MHz CL18 ARGB
GPU
Nvidia RTX 3080 10GB DDR6
Audio
Integrata 7.1 HD audio
Monitor
LG 34GN850
PSU
Gigabyte P850PM
Case
Phanteks Enthoo Evolv X ARGB
Periferiche
MSI Vigor GK30, mouse Logitech
Net
FTTH Aruba, 1Gb (effettivi: ~950Mb / ~480Mb)
OS
Windows 10 64bit / OpenSUSE Tumbleweed
Scusate la confusione, il mio commento precedente si riferiva solo al fatto che come e dove sia allocato un vettore locale dipende strettamente dal compilatore, non ha nessun riferimento alla domanda originale.

Si, quindi è ciò che ho descritto sopra e che ha riportato anche pabloski nell'ultimo post in sostanza.

Comunque il codice che hai mostrato non dovrebbe nemmeno compilare, in quanto hai dichiarato pluto come array ma dove fai l'extern usi un puntatore (i tipi non dovrebbero essere compatibili).

La "stringa di caratteri" comunque sarà molto probabilmente memorizzata in una locazione differente rispetto all'altra stringa; è il caso di cui sopra, dove viene memorizzato il puntatore. La stringa sarà nel segmento dedicato ai dati costanti (rdata).

Più tardi compilo e riporto i risultati, giusto per curiosità.

EDIT:

str_memory.png
Eccolo infatti! La stringa si trova in rdata (è una costante).

In effetti non ho potuto compilare senza utilizzare [], rimuovendo il puntatore sulla stringa che era stata definita utilizzando un array.
Codice:
es.c:6:14: error: conflicting types for 'pluto'
extern char *pluto;
              ^~~~~
 
Ultima modifica:
Stato
Discussione chiusa ad ulteriori risposte.

Ci sono discussioni simili a riguardo, dai un'occhiata!

Entra

oppure Accedi utilizzando
Discord Ufficiale Entra ora!

Discussioni Simili