View Full Version : [C++] ancora su puntatori ad indirizzi, * e &
vermaccio
01-06-2006, 12:16
se ho una funzione
void esempio(double *x) {
double i;
double numero=7;
for (i = 0; i<numero; i++) {
*(x+i)=i;
}
} //end function
se nel main attivo
double test[7];
esempio(&test[0]);
alla fine avrò:
test[0]=0;
test[1]=1;
test[2]=2;
test[3]=3;
test[4]=4;
test[5]=5;
test[6]=6;
domanda:
ma...se io NON so a priori il ciclo for dentro la funzione quante volte verrà attivato (al posto del "numero=7" ho un valore che saprò solo durante lo svolgimento della funzione).Ebbene:
poichè nel main ho definito
double test[7];
ovvero è come se avessi detto (tanto per capire)
test[numero]; visto che numero=7
come faccio?
io num non lo so.
se la funzione mi "spara" numero=12
che ne so io a priori da scrivere nel listato
double test[12];
come risolvo?
cioè come faccio a apssare alla funzione l'indirizzo del primo elemento di un array di cui però non ho idea di quanti elementi avrà l'array e quindi non posso definirlo a priori?
oppure c'è un altro metodo?
insoma io devo costruire un array dentro una funzione, atrray che sarà poi visibile come variabile globale nel main e di cui non so quanto deve essere grosso a priori ma solo durante lo svolgimento della function stessa.
se ho una funzione
void esempio(double *x) {
double i;
double numero=7;
for (i = 0; i<numero; i++) {
*(x+i)=i;
}
} //end function
se nel main attivo
double test[7];
esempio(&test[0]);
alla fine avrò:
test[0]=0;
test[1]=1;
test[2]=2;
test[3]=3;
test[4]=4;
test[5]=5;
test[6]=6;Ok, non fa una piega. Io userei solo l'indice del for di tipo intero e poi userei x[i] invece di *(x+i), che comunque sarebbe la stessa cosa.
domanda:
ma...se io NON so a priori il ciclo for dentro la funzione quante volte verrà attivato (al posto del "numero=7" ho un valore che saprò solo durante lo svolgimento della funzione).Ebbene:
poichè nel main ho definito
double test[7];
ovvero è come se avessi detto (tanto per capire)
test[numero]; visto che numero=7
come faccio?
io num non lo so.
se la funzione mi "spara" numero=12
che ne so io a priori da scrivere nel listato
double test[12];
come risolvo?
cioè come faccio a apssare alla funzione l'indirizzo del primo elemento di un array di cui però non ho idea di quanti elementi avrà l'array e quindi non posso definirlo a priori?
oppure c'è un altro metodo?
insoma io devo costruire un array dentro una funzione, atrray che sarà poi visibile come variabile globale nel main e di cui non so quanto deve essere grosso a priori ma solo durante lo svolgimento della function stessa.Questo non è molto chiaro. Se una funzione deve operare su un array, deve almeno sapere, in qualche modo, quanti elementi contiene!!! Devi allocare dinamicamente l'array??? E poi cosa significa "visibile come variabile globale nel main"???
Spiega meglio cosa vorresti fare.
vermaccio
01-06-2006, 13:05
la situazione è questa: devo creare tramite un function un array bidimensionale nX2 (una matrice di 2 righe e n colonne).
iquesta matrice, una volta chiusa la funziona che la crea, deve esere visibile da altre funzioni che si attiveranno in seguito. quiandi la "matrice" deve essre una variabile globale.
il problem è che finchè la funzione creatirce non si attiva non saprò quante colonne "n" avrà la matrice.
quindi la domanda è:
data una function che al suo interno "dichiara" il numero di colonne......
come faccio nel main a dichiarare una variabile globale in cui mettere i dati della matrice?
qualsiasi soluzione va bene.
quindi
1)si attiva la funzione
2)nella funzione vine e detto, in base al suo scopo, che n=qualcosa (es:21) che varia ad ogni chiamata
3)viene riempita una matrice 2Xn
4)la funzione si chiude
come fare per avere fuori della funzione, una volta chiusa, quella matrice o un qualcosa del genere che contenga i dati della matrice?
Io farei così:
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
double **array;
int rows;
int columns;
} DOUBLE_2D;
DOUBLE_2D alloc_double_2d (int rows, int columns)
{
int i;
DOUBLE_2D double2d;
double2d.array = (double**) malloc (rows * sizeof (double*));
for (i = 0; i < rows; i++)
double2d.array[i] = (double*) malloc (columns * sizeof (double));
double2d.rows = rows;
double2d.columns = columns;
return double2d;
}
void free_double_2d (DOUBLE_2D *double2d)
{
int i;
for (i = 0; i < double2d->rows; i++)
free (double2d->array[i]);
free (double2d->array);
double2d->array = NULL;
double2d->rows = 0;
double2d->columns = 0;
}
DOUBLE_2D tua_funzione (void)
{
int n = 21;
DOUBLE_2D double2d;
/* .... */
double2d = alloc_double_2d (2, n);
return double2d;
}
int main (void)
{
DOUBLE_2D arr;
arr = tua_funzione ();
free_double_2d (&arr);
return 0;
}
Sull'utilizzo del puntatore ** per gestire delle matrici bidimensionali, se n'era già parlato in <questo> (http://www.hwupgrade.it/forum/showthread.php?t=1204340) thread.
Come avevo detto io, un array di questo tipo non è esattamente un array come inteso dal linguaggio "C". Perché i dati non sono tutti contigui in memoria, in pratica così è semplicemente "un array di array".
Ma in molti casi va bene, non frega niente se non è contiguo ma solo se la dimensionalità è uguale 2. ;)
vermaccio
01-06-2006, 14:16
mumble.. mumble...
quindi usi una struttura!
grande idea!
A)
DOUBLE_2D alloc_double_2d
alloca e crea la "matrice" rowXcolumns
B)
void free_double_2d (DOUBLE_2D *double2d)
cancella e dealloca la memoria occupata dalla matrice rowXcolumns prima creata
C)
DOUBLE_2D tua_funzione (void)
qui dentro richiamo la A per creare la matrice rowXcolumns in base ai valori row e columns che qui dentro vengono definiti
D)
ne main
free_double_2d (&arr);
uccide quella matrice.
domanda1:
------------
ma se non la uccido viene sovrascritta oppure rimane ad occupare memoria perchè la successiva chiamata della funzione A crea un altra matrice ma quella "vecchia" è ancora li ma senza puntatore per usarla? quindi se non cancello ogni volta la matrice creata alla lunga la memoria si riempie?
domanda2:
-----------
nella A
double2d.array = (double**) malloc (rows * sizeof (double*));
alloca la memoria necessaria per le row righe ognuna avente columns colonne. ma che significa "**" nel double?
domanda3:
-----------
sempre nella A:
for (i = 0; i < rows; i++)
double2d.array[i] = (double*) malloc (columns * sizeof (double));
riempie i campi dell'array.
domanda4:
------------
nel main
int main (void)
{
DOUBLE_2D arr; /definisco la "variabile" globale arr! ma non si sa ancora quanto è grande l "array" la dentro!
arr = tua_funzione ();
qui dentro riempio la variabile arr dopo averne definito il contenuto. visto che la arr è globale ogni modifica sarà visibile ancora dopo aver chiuso chiuso la funzione tua_funzione.
free_double_2d (&arr);
ammazzo la arr ora creata
return 0;
}
tutto ok?
ps: ci speravo fossi tu a rispondere al mio quesito! sei il mio micio preferito! :)
ma se non la uccido viene sovrascritta oppure rimane ad occupare memoria perchè la successiva chiamata della funzione A crea un altra matrice ma quella "vecchia" è ancora li ma senza puntatore per usarla? quindi se non cancello ogni volta la matrice creata alla lunga la memoria si riempie?Esatto, rimane lì allocata senza che il programma abbia dei riferimenti a quelle locazioni.
nella A
double2d.array = (double**) malloc (rows * sizeof (double*));
alloca la memoria necessaria per le row righe ognuna avente columns colonne. ma che significa "**" nel double?
sempre nella A:
for (i = 0; i < rows; i++)
double2d.array[i] = (double*) malloc (columns * sizeof (double));
riempie i campi dell'array.La struttura di un tale array è la seguente:
http://img476.imageshack.us/img476/1153/array2d9hf.gif
In pratica è "un array di array". C'è un primo array che contiene dei puntatori agli array della seconda dimensione. Ecco perché serve allocare prima un array delle righe che contengono double* e poi per ogni riga allocare un array di N elementi double.
double** perché solo così si può fare: double2d.array[r][c]
nel main
int main (void)
{
DOUBLE_2D arr; /definisco la "variabile" globale arr! ma non si sa ancora quanto è grande l "array" la dentro!
arr = tua_funzione ();
qui dentro riempio la variabile arr dopo averne definito il contenuto. visto che la arr è globale ogni modifica sarà visibile ancora dopo aver chiuso chiuso la funzione tua_funzione.
free_double_2d (&arr);
ammazzo la arr ora creata
return 0;
}arr non è globale! È una variabile locale del main. Se la vuoi globale la metti fuori dalle funzioni. La tua_funzione restituisce una copia della struttura, che viene quindi assegnata ad arr.
ps: ci speravo fossi tu a rispondere al mio quesito! sei il mio micio preferito! :)miaaao! :)
vermaccio
01-06-2006, 15:10
usi operativi della matrice "double2d" rowXcolumns:
1)
nella funzione
DOUBLE_2D tua_funzione (void)
se
row=2
columns=21
e se volessi settare gli elementi come faccio?
con un ciclo for.
ma come setto UN elemento dentro il ciclo for?
per dire che voglio che l'elemento riga 2, colonna 8 sia un double uguale a 999, come scrivo?
[tra parentesi: la numerazione va da 0 in poi per gli elementi?]
2)
e invece nel main se voglio leggere
l'elemento riga2 e colonne 12 di double2d ed assegnarlo alla variabile "double estratto;"?
3)
se devo passare ad un altra funzione la matrice bidimensionale, come gliela passo?
cioè se ho:
void altra_funzione (DOUBLE_2D interna) {
...
blabla
...
}
la richiamo dal main con
altra_funzione (double2d);
?
1)
nella funzione
DOUBLE_2D tua_funzione (void)
se
row=2
columns=21
e se volessi settare gli elementi come faccio?
con un ciclo for.
ma come setto UN elemento dentro il ciclo for?
per dire che voglio che l'elemento riga 2, colonna 8 sia un double uguale a 999, come scrivo?Se hai dichiarato un DOUBLE_2D arr devi usare arr.array[r][c].
[tra parentesi: la numerazione va da 0 in poi per gli elementi?]Sì.
2)
e invece nel main se voglio leggere
l'elemento riga2 e colonne 12 di double2d ed assegnarlo alla variabile "double estratto;"?Vedi sopra. Esempio: arr.array[2][12].
3)
se devo passare ad un altra funzione la matrice bidimensionale, come gliela passo?
cioè se ho:
void altra_funzione (DOUBLE_2D interna) {
...
blabla
...
}
la richiamo dal main con
altra_funzione (double2d);
?Esatto, così la passi "per valore" (passi una copia della struttura) ma la puoi passare anche tramite un puntatore. Dipende da cosa fa più comodo.
Nota: in ANSI "C" è permesso passare/ritornare strutture per valore.
vermaccio
01-06-2006, 15:30
A2)
(quesito3)
se NON voglio passarla per valore ma per qualcosa=indirizzo?
nel main la richiamo con
altra_funzione (&arr[0][0]);
?
e dentro la funzione
void altra_funzione (DOUBLE_2D* interna) {
con dentro la funzione cose del tipo
-->per assegnare
(*interna)=qualcosa; per modificare il primo elemento
e
(*interna+column+row*column)=qualcosa; per modificare l'elemento alla posizione row,column?
-->per leggere
qualcosa=(*interna);
e
qualcosa=(*interna+column+row*column);
???
B2)
inoltre essendo una variabile del main posso comunque evitare questa paranoia ed usare direttamente
definire
void altra_funzione2 (altre cose) {
e chiamarla con
altra_funzione2 (...);
al cui interno faccio diretamente
arr.array[row][column]=qualcosa;
e
qualcosa=arr.array[row][column];
???
A2)
(quesito3)
se NON voglio passarla per valore ma per qualcosa=indirizzo?
nel main la richiamo con
altra_funzione (&arr[0][0]);
?
e dentro la funzione
void altra_funzione (DOUBLE_2D* interna) {
con dentro la funzione cose del tipo
-->per assegnare
(*interna)=qualcosa; per modificare il primo elemento
e
(*interna+column+row*column)=qualcosa; per modificare l'elemento alla posizione row,column?
-->per leggere
qualcosa=(*interna);
e
qualcosa=(*interna+column+row*column); Per passarla:
altra_funzione (&arr);
arr è una struttura, NON il double** (che è invece contenuto all'interno della struttura).
nella funzione si userà quindi:
interna->array[r][c] = ....;
... = interna->array[r][c];
B2)
inoltre essendo una variabile del main posso comunque evitare questa paranoia ed usare direttamente
definire
void altra_funzione2 (altre cose) {
e chiamarla con
altra_funzione2 (...);
al cui interno faccio diretamente
arr.array[row][column]=qualcosa;
e
qualcosa=arr.array[row][column];Sì, ma solo se arr è globale, cioè dichiarato fuori dalle funzioni.
vermaccio
01-06-2006, 16:07
Per passarla:
altra_funzione (&arr);
arr è una struttura, NON il double** (che è invece contenuto all'interno della struttura).
nella funzione si userà quindi:
interna->array[r][c] = ....;
... = interna->array[r][c];
quindi così come per gli array passati per indirizzo (con la &) chiamando la funzione io faccio *(array+cella)
per una struttura passta con la & io faccio
struttura->campo[x][y]
se campo è un array bidimensionale.
Sì, ma solo se arr è globale, cioè dichiarato fuori dalle funzioni.
ok. è il mio caso. quindi invece di impazzirmi con passaggi "&" e le conseguenti "->" faccio prima così.
:)
vermaccio
01-06-2006, 16:27
sempre più contorto
sino ad ora abbiamo chiarito come creare e riempire una struttura con dentro un array bidimensionale le cui dimensioni sono decide dentro una funzione che poi la riempie anche.
Però si è dato per scontato che la funzione "decida" row e columns una volta per tutte, poi crea l'array rowXcolmns e lo riempie.
che fare nel caso in cui la funzione solo alla fine saprà columns MA (dolore!) a mano a mano che aggiunge columns deve salvare i dati?
esempio:
sono ad occhi chiusi in una stanza piena di sedie.
apro gli occhi e mano mano che giro su me stesso (sino a fare un giro completo di 360°) per ogni 10gradi di rotazione conto quante sedie vedo davanti a me.
alla fine columns=numero sedie viste.
Però nell'array bidimensionale devo stoccare, per ogni colonna:
riga1=numero totali gambe delle sedie che vedo in quel momento
riga2=numero totali delle sedie che vedo
io creo una nuova colonna solo se nel punto di osservazione istantaneo vedo sedie.
se non ne vedo non faccio nulla a ruotoun altro po.
se ad esempio ho solo 1 sedia alla mia sinistra ,due dietro, e una a destra: io avrò tre colonne.
4 8 4
1 2 1
con il metodo prima visto io avrò ALLA FINE columns=3
e creo array 2X3 e lo riempio...
ma...
con cosa?
io devo riempirlo MENTRE giro (quando non so che alla fine columns=3), NON a giro finito (quando so columns=3)!
quindi devo riempire a mano a mano la struttura senza sapere quanto è grossa!
una soluzione è far si che nella function:
1)giro su me stesso
2)a mano a mano stocco dati dentro un altra entità locale
3) a fine giro so il numero colonne
4)creo struuttura esterna come detto in tutto il resto del thread
5)copio valori da entità locale e struttura esterna
ma..
entità=????
siamo da capo:
che uso per stoccare dati a mano a mano senza sapere quanti saranno alla fine???????????
arghhhhhhhh!
sempre più contorto
sino ad ora abbiamo chiarito come creare e riempire una struttura con dentro un array bidimensionale le cui dimensioni sono decide dentro una funzione che poi la riempie anche.
Però si è dato per scontato che la funzione "decida" row e columns una volta per tutte, poi crea l'array rowXcolmns e lo riempie.
che fare nel caso in cui la funzione solo alla fine saprà columns MA (dolore!) a mano a mano che aggiunge columns deve salvare i dati?Ci sono diverse soluzioni.
Non puoi creare una volta sola un array di dimensioni "ragionevolmente" grandi in modo che ti vada bene???
Se questa soluzione non ti piace, non ti va bene o non ti basta, si può fare in un altro modo: creare una apposita funzione di riallocazione dell'array. Come gestire la riallocazione dell'array ... bisogna vedere. Il problema è questo: se ogni volta che devi aggiungere 1 elemento, allarghi le colonne dell'array ... credo che il tuo programma passerà più tempo a fare (ri)-allocazioni che altro!
Comunque ci penso un attimo. Risistemo il codice che avevo postato mettendo magari anche la gestione degli errori ... e poi riposto il codice.
Ci sono diverse soluzioni.
Non puoi creare una volta sola un array di dimensioni "ragionevolmente" grandi in modo che ti vada bene???
Se questa soluzione non ti piace, non ti va bene o non ti basta, si può fare in un altro modo: creare una apposita funzione di riallocazione dell'array. Come gestire la riallocazione dell'array ... bisogna vedere. Il problema è questo: se ogni volta che devi aggiungere 1 elemento, allarghi le colonne dell'array ... credo che il tuo programma passerà più tempo a fare (ri)-allocazioni che altro!
Comunque ci penso un attimo. Risistemo il codice che avevo postato mettendo magari anche la gestione degli errori ... e poi riposto il codice.
Non ho letto tutta la discussione, in ogni caso puoi usare vector<class T> della libreria standard. E' un vettore che si espande da solo ed è ottimizzato per usi generali. In questi casi di solito si alloca un quantità ragionevolmente grande che si provvede ad espandere quando serve. Puoi anche allocare il minimo indispensabile ma questo comporta un notevole sovraccarico per la cpu che si può evitare a costa di (poca) memoria.
Saluti
vermaccio
01-06-2006, 17:28
Non ho letto tutta la discussione, in ogni caso puoi usare vector<class T> della libreria standard. E' un vettore che si espande da solo ed è ottimizzato per usi generali. In questi casi di solito si alloca un quantità ragionevolmente grande che si provvede ad espandere quando serve. Puoi anche allocare il minimo indispensabile ma questo comporta un notevole sovraccarico per la cpu che si può evitare a costa di (poca) memoria.
Saluti
puoi postare un esempietto pratico?
comunque non posso permettermi sovraccarichi di cpu: il codice cpp una volta lanciato lancia altre applicazioni e già così un AMD2600+ fa fatica a stargli dietro.
e se la simulazione inizia a "scattare" è la fine!
preferisco sprecare memoria che avere sovraccarichi di cpu.
credo che la soluzione "a spanne" di creare un entità di appoggio sovrabbondante sia valida.
ad esempio
double entità[2][100];
magari nel 99% dei casi ne riempirò solo le prima 5 colonne.
a 100 non arriverà MAI.
Per quanto riguarda l'esempio scemo sull'uso del vector<T> eccoti questa cavolata:
#include <vector>
#include <iostream>
void stampaAllaC(const std::vector<int>& arr)
{
unsigned int i;
for (i = 0; i < arr.size(); i++)
{
std::cout << "vettore[" << i << "] = " << arr[i] << std::endl;
}
}
void stampaAllaCPP(const std::vector<int>& arr)
{
std::vector<int>::const_iterator it;
for (it = arr.begin(); it != arr.end(); ++it)
{
std::cout << *it << std::endl;
}
}
int main()
{
const int minLimit = 0;
const int maxLimit = 200;
int i;
std::vector<int> vect;
for (i = minLimit; i <= maxLimit; i++)
{
if ((i % 3) == 0)
{
vect.push_back(i);
}
}
std::cout << "Alla fine gli elementi nel vettore sono " << vect.size() << std::endl;
// puoi recuperare i dati con l'operatore [] come si fa in C
stampaAllaC(vect);
// oppure usare gli iteratori
stampaAllaCPP(vect);
return 0;
}
La potenza dei contenitori della Standard Template Library consiste nel fatto che:
1) li puoi usare non solo con i tipi "standard" (int come in questo caso, long, float, car ecc ecc) ma anche con tipi definiti da te (unica accortezza: per certi contenitori devi definire l'ogetto da memorizzare come una classe che abbia l'operatore di assegnazione (operator=), il costruttore di defult, il costruttore di copia e l'operatore di ordinamento < (operator<)
2) qui ho usato un "vector" che era quello che ti era gia' stato nominato: in realta' ne esistono numerosi altri (map, set...)
Uno che trovo molto utile e' la map<KEY,VAL> che e' un contenitore associativo.
Ti permette di associare una chiave (di qualsiasi tipo, anche definito da te) ad un valore da memorizzare (di qualsiasi tipo, anche definito da te).
Ad esempio se hai
std::map<std::string, float> coeff; // creo una mappa vuota
puoi memorizzare i coefficienti per la conversione in metri cosi';
// inizio a popolare la mappa
coeff[std::string("metro")] = 1.0;
coeff[std::string("kilometro")] = 1000.0;
coeff[std::string("decimetro")] = 0.1;
e anche qui hai a disposizione gli operatori per stampare il contenuto della mappa senza sapere quanti elementi ne fanno parte.
3) i contenitori hanno gia' operazioni quali ad esempio l'ordinamento, l'operatore di copia (quindi puoi copiare tutto un vettore semplicemente usando il carattee "=")
4) tramite i connettori (ma questo per favore non farmelo spigare) puoi "applicare" diverse operazioni su ogni elemento del contenitore senza scrivere il solito ciclo per scorrere tutti gli elementi presenti nel contenitore
5) come gia' detto prima gli elementi e le chiavi possono essere di qualsiasi tipo.
Questo permette anche di memorizzare un contenitore in un contenitore, ovvero
puoi ad esempio creare vettori di mappe e mappe di mappe di mappe (giuro, l'ho visto fare)
Questi sono solo i primi vantaggi che mi sono venuti in mente.
Altra cosa: spesso si dice che il C++ e' piu' lento (in termini di tempi di esecuzione) rispetto al C. Per esperienza personale (con il C e C++ ci campo da una vita), e stando anche a quanto affermato piu' volte da B. Stroustrup (speriamo stavolta di averlo scritto bene... e' l'inventore del C++) , SE IL CMPILATORE CHE USI E' FATTO BENE e se hai scritto in maniera decente il tuo programma, non dovresti avere problemi di prestazioni negative rispetto all'analogo algoritmo implementato in C.
IMHO se non usi le classi e tutto il mondo del C++, e' uno spreco usare il C++ per far girare un algoritmo scritto in C.
Ciao,
Mr Hyde
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.