Repository 32bit  Forum
Repository 64bit  Wiki

[C] Problema doppio puntatore

Forum dedicato alla programmazione.

Moderatore: Staff

Regole del forum
1) Citare in modo preciso il linguaggio di programmazione usato.
2) Se possibile portare un esempio del risultato atteso.
3) Leggere attentamente le risposte ricevute.
4) Scrivere i messaggi con il colore di default, evitare altri colori.
5) Scrivere in Italiano o in Inglese, se possibile grammaticalmente corretto, evitate stili di scrittura poco chiari, quindi nessuna abbreviazione tipo telegramma o scrittura stile SMS o CHAT.
6) Appena registrati è consigliato presentarsi nel forum dedicato.

La non osservanza delle regole porta a provvedimenti di vari tipo da parte dello staff, in particolare la non osservanza della regola 5 porta alla cancellazione del post e alla segnalazione dell'utente. In caso di recidività l'utente rischia il ban temporaneo.

[C] Problema doppio puntatore

Messaggioda elvis » mer gen 05, 2011 12:15

Salve, non riesco a uscire da questo problema: ho una funzione che deve restituire una matrice, tramite un doppio puntatore, al main che dovrà poi stamparla.

Il codice è questo:

Codice: Seleziona tutto
#include <stdio.h>
#include <stdlib.h>

int **prova(int N); // prototipo funzione

int main()
{
   int NN = 4, i, j;  // NN è il numero numero di righe-colonne della matrice quadrata NNxNN
   
   int **y;
   
   y = prova(NN);
   
   printf("**y = %d *(*y + 2) = %d\n", **y, *(*y + 2));  // Qui **y è ok, e mi stampa il valore esatto a cui punta
   
   printf("Stampa matrice dentro il main:\n");   
   for (i = 0; i < NN; i++) {                               
     
      for(j = 0; j < NN; j++) printf("%3d ", *(*y + NN*i + j));   // Qui invece è come se **y perdesse l'indirizzo che contiene come valore..
     
      printf("\n");
   }           
   return 0;
}

int **prova(int N)
{
   int i, j, t = 2;
   
   int *Q = malloc((N*N)*sizeof(int));   // alloca la memoria per la matrice NxN
     
   int **M = &Q;
   
   for (i = 0; i < N; i++) {       // riempie la matrice con valori di prova (cioè t++)
     
      for(j = 0; j < N; j++)   
   
    *(*M + N*i + j) = t++;
   }
   
   printf("Stampa matrice dentro la funzione:\n");   
   for (i = 0; i < N; i++) {
     
      for(j = 0; j < N; j++) printf("%3d ", *(*M + N*i + j));
     
      printf("\n");
   }   
   
   int *PP = &M[0][0];   // Puntatori ausiliari
   
   int **G = &PP;
   
   return G;
}


L'output a schermo è il seguente:

bash-4.1$ a.out
Stampa matrice dentro la funzione:
2 3 4 5
6 7 8 9
10 11 12 13
14 15 16 17
**y = 2 *(*y + 2) = 4
Stampa matrice dentro il main:
Segmentation fault
bash-4.1$


Come potete vedere, è come se entrando nei due for nel main, **y perdesse l'indirizzo a cui punta, mentre il printf precedente stampa correttamente i due valori (0,0) e (0,2) della matrice "ricevuta".
Suggerimenti?
elvis
Linux 1.0
Linux 1.0
 
Messaggi: 53
Iscritto il: lun set 27, 2010 9:35
Slackware: 13.1 x64
Desktop: KDE 4.5.2
Distribuzione: Opensuse 11.3 x86

Re: [C] Problema doppio puntatore

Messaggioda 414N » mer gen 05, 2011 13:31

elvis ha scritto:
Codice: Seleziona tutto
  [...]
   int *PP = &M[0][0];   // Puntatori ausiliari
   
   int **G = &PP;
   
   return G;
}


A naso direi che l'errore è proprio qua.
Nella funzione che crea la matrice hai aggiunto queste due righe per "dilettarti" con i puntatori, assegnando prima a PP l'indirizzo del primo elemento della matrice, poi a G l'indirizzo del puntatore PP, che è una variabile locale alla funzione il cui contenuto è una copia dell'indirizzo del primo elemento della matrice. Per finire, restituisci al main G invece di M, come sarebbe logico presupporre. Come certamente saprai, all'uscita da una funzione tutte le variabili locali usate al suo interno cessano di esistere (o meglio, verranno sovrascritte nei prossimi accessi allo stack da parte di una qualsiasi funzione), quindi dall'esterno (dal main, in questo caso) non puoi usare puntatori a variabili locali di funzioni.
Per risolvere il tuo problema, ho modificato nel seguente modo il tuo codice:
  • Ho reso globali il puntatore alla matrice e un puntatore temporaneo usato per l'allocazione dinamica (e basta). In questo modo, non corri il rischio di andare a puntare a roba che rischia di scomparire nel cambiamento di scope.
  • Ho aggiunto un controllo alla malloc. Bisogna SEMPRE controllare che l'allocazione sia andata a buon fine.
Codice: Seleziona tutto
#include <stdio.h>
#include <stdlib.h>

int **M = NULL;
int *temp = NULL;

void prova(int N); // prototipo funzione

int main()
{
        int NN = 4, i, j;  // NN è il numero numero di righe-colonne della matrice quadrata NNxNN

        prova(NN);

        printf("**M = %d *(*M + 2) = %d\n", **M, *(*M + 2));  // Qui **M è ok, e mi stampa il valore esatto a cui punta

        printf("Stampa matrice dentro il main:\n");   
        for (i = 0; i < NN; i++) {                               

                for(j = 0; j < NN; j++) printf("%3d ", *(*M + NN*i + j));   // Qui invece è come se **M perdesse l'indirizzo che contiene come valore..

                printf("\n");
        }           
        return 0;
}

void prova(int N)
{
        int i, j, t = 2;

        temp = malloc((N*N)*sizeof(int));   // alloca la memoria per la matrice NxN

        if ( temp == NULL){
                fprintf (stderr, "Errore nell'allocazione della matrice nello heap.\n");
                exit (1);
        }

        M = &temp;

        for (i = 0; i < N; i++) {       // riempie la matrice con valori di prova (cioè t++)

                for(j = 0; j < N; j++)   

                        *(*M + N*i + j) = t++;
        }

        printf("Stampa matrice dentro la funzione:\n");   
        for (i = 0; i < N; i++) {

                for(j = 0; j < N; j++) printf("%3d ", *(*M + N*i + j));

                printf("\n");
        }   

        int *PP = &M[0][0];   // Puntatori ausiliari

        int **G = &PP;

}

Per finire, un consiglio: per non perderti con tutti questi puntatori, ti conviene usare delle typedef per astrarre un po' i tipi.
Nel caso di matrici, potresti pensare di usare delle struct per ospitare, oltre alla matrice stessa, informazioni quali il numero di righe e colonne (in modo da generalizzare e poter trattare indifferentemente matrici rettangolari), il determinante ecc.
Avatar utente
414N
Iper Master
Iper Master
 
Messaggi: 2882
Iscritto il: mer feb 13, 2008 16:19
Località: Bulagna
Slackware: 14.0 (x64)
Kernel: 3.2.29
Desktop: LXDE

Re: [C] Problema doppio puntatore

Messaggioda Barone Rosso » mer gen 05, 2011 14:21

Codice: Seleziona tutto
int *Q = malloc((N*N)*sizeof(int));   // alloca la memoria per la matrice NxN
     
   int **M = &Q;

:shock: :shock: :shock: :shock: :shock: :shock: :shock: :shock: :shock: :shock: :shock: :shock:

Hai allocato un vettore e pretendi di usarlo come un vettore di puntatori?
Mi sembra ovvio che ti vada in segmentation fault!

