Problème avec allocation dynamique de tableau (C)

Problème avec allocation dynamique de tableau (C) - C - Programmation

Marsh Posté le 06-04-2009 à 22:34:15    

Bonjour à tous,
 
Maintes excuses, j'ai cherché des réponses à ce sujet sur le forum et j'ai trouvé un élément de réponse, seulement je ne comprends pas pourquoi mon code ne fonctionne pas.
 
Je m'explique simplement :
 
Je souhaite créer à terme des tableaux de taille variable à l'exécution d'où mon intention d'utiliser l'allocation dynamique.
 
J'ai commencé par le code suivant :
 

Code :
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. void main(){
  4. int i=0;
  5. int n=2;
  6. int p=2;
  7. double ** tab;
  8. tab=(double **)malloc(n*sizeof(double *));
  9. for( i=0;i<n;i++);
  10. {
  11. tab[i]=(double *) malloc(p*sizeof(double));
  12. }
  13. tab[0][0]=1;
  14. tab[0][1]=0;
  15. tab[1][0]=0;
  16. tab[1][1]=1;
  17. printf("%1.0f %1.0f\n",tab[0][0],tab[0][1]);
  18. printf("%1.0f %1.0f\n",tab[1][0],tab[1][1]);
  19. getchar();
  20. }


 
J'obtiens à l'exécution un exception d'accès en écriture (sous VS 2008 express) dès la première ligne d'affectation tab[0][0]=1;.
 
 
J'ai trouvé un topic similaire où le code suivant fonctionne :

Code :
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. void main(){
  4. int ** tab;
  5. int i;
  6. int j;
  7. int k=2,n=2;
  8. tab = (int ** ) malloc (k * sizeof (int*));
  9. for (i=0;i<k;i++)
  10. {
  11. tab[i]=(int *)malloc(n * sizeof(int));
  12. }
  13. for (i = 0; i < k; i = i + 1)
  14. {
  15.      for (j = 0; j < n; j = j + 1)
  16. {
  17.  tab[i][j] = i+j;
  18. }
  19. }
  20. printf("%i %i\n",tab[0][0],tab[0][1]);
  21. printf("%i %i\n",tab[1][1],tab[1][1]);
  22. getchar();
  23. }


 
Je ne comprends pas pourquoi ce dernier fonctionne tandis que le mien plante lamentablement... un petit peu d'aide serait très appréciée !
Merci à tous,
 
Flo

Message cité 1 fois
Message édité par MKflo84 le 06-04-2009 à 22:39:41
Reply

Marsh Posté le 06-04-2009 à 22:34:15   

Reply

Marsh Posté le 07-04-2009 à 01:07:27    

Remarque préliminaire : il manque #include <malloc.h> mais peut-être que cet include est fait automatiquement.
 
tab[0][0]=1; ne convient pas, car c'est équivalent à "la cellule mémoire qui se trouve à l'adresse de tab + 0 fois le nombre de colonnes + 0 = 1." Il y a deux problèmes : a) le nombre de colonnes n'étant pas spécifié, je ne sais pas ce que le compilateur va prendre, peut-être zéro ; b) l'adressage concerne le pointeur au lieu du pointeur de pointeur.
 
Le plus simple est d'utiliser un tableau à une seule dimension, avec un simple malloc(n*p*sizeof(int)) ou bien d'utiliser des pointeurs sur des pointeurs plutôt que la notation [][] qui adresse en fait un espace à une seule dimension.

Reply

Marsh Posté le 07-04-2009 à 08:39:40    

Oui mais non. Pour la Neme fois, voila la façon correct d'allouer un tableau 2D contigue et supportant [][] et qui respecte le cache.

 
Code :
  1. // Gestion  mémoire
  2. float** alloc2D_float( size_t h, size_t w )
  3. {
  4.   float **m;
  5.   m    = (float**)malloc( h*sizeof(float*) );
  6.   m[0] = (float*)malloc( h*w*sizeof(float) );
  7.   for(size_t i=1;i<h;i++) m[i]=m[i-1]+w;
  8.   return m;
  9. }
  10. void release2D_float( float** ptr )
  11. {
  12.   if(ptr) free(ptr[0]);
  13.   if(ptr) free(ptr);
  14. }
  15. int main()
  16. {
  17.   float** tab;
  18.   tab = alloc2D_float( 5,9);  // 5 lignes x 9 coloness
  19.   for(int i=0;i<5;++i)
  20.     for(int j=0;j<9;++j)
  21.       tab[i][j] = 1.f/(1+i+j);
  22.   release2D_float(tab);
  23. }
 

A dupliquer/generaliser pour les autres types.

 

Un tableau 2D dont les lignes sont des zones mémoires disjointes est la pire des choses à faire.

Message cité 1 fois
Message édité par Joel F le 07-04-2009 à 08:41:10
Reply

Marsh Posté le 07-04-2009 à 09:18:30    

MKflo84 a écrit :

Maintes excuses, j'ai cherché des réponses à ce sujet sur le forum et j'ai trouvé un élément de réponse, seulement je ne comprends pas pourquoi mon code ne fonctionne pas.

 

