Allocation dynamique d'une matrice inconnue en C




arrays matrix (2)

Je ne vois pas où vous lisez réellement le nombre de lignes / colonnes, mais une fois que vous les avez, l'attribution est simple:

int (*matrix)[columnCount] = malloc(rowCount*sizeof(*matrix));

C'est tout. Cela déclare que matrix est un pointeur *matrix sur un tableau d'entiers columnCount . Les parenthèses sont nécessaires car int* matrix[...] déclarerait plutôt un tableau de pointeurs. Le malloc() alloue de l'espace pour de tels tableaux rowCount , vous donnant la matrice 2D complète dans une seule mémoire. L'accès est comme pour n'importe quel tableau 2D:

for(int y = 0; y < rowCount; y++) {
    for(int x = 0; x < columnCount; x++) {
        matrix[y][x] = 42;
    }
}

La distribution est aussi simple que l'allocation:

free(matrix);

Je dois prendre un fichier qui est entré par l'utilisateur et le multiplier par un autre fichier. C'est ce que je sais faire.

Le problème est qu'un fichier est un tableau et l'autre est une matrice.

J'ai besoin de scanner dans la première ligne de la matrice pour trouver la taille de la matrice, puis d'affecter dynamiquement la matrice et le tableau à partir des fichiers.

Voici ce que j'ai jusqu'à présent:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
int main()
{       
    int row1, col1;
        //These values need to be pulled from the first file//
    char filename1[100];
        //Setting the file name for entry and setting the limit to 100//
    FILE* fp1;
        //FILE must be set as a pointer (FILE must also be capitalized)//

    printf("Enter file name including file extension: \n");
        //This will pull in the name entered by the user//
    scanf("%s", filename1);
        //Scans in the name of the first file//

    fp1 = fopen(filename1, "r");
        //This will open the file as entered by the user//
    if (fp1 == NULL)
    {
        printf("\nError, file not found\n");
        exit(0);
    }
        //This is for the first file//

    char filename2[100];
        //Setting the file name for entry and setting the limit to 100//
    FILE* fp2;
        //FILE must be set as a pointer (FILE must also be capitalized)//

    printf("Enter file name including file extension: \n");
        //This will pull in the name entered by the user//
    scanf("%s", filename2);
        //Scans in the name of the first file//

    fp2 = fopen(filename2, "r");
        //This will open the file as entered by the user//
    if (fp2 == NULL)
    {
        printf("\nError, file not found\n");
        exit(0);
    }
        //This is for the second file//

        //**I need to now dynamically allocate the input files**//

    return 0;
} 

Je suis également désolé de paraître comme je viens de partir après avoir posté ma question, car certains membres ont partagé des commentaires dans lesquels ils disaient que je pêchais dans le code. Je ne suis pas; Je n'avais tout simplement pas réalisé à quel point cette communauté était active. Merci pour l'entrée à ce jour.

Voici la capture d'écran de tout ce que j'ai à ce jour, y compris les fichiers qui vont être lus.

Merci pour les suggestions. J'ai été capable de comprendre la fonction "fgets" et je l'ai utilisée pour extraire la taille de la matrice du premier fichier. Après cela, l’allocation dynamique était facile.


Ma recommandation est de considérer votre matrice comme ayant un type de données abstrait que vous souhaitez implémenter.

Une méthode courante consiste à utiliser un tableau de pointeurs (des tableaux représentant les lignes de votre matrice). Mais je pense que c'est déroutant et inefficace.

Alors, quelles sont les opérations que vous voulez sur vos matrices?

  • créer une matrice de dimensions données

  • détruire une matrice créée précédemment

  • accéder à un élément d'une matrice donnée avec des index de lignes et de colonnes donnés

  • changer la valeur d'un élément dans une matrice donnée avec des index de lignes et de colonnes donnés

  • etc....