se lo vuoi usare come matrice NxN; dati
i, j = 0,1,2, ... N-1
Codice: Seleziona tutto
a = Q[i*N+j]
Q[i*N+j] = b


Se vuoi un vettore di vettori, la allocazione la devi fare in questo modo:
Codice: Seleziona tutto
int **M = malloc((N)*sizeof(int*));
for ( i=0 ; i<N ; i++ ){
    M[i] = malloc((N)*sizeof(int));
}

M[i][j] = a ;


Chiaro?
Avatar utente
Barone Rosso
Linux 1.0
Linux 1.0
 
Messaggi: 58
Iscritto il: mer set 15, 2010 7:11
Distribuzione: ArchLinux

Re: [C] Problema doppio puntatore

Messaggioda elvis » mer gen 05, 2011 15:07

414N ha scritto:A naso direi che l'errore è proprio qua.
Nella funzione che crea la matrice hai aggiunto queste due righe per "dilettarti" con i puntatori, assegnando prima a PP l'indirizzo del primo elemento della matrice, poi a G l'indirizzo del puntatore PP, che è una variabile locale alla funzione il cui contenuto è una copia dell'indirizzo del primo elemento della matrice. Per finire, restituisci al main G invece di M, come sarebbe logico presupporre. Come certamente saprai, all'uscita da una funzione tutte le variabili locali usate al suo interno cessano di esistere (o meglio, verranno sovrascritte nei prossimi accessi allo stack da parte di una qualsiasi funzione), quindi dall'esterno (dal main, in questo caso) non puoi usare puntatori a variabili locali di funzioni.
Per risolvere il tuo problema, ho modificato nel seguente modo il tuo codice:
  • Ho reso globali il puntatore alla matrice e un puntatore temporaneo usato per l'allocazione dinamica (e basta). In questo modo, non corri il rischio di andare a puntare a roba che rischia di scomparire nel cambiamento di scope.
  • Ho aggiunto un controllo alla malloc. Bisogna SEMPRE controllare che l'allocazione sia andata a buon fine.
Codice: Seleziona tutto
#include <stdio.h>
#include <stdlib.h>

int **M = NULL;
int *temp = NULL;

void prova(int N); // prototipo funzione

int main()
{
        int NN = 4, i, j;  // NN è il numero numero di righe-colonne della matrice quadrata NNxNN

        prova(NN);

        printf("**M = %d *(*M + 2) = %d\n", **M, *(*M + 2));  // Qui **M è ok, e mi stampa il valore esatto a cui punta

        printf("Stampa matrice dentro il main:\n");   
        for (i = 0; i < NN; i++) {                               

                for(j = 0; j < NN; j++) printf("%3d ", *(*M + NN*i + j));   // Qui invece è come se **M perdesse l'indirizzo che contiene come valore..

                printf("\n");
        }           
        return 0;
}

void prova(int N)
{
        int i, j, t = 2;

        temp = malloc((N*N)*sizeof(int));   // alloca la memoria per la matrice NxN

        if ( temp == NULL){
                fprintf (stderr, "Errore nell'allocazione della matrice nello heap.\n");
                exit (1);
        }

        M = &temp;

        for (i = 0; i < N; i++) {       // riempie la matrice con valori di prova (cioè t++)

                for(j = 0; j < N; j++)   

                        *(*M + N*i + j) = t++;
        }

        printf("Stampa matrice dentro la funzione:\n");   
        for (i = 0; i < N; i++) {

                for(j = 0; j < N; j++) printf("%3d ", *(*M + N*i + j));

                printf("\n");
        }   

        int *PP = &M[0][0];   // Puntatori ausiliari

        int **G = &PP;

}

Per finire, un consiglio: per non perderti con tutti questi puntatori, ti conviene usare delle typedef per astrarre un po' i tipi.
Nel caso di matrici, potresti pensare di usare delle struct per ospitare, oltre alla matrice stessa, informazioni quali il numero di righe e colonne (in modo da generalizzare e poter trattare indifferentemente matrici rettangolari), il determinante ecc.