Je souhaite créer à terme des tableaux de taille variable à l'exécution d'où mon intention d'utiliser l'allocation dynamique.

 

J'ai commencé par le code suivant :


Tu peux déjà corriger ça :

 

-------------- Build: Debug in hello ---------------

 

Compiling: main.c
Linking console executable: bin\Debug\hello.exe
C:\dev\hello\main.c:4: warning: function declaration isn't a prototype
C:\dev\hello\main.c:4: warning: return type of 'main' is not `int'
Output size is 18.72 KB
Process terminated with status 0 (0 minutes, 1 seconds)
0 errors, 2 warnings


Ensuite, il y a un ';' après un for, ce qui fait que ce qui est après n'est effectué qu'une seule fois...

Code :
  1. for (i = 0; i < n; i++);
  2.    {
  3.       tab[i] = (double *) malloc (p * sizeof (double));
  4.    }


est équivalent à

Code :
  1. for (i = 0; i < n; i++)
  2.    {
  3.    }
  4.    {
  5.       tab[i] = (double *) malloc (p * sizeof (double));
  6.    }


Ce qui n'est évidemment pas ce que tu attends...

 

Le cast est inutile. Ne pas oublier de libérer ce qui a été alloué.

 

Ceci fonctionne :

Code :
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. int main (void)
  4. {
  5.    int i = 0;
  6.    int n = 2;
  7.    int p = 2;
  8.    double **tab = malloc (n * sizeof *tab);
  9.    for (i = 0; i < n; i++)
  10.    {
  11.       tab[i] = malloc (p * sizeof *tab[i]);
  12.    }
  13.    tab[0][0] = 1;
  14.    tab[0][1] = 0;
  15.    tab[1][0] = 0;
  16.    tab[1][1] = 1;
  17.    printf ("%1.0f %1.0f\n", tab[0][0], tab[0][1]);
  18.    printf ("%1.0f %1.0f\n", tab[1][0], tab[1][1]);
  19.    for (i = 0; i < n; i++)
  20.    {
  21.       free (tab[i]);
  22.    }
  23.    free (tab);
  24.    return 0;
  25. }



1 0
0 1

 

Process returned 0 (0x0)   execution time : 0.574 s
Press any key to continue.


Il manque des contrôles pour vérifier si l'allocation a réussi.

 

Ceci peut aider :

 

http://mapage.noos.fr/emdel/notes.htm#tabdyn_2d


Message édité par Emmanuel Delahaye le 07-04-2009 à 09:28:14

---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 07-04-2009 à 09:43:57    

Joel F a écrit :

Oui mais non. Pour la Neme fois, voila la façon correct d'allouer un tableau 2D contigue et supportant [][] et qui respecte le cache.
 

Code :
  1. // Gestion  mémoire
  2. float** alloc2D_float( size_t h, size_t w )
  3. {
  4.   float **m;
  5.   m    = (float**)malloc( h*sizeof(float*) );
  6.   m[0] = (float*)malloc( h*w*sizeof(float) );
  7.   for(size_t i=1;i<h;i++) m[i]=m[i-1]+w;
  8.   return m;
  9. }
  10. void release2D_float( float** ptr )
  11. {
  12.   if(ptr) free(ptr[0]);
  13.   if(ptr) free(ptr);
  14. }
  15. int main()
  16. {
  17.   float** tab;
  18.   tab = alloc2D_float( 5,9);  // 5 lignes x 9 coloness
  19.   for(int i=0;i<5;++i)
  20.     for(int j=0;j<9;++j)
  21.       tab[i][j] = 1.f/(1+i+j);
  22.   release2D_float(tab);
  23. }


 
A dupliquer/generaliser pour les autres types.
 
Un tableau 2D dont les lignes sont des zones mémoires disjointes est la pire des choses à faire.


Ca ne change pas grand chose, à part une allocation un peu plus longue...
 
Ton code, une fois rendu compilable et testable, a l'air de fonctionner.

Code :
  1. /* Gestion  mémoire */
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. float **alloc2D_float (size_t h, size_t w)
  5. {
  6.    float **m = malloc (h * sizeof (float *));
  7.    m[0] = malloc (h * w * sizeof (float));
  8.    {
  9.       size_t i;
  10.       for (i = 1; i < h; i++)
  11.          m[i] = m[i - 1] + w;
  12.    }
  13.    return m;
  14. }
  15. void release2D_float (float **m)
  16. {
  17.    if (m != NULL)
  18.    {
  19.       free (m[0]);
  20.    }
  21.    free (m);
  22. }
  23. void display2D_float (float **m, size_t h, size_t w)
  24. {
  25.    if (m != NULL)
  26.    {
  27.       size_t i;
  28.       for (i = 0; i < h; ++i)
  29.       {
  30.          size_t j;
  31.          for (j = 0; j < w; ++j)
  32.          {
  33.             printf ("%8.2f", m[i][j]);
  34.          }
  35.          printf ("\n" );
  36.       }
  37.    }
  38. }
  39. int main (void)
  40. {
  41.    float **tab;
  42. /* 5 lignes x 9 coloness */
  43.    tab = alloc2D_float (5, 9);
  44.    {
  45.       int i;
  46.       for (i = 0; i < 5; ++i)
  47.       {
  48.          int j;
  49.          for (j = 0; j < 9; ++j)
  50.          {
  51.             tab[i][j] = 1.f / (1 + i + j);
  52.          }
  53.       }
  54.    }
  55.    display2D_float (tab, 5, 9);
  56.    release2D_float (tab);
  57.    return 0;
  58. }



    1.00    0.50    0.33    0.25    0.20    0.17    0.14    0.13    0.11
    0.50    0.33    0.25    0.20    0.17    0.14    0.13    0.11    0.10
    0.33    0.25    0.20    0.17    0.14    0.13    0.11    0.10    0.09
    0.25    0.20    0.17    0.14    0.13    0.11    0.10    0.09    0.08
    0.20    0.17    0.14    0.13    0.11    0.10    0.09    0.08    0.08
 
Process returned 0 (0x0)   execution time : 0.018 s
Press any key to continue.



---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 07-04-2009 à 10:29:42    

Emmanuel Delahaye a écrit :


Ca ne change pas grand chose, à part une allocation un peu plus longue...


Bah, en terme de localité du cache et donc de perfs au final, ca change beaucoup ;)
 

Emmanuel Delahaye a écrit :


Ton code, une fois rendu compilable et testable, a l'air de fonctionner.


Ouais, j'ai recopié ça d'un source C++ un peut trop rapidement ;)

Reply

Marsh Posté le 07-04-2009 à 11:52:38    

Joel F a écrit :


Bah, en terme de localité du cache et donc de perfs au final, ca change beaucoup ;)


Tu veux dire qu'on fait un tir groupé... OK.


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 07-04-2009 à 13:17:38    

Merci à tous pour vos réponses.
 
J'ai fini par trouver hier soir tard, que tout venait de ce fameux ";" après for, qui rendait inopérante ma boucle et qui n'affectait que la dernière collonne de ma matrice. Je suis désolé d'avoir spamé le forum avec une boulette aussi grosse... mais celà a l'avantage de nous ammener à quelque chose de plus intéressant pour le profane que je suis :
 
La remarque de Joel F m'interpelle. Je comprend que la solution qu'il propose a l'avantage d'affecter des espaces contigus de mémoire pour les données (une seul malloc de taille k*n, vers lequel vont pointer les éléments du premier malloc) au lieu d'affecter autant d'espaces, potentiellement disjoints, qu'il y a de lignes pour ma "solution". Pourrais-t-on juste m'expliquer "simplement" (sans vouloir offenser personne) et un peu plus en détail ce que celà change en terme de perfs, pourquoi et si oui, me donner un ordre d'idée du rapport ? (1 ordre de grandeur ou plus ?)
 
Pour répondre à billgatesanonym, comme tab a été affecté d'après un malloc, la taille de la première dimension est connue (vu dans "la ref. du langage C" par C. Delanoy, p337 "retrouver artificiellement le formalisme du tableau" ).
 
Merci d'avance,

Message cité 1 fois
Message édité par MKflo84 le 07-04-2009 à 13:23:21
Reply

Marsh Posté le 07-04-2009 à 13:21:56    

Joel F a écrit :


Ouais, j'ai recopié ça d'un source C++ un peut trop rapidement ;)


En C++, on utilise new et delete...


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 07-04-2009 à 13:31:32    

Emmanuel Delahaye a écrit :


En C++, on utilise new et delete...


 
Pas pour des types POD.
En outre, ce n'es pas malloc/free qui est à proscirire, c'est :
- le melange malloc/Delete, new/free
- malloc/free sur des types objets.

Reply

Marsh Posté le 07-04-2009 à 13:31:32   

Reply

Marsh Posté le 07-04-2009 à 13:33:53    

MKflo84 a écrit :


La remarque de Joel F m'interpelle. Je comprend que la solution qu'il propose a l'avantage d'affecter des espaces contigus de mémoire pour les données (une seul malloc de taille k*n, vers lequel vont pointer les éléments du premier malloc) au lieu d'affecter autant d'espaces, potentiellement disjoints, qu'il y a de lignes pour ma "solution". Pourrais-t-on juste m'expliquer "simplement" (sans vouloir offenser personne) et un peu plus en détail ce que celà change en terme de perfs, pourquoi et si oui, me donner un ordre d'idée du rapport ? (1 ordre de grandeur ou plus ?)


 
Quand tu faix N malloc, les adresses sont potentiellement sur des pages mémoire ou du moins des zone smémoire discontinues.
Lorsque tu acces à tab[i][j], le processeur va tenter de charger tab[i][j] et ses k voisins contigus ou k est la taille d'une ligne du cache.
Avec des lignes discontinues, tu va forcement avoir des cache misses en fin de lignes car il ne pas remplir à fond sa ligne de cache.
Et en general, les cache misses = plein de cycles dans la tete car accéder au cache est 10x plus rapide que de charger depuis la memoire principale.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed