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.