Mi sono dimenticato di specificare che non posso modificare l'intestazione della funzione (in questo caso renderla una f. void), deve restituire per forza un doppio puntatore.
Provando a levare i due puntatori prima del return, e ritornando M, l'errore (ovviamente?) rimane.

Ok per verifica malloc e typedef.


Barone Rosso ha scritto:
Codice: Seleziona tutto
int *Q = malloc((N*N)*sizeof(int));   // alloca la memoria per la matrice NxN
     
   int **M = &Q;

:shock: :shock: :shock: :shock: :shock: :shock: :shock: :shock: :shock: :shock: :shock: :shock:

Hai allocato un vettore e pretendi di usarlo come un vettore di puntatori?
Mi sembra ovvio che ti vada in segmentation fault!

se lo vuoi usare come matrice NxN; dati
i, j = 0,1,2, ... N-1
Codice: Seleziona tutto
a = Q[i*N+j]
Q[i*N+j] = b


Se vuoi un vettore di vettori, la allocazione la devi fare in questo modo:
Codice: Seleziona tutto
int **M = malloc((N)*sizeof(int*));
for ( i=0 ; i<N ; i++ ){
    M[i] = malloc((N)*sizeof(int));
}

M[i][j] = a ;


Chiaro?

Non del tutto, non comprendo cosa significhi quell' a = Q[..] = b.
Stai semplicemente dicendo che dopo aver fatto quel malloc "indentato" (con quel for), dentro M[i][j] posso metterci gli interi che mi pare?

EDIT: comunque gestendo il malloc in questo modo funziona, grazie!
elvis
Linux 1.0
Linux 1.0
 
Messaggi: 53
Iscritto il: lun set 27, 2010 9:35
Slackware: 13.1 x64
Desktop: KDE 4.5.2
Distribuzione: Opensuse 11.3 x86

Re: [C] Problema doppio puntatore

Messaggioda Barone Rosso » mer gen 05, 2011 15:17

elvis ha scritto:Non del tutto, non comprendo cosa significhi quell' a = Q[..] = b.
Stai semplicemente dicendo che dopo aver fatto quel malloc "indentato" (con quel for), dentro M[i][j] posso metterci gli interi che mi pare?

EDIT: comunque gestendo il malloc in questo modo funziona, grazie!


Caso 1)
Q = malloc( N*N , sizeof(int) ) ;


poi usi Q in questo modo.
ad esempio:
Q[i*N+j] = 23 ;

Usi un vettore come se fosse una matrice, si tratta di una tecnica molto diffusa ed in genere preferita alla costruzione di un vettore di vettori.

Caso 2)
M[i] ... è un vettore di int* (puntatori a int).
mentre M[i][j] ... è la tua matrice, che di fatto è un vettore di vettori.

Consiglio: se vuoi fare un matrice ... usa il metodo 1.
Avatar utente
Barone Rosso
Linux 1.0
Linux 1.0
 
Messaggi: 58
Iscritto il: mer set 15, 2010 7:11
Distribuzione: ArchLinux

Re: [C] Problema doppio puntatore

Messaggioda elvis » mer gen 05, 2011 15:24

Molto interessante, grazie!

Però strano, forse mi è sfuggito ma nel Deitel non ho mai visto citato il caso 1, devo vedere se invece ci sia sul Kernighan...
elvis
Linux 1.0
Linux 1.0
 
Messaggi: 53
Iscritto il: lun set 27, 2010 9:35
Slackware: 13.1 x64
Desktop: KDE 4.5.2
Distribuzione: Opensuse 11.3 x86

Re: [C] Problema doppio puntatore

Messaggioda Barone Rosso » mer gen 05, 2011 17:19

elvis ha scritto:Molto interessante, grazie!

Però strano, forse mi è sfuggito ma nel Deitel non ho mai visto citato il caso 1, devo vedere se invece ci sia sul Kernighan...


Non penso che lo citino. Il caso 1 è solo un vettore con una strategia di indicizzazione.
In un testo più specifico a problematiche di ingegneria del software (per applicazioni numeriche) lo citano di sicuro.