BTW, vous pouvez en avoir plusieurs variantes. Par exemple, vous pouvez vérifier les erreurs (par exemple, rejeter un index négatif) ou disposer de fonctions dangereuses (mais légèrement plus rapides) capables d' un comportement indéfini (ce qui est très scary ). Bien sûr, vous pouvez définir plus d'opérations (en utilisant d'autres), par exemple la multiplication de matrice, etc.

Vous devez répertorier - sur papier ou sur tableau - toutes les opérations souhaitées sur vos matrices et les expliquer dans votre documentation (ou vos commentaires). En pratique, vous pouvez effectuer plusieurs dizaines, voire des centaines d'opérations sur votre type de données abstrait. Documentez également ce qui se passe dans les cas d'erreur.

Je recommande généralement de conserver les dimensions avec la matrice (sauf si vous savez qu'une partie de la dimension est une constante). Un moyen courant d'implémenter des types de données abstraits en C consiste à les encapsuler dans une struct et à utiliser des pointeurs sur celles-ci.

Je suggère donc d'utiliser un membre de tableau flexible (en tant que dernier élément de votre struct ). Voici ma structure matrix_st :

  struct matrix_st {
    unsigned m_h, m_w; // height and width of matrix
    double m_v[]; // values inside the matrixes, there are m_h*m_w of them
  };

donc mon type de données abstrait est juste des pointeurs vers

  typedef struct matrix_st Matrix;

Voici les déclarations des fonctions implémentant mon type de données abstrait:

  Matrix* matrix_create(unsigned height, unsigned width);
  void matrix_destroy(Matrix*mat);
  double matrix_access(Matrix*mat, unsigned i, unsigned j);
  void matrix_change_element(Matrix*mat, unsigned i, unsigned j,double v);

Voici quelques implémentations (puisque je ne veux pas traiter de matrices pathologiquement énormes, je définis une dimension maximale; les ressources informatiques sont toujours finies!):

  #define MATRIX_MAXDIM 10000000 /* ten millions */
  Matrix* matrix_create(unsigned height, unsigned width) {
     if (height>MATRIX_MAXDIM || width>MATRIX_MAXDIM) {
        fprintf(stderr, "too huge matrix height=%u width=%u\n",
                height, width);
        exit(EXIT_FAILURE);
     };
     Matrix* res = 
        calloc(1, sizeof(Matrix) + height*width*sizeof(double));
     if (!res) {
         perror("matrix calloc");
         exit(EXIT_FAILURE);
     };
     res->m_h = height;
     res->m_w = width;
     return res; 
  } // end matrix_create

J'utilise calloc pas malloc parce que je veux vraiment de la mémoire zéro-ed. Donc, la matrice retournée contient tous les zéros. BTW sur certains ordinateurs (pas le mien, un bureau PC / Linux / Debian / x86-64), la height*width*sizeof(double) pourrait déborder.

Voici la fonction pour accéder à certains éléments. Il vérifie quelques erreurs.

double matrix_access(Matrix*mat, unsigned i, unsigned j) 
{ 
   if (!mat) 
      { fprintf(stderr, "no matrix to access\n"); exit(EXIT_FAILURE; };
   unsigned h = mat->m_h;
   unsigned w = mat->m_w;
   if (i >= h || j >= w)
      { fprintf(stderr, "out-of-bound matrix access\n"); 
        exit(EXIT_FAILURE); };
   return mat->m_v [i*h + j];
}

Comme je n’ai fait qu’un seul calloc la destruction est simple à coder:

  void matrix_destroy(Matrix*mat) {
    if (!mat) { fprintf(stderr, "no matrix to destroy\n"); exit(EXIT_FAILURE); };
    assert (mat->m_h < MATRIX_MAXDIM);
    assert (mat->m_w < MATRIX_MAXDIM);
    free (mat);
  }

Les déclarations d'affirmation sont en principe inutiles (elles vérifient quelque chose qui devrait toujours être vrai). Mais j’adore la programmation défensive (cela m’aiderait à détecter des bugs dans d’ autres endroits qui abusent de ma Matrix ). Ils pourraient être désactivés (lire assert(3) ) au moment de la compilation.

En passant, vous pouvez déclarer ces fonctions en inline ou en static inline (et les définir dans un fichier d’en-tête inclus). Un compilateur optimiseur est susceptible de produire un code efficace (par exemple, compiler with gcc -O2 -Wall -march=native lors d’une analyse comparative).

Puisque vous lisez une matrice à partir d’un fichier, vous devez définir votre format de fichier (utiliser dans votre documentation une notation EBNF pour décrire la syntaxe de ce fichier est utile) et vous pouvez définir et implémenter une fonction lisant et créant une matrice à partir de. une poignée de fichier ouverte.

Le codage des autres fonctions est laissé comme exercice au lecteur.

N'oubliez pas de compiler with tous les avertissements et informations de débogage, donc gcc -Wall -Wextra -g avec GCC . Utilisez le débogueur gdb (et aussi valgrind pour chasser les fuites de mémoire ). Lisez la documentation de chaque fonction utilisée (par exemple, votre code ne vérifie pas le nombre de retours de scanf mais il devrait le faire). Exécutez plusieurs cas de test . Essayez de vous convaincre que votre code est bon (en en prouvant des parties). Peut-être utiliser un analyseur de code source statique (par exemple, Frama-C , qui souhaite des annotations supplémentaires dans ACSL ). Si vous devez benchmark votre programme, activez les optimisations au moment de la compilation (par exemple, en passant -O2 -march=native à gcc ....).

Dans un commentaire de code, vous demandez:

 // I need to now dynamically allocate the input files

Vous n'allouez pas de files entrée (le système d'exploitation les gère), vous allouez une zone de mémoire . Lisez à propos de l'allocation de mémoire dynamique en C. Notez que l'allocation de mémoire peut échouer (par exemple, comme malloc(3) dans malloc(3) ), car votre espace d'adressage virtuel ne peut pas croître indéfiniment.

En passant, la pile d’appels est limitée (typiquement à un mégaoctet ou à quelques-uns d’entre eux sur des ordinateurs de bureau), vous voulez donc généralement éviter les grandes variables automatiques . C’est une autre bonne raison d’éviter de placer des matrices dans votre cadre d’appel et de préférer les allocation de mémoire pour eux.







dynamic-allocation