Je suis nouveau dans les pointeurs et j'essaie d'implémenter un code simple, dans lequel je lis et écris un tableau 2D, mais je n'ai aucune idée pourquoi cela ne fonctionne pas. Quelqu'un pourrait-il fournir des suggestions?

#include<stdio.h>
#include<stdlib.h>

int read_matr(int ***m, FILE *f){
    int i,j,length,a;
    fscanf(f,"%d",&length);
    *m = (int **)malloc(length*sizeof(int));
    for(i=0;i<length;++i)
        for(j=0;j<length;++j){
                fscanf(f,"%d",&a);
                *(*(m+i)+j) = a;
        }
    return length;
}

void write_matr(int *m[][3], int length, FILE *g){
    int i,j;
    for(i=0;i<length;++i){
        for(j=0;j<length;++j)
            fprintf(g,"%d ",*(*(m+i)+j));
        fprintf(g,"\n");
    }
    fprintf(g,"\n");
}

int main(){
    FILE *f = fopen("datein.txt","r");
    FILE *g = fopen("dateout.txt","w");
    int **m, n = read_matr(m,f);
    write_matr(m,n,g);
    free(m);
    return 0;
}
0
theSongbird 27 janv. 2017 à 17:43

4 réponses

Meilleure réponse

Il y a au moins deux problèmes avec le code fourni. :

  • Vous passez une variable ** int comme premier paramètre de read_matr, mais cette fonction demande un paramètre *** int (un pointeur vers un tableau à 2 dimensions). Le paramètre semble correct, mais vous devez ensuite passer un pointeur vers votre tableau de cette manière:

    int n = read_matr(&m, f);
    
  • Votre tableau est censé avoir une dimension 2D, mais vous ne l'allouez qu'en tant que tableau 1 dimension. Après avoir alloué un premier tableau, vous devez allouer chaque ligne indépendamment:

    *m = (int **) malloc(length * sizeof(int*));
    for(i=0; i<length; ++i) {
        (*m)[i] = (int*) malloc(length * sizeof(int));
        ...
    }
    

En option, vous pouvez créer un tableau 1dimensionnel de taille longueur * longueur, puis vous accéderez à m[i][j] by m[i * length + j]

Les avertissements auraient pu vous aider beaucoup là-bas.

1
Rémi Bonnet 28 janv. 2017 à 02:20

Je vous suggère de regarder les avertissements du compilateur. Il y en a beaucoup dans ce code. Avec l'aide de ceux-ci, vous devriez être en mesure de résoudre certaines choses par vous-même.

Ce que je ne comprends pas. Vous définissez int **m et donnez-le non initialisé à la fonction read_matr(int ***m, FILE *f), mais ce que vous devez dans ce cas serait read_matr(&m, f);

MODIFIER

Voici donc quelques erreurs que vous avez commises.

  1. Veuillez ne pas transtyper les retours malloc du pointeur.

    int **m;
    *m = malloc(length*sizeof(int));
    

Sachez que ce que vous obtenez est un pointeur vers un pointeur vers la mémoire allouée. Ce n'est pas égal à m[][]. Si vous souhaitez allouer un tableau 2D à votre façon, vous devez faire quelque chose comme ceci:

int **m;
int rows, cols;
*m = malloc(sizeof(rows) * rows);
for(int i = 0; i < rows; i++){
    m[i] = malloc(sizeof(cols) * cols);
}

Voir: Utilisation de malloc pour l'allocation de plusieurs -des tableaux dimensionnels avec différentes longueurs de ligne

1
Community 23 mai 2017 à 12:01

Pour allouer correctement un tableau 2D en C moderne, procédez comme suit:

int (*array)[x][y] = malloc( sizeof(int[x][y]) );

Qui pourrait être simplifié par commodité comme:

int (*array)[y] = malloc( sizeof(int[x][y]) );

Pour allouer correctement un tableau 2D dans les anciennes versions de C (C90 / ANSI-C), vous utiliseriez des "tableaux mutilés":

int* array = malloc(x*y*z*sizeof(int));

Ce qui précède sont les seuls moyens d'allouer dynamiquement un vrai tableau 2D en C. Tout ce qui est basé sur un pointeur à pointeur n'est pas un tableau 2D, ni ne peut être utilisé comme tel. Cela est dû au fait que de telles "choses" pointeur à pointeur sont dispersées dans tout le tas. Ils sont lents et inutilement complexes.

2
Lundin 27 janv. 2017 à 15:22