Tecnicamente parlando il caso 2 è una matrice.
Avatar utente
Barone Rosso
Linux 1.0
Linux 1.0
 
Messaggi: 58
Iscritto il: mer set 15, 2010 7:11
Distribuzione: ArchLinux

Re: [C] Problema doppio puntatore

Messaggioda 414N » mer gen 05, 2011 18:19

elvis ha scritto:Provando a levare i due puntatori prima del return, e ritornando M, l'errore (ovviamente?) rimane.

Il problema è che tenti di ritornare l'indirizzo di una variabile locale di una funzione, e questo ti darà sempre problemi. Se non usi variabili globali, fai fatica ad uscirne.
Adottando una strategia simile a quella che ti ho presentato, puoi risolvere ritornando direttamente &temp (se la malloc è andata a buon fine), altrimenti NULL se non è andata a buon fine. Poi gestisci la situazione nel main (in pratica, incapsuli la malloc). In questo modo, la signature della funzione (che hai detto essere non modificabile) non cambia.

Barone Rosso ha scritto:Caso 1)
Q = malloc( N*N , sizeof(int) ) ;
poi usi Q in questo modo.
ad esempio:
Q[i*N+j] = 23 ;

Usi un vettore come se fosse una matrice, si tratta di una tecnica molto diffusa ed in genere preferita alla costruzione di un vettore di vettori.

Alla fine è proprio quello che succede nei for innestati sia nel main che nella funzione, se ci guardi bene.
Certo, con le [] e un passaggio di puntamento in meno sarebbe stato un po' più leggibile e meno contorto...
Avatar utente
414N
Iper Master
Iper Master
 
Messaggi: 2882
Iscritto il: mer feb 13, 2008 16:19
Località: Bulagna
Slackware: 14.0 (x64)
Kernel: 3.2.29
Desktop: LXDE

Re: [C] Problema doppio puntatore

Messaggioda Calzo » mar mar 01, 2011 21:17

Secondo me invece il problema è che la matrice definita come
Codice: Seleziona tutto
int **M;
è un vettore di vettori, tant'è che poi la indirizzi con M[x][y], ossia l'elemento y puntato dal vettore di interi M[x].
Io ho provato a scrivere questo esempio e funziona (spero ti sia di aiuto):
Codice: Seleziona tutto
#include <stdio.h>
#include <stdlib.h>

#define DIM   4

int **genera_matrice(int N)
{
    int i,j;
    int **ret = malloc(N*sizeof(int));

    for (i=0; i<N; i++)
        ret[i] = malloc(N*sizeof(int));
   
    // Riempio la matrice e la stampo
    for (i=0; i<N; i++)
    {
        for (j=0; j<N; j++)
        {
        ret[i][j] = i*j;
        printf("%d\t", ret[i][j]);
        }
        printf("\n");
    }

    return ret;
}

int main(void)
{
    int i,j;
    int **M;
   
    M = genera_matrice(DIM);

    printf("\n");
    for (i=0; i<DIM; i++)
    {
        for (j=0; j<DIM; j++)
            printf("%d\t", M[i][j]);
        printf("\n");
    }
}

In altre parole la funzione genera_matice() (l'equivalente della tua funzione prova()), prima genera il primo vettore, il quale è un vettore di puntatori, poi crea tutti gli altri vettori.
Il mio programma è solo di esempio: non fa controlli sulla mamoria (come giustamente ti hanno suggerito prima) e non libera la memoria in uscita (che ovviemente dovrà liberare tutti i vettori e poi la matrice finale).

Bye
Avatar utente
Calzo
Linux 2.0
Linux 2.0
 
Messaggi: 112
Iscritto il: sab ott 06, 2007 21:21
Località: MN
Slackware: 10.2 | 13
Desktop: Fluxbox | KDE


Torna a Programmazione

Chi c’è in linea

Visitano il forum: Nessuno e 0 ospiti