Ma première suggestion serait de séparer la gestion de la mémoire des E / S. Créez d'abord votre matrice, et une fois que vous avez vérifié que cela a réussi, puis lisez votre entrée.

Ma deuxième suggestion serait d'utiliser la notation en indice régulier (m[i][j]) au lieu des déréférences de pointeur explicites (*(*(m + i) + j)). Plus facile à lire, plus difficile à se tromper.

Enfin, lorsque vous allouez de la mémoire de cette façon, vous devez allouer de la mémoire pour chaque ligne individuellement:

void createMatrix( int ***m, size_t length )
{
  *m = malloc( sizeof **m * length );
  if ( !*m )
  {
    // memory allocation error, bail out here
    return;
  }

  size_t i;
  for ( i = 0; i < length; i++ )
  {
    /**
     * Postfix [] has higher precedence than unary *, so *m[i] will be
     * parsed as *(m[i]) - it will index into m and dereference the result.
     * That's not what we want here - we want to index into what m 
     * *points to*, so we have to explicitly group the unary * operator
     * with m and index into the result.
     */
    (*m)[i] = malloc( sizeof *(*m)[i] * length );
    if ( !(*m)[i] )                              
      break;
  }

  if ( i < length )
  {
    // memory allocation failed midway through allocating the rows;
    // free all previously allocated memory before bailing out
    while ( i-- )
      free( (*m)[i] );
    free( *m );
    *m = NULL;
  }
}

En supposant que vous l'appeliez comme

int **m;
createMatrix( &m, 3 ); // note & operator on m

Vous vous retrouvez avec quelque chose qui ressemble à ceci:

     +---+                                                  +---+
m[0] |   | ---------------------------------------> m[0][0] |   |
     +---+                                +---+             +---+
m[1] |   | ---------------------> m[1][0] |   |     m[0][1] |   |
     +---+               +---+            +---+             +---+
m[2] |   | ----> m[2][0] |   |    m[1][1] |   |     m[0][2] |   |
     +---+               +---+            +---+             +---+
      ...        m[2][1] |   |    m[1][2] |   |              ...
                         +---+            +---+
                 m[2][2] |   |             ...
                         +---+
                          ...

Ce n'est pas un vrai tableau 2D - les lignes ne sont pas contiguës. L'élément qui suit immédiatement m[0][2] n'est pas m[1][0]. Vous pouvez l'indexer comme si c'était un tableau 2D, mais c'est à peu près tout.

Parfois, ce n'est pas la mauvaise réponse. En fonction de la fragmentation de votre tas, une seule demande d'allocation N x M peut échouer, mais N allocations séparées de M peuvent réussir. Si votre algorithme ne repose pas sur la contiguïté de tous les éléments, cela fonctionnera, même si ce sera probablement plus lent que d'utiliser un vrai tableau N x M.

Si vous voulez un vrai tableau 2D contigu dont les dimensions ne sont pas connues avant l'exécution, vous aurez besoin d'un compilateur qui prend en charge les tableaux de longueur variable (VLA), et vous l'allouerez comme suit:

size_t length = get_length_from( f );
int (*m)[length] = malloc( sizeof *m * length ); // sizeof *m == sizeof (int [length])

m est un pointeur vers un length - tableau d'éléments de int, et nous allouons suffisamment de mémoire pour length de tels tableaux. En supposant que length est à nouveau 3, vous vous retrouvez avec quelque chose qui ressemble à ceci:

        +---+
m[0][0] |   |
        +---+ 
m[0][1] |   |
        +---+ 
m[0][2] |   |
        +---+ 
m[1][0] |   |
        +---+ 
m[1][1] |   |
        +---+ 
m[1][2] |   |
        +---+ 
m[2][0] |   |
        +---+ 
m[2][1] |   |
        +---+ 
m[2][2] |   |
        +---+      

Si les VLA ne sont pas disponibles (versions antérieures à C99 ou C2011 où __STDC_NO_VLA__ est défini) et vous voulez que tous les éléments de votre tableau soient contigus, vous devrez l'allouer en tant que Tableau 1D:

size_t length = 3;
int *m = malloc( sizeof *m * length * length );

Vous pouvez configurer un tableau supplémentaire de pointeurs pour faire semblant m est un tableau 2D:

int *q[] = { &m[0], &m[3], &m[6] };

Donc

q[0][0] == m[0];
q[0][1] == m[1];
q[0][2] == m[2];
q[1][0] == m[3];
q[1][1] == m[4];
...

Etc.

0
John Bode 27 janv. 2017 à 17:14