Se connecter / S'enregistrer
Votre question

[Résolu][Langage C] Tableaux 2 dimensions et structure : recopier

Tags :
  • structure
  • tableau
  • Programmation
Dernière réponse : dans Programmation
4 Mars 2012 19:30:08

Bonjour,

Je dispose d'un pointeur issu d'une structure dans ce goût là :
  1. typedef struct {
  2. double a[10000];
  3. double b[10000];
  4. double c[10000];
  5. double d[10000];
  6. } MONTYPE
  7.  
  8. /* etc... */
  9. // plus loin
  10.  
  11. MONTYPE *p
  12. /*
  13. Blabla
  14. ...
  15. */


Et d'un autre côté je dispose d'un pointeur vers un tableau double [4][10000]
Je voulais savoir quelle était la méthode permettant de recopier la première ligne de mon tableau dans p->a, la 2e ligne dans p->b etc...

[EDIT] En fait ce que je veux c'est que p->a "pointe" sur la première ligne, p->b pointe sur la 2e ligne etc... inutile de recopier les 10000 valeurs.
Re-merci


Je précise que je ne peux pas changer gnd chose, car ces pointeurs sont issus d'autres fonctions que j'utilise pour du calcul scientifique, et que je ne sais pas les modifier. D'autre part je ne suis pas informaticien, encore moins programmeur. :D 
Voilà, j'espère avoir été suffisamment précis..

Merci à vous !

Autres pages sur : resolu langage tableaux dimensions structure recopier

a b L Programmation
4 Mars 2012 23:29:41

Déjà, je suis content de voir que tu ne fais pas de calcul scientifique en Fortran. :) 
Par contre, je reste déçu car tu fais du C et non du C++. :p 
Bon, comme tu m'as déjà bien aidé sur des maths et pas de la programmation, je prends le temps d'expliquer la situation.

Lorsque tu déclares "double a[10000];", tu fais un tableau statique, c'est-à-dire qu'une fois compilé, ton programme réserve la place de 1000*8 octets pour a, donc c'est un pointeur qui pointe sur une mémoire déjà "pré-allouée".
Il te faut utiliser un pointeur qui n'a pas encore de mémoire allouée, et au lieu de l'allouer (new en C++ ou malloc en C), tu le fais pointer directement sur la mémoire de l'autre tableau.

Bref, ta structure doit ressembler à ceci:
  1. typedef struct {
  2. double * pA;
  3. double * pB;
  4. double * pC;
  5. double * pD;
  6. } MONTYPE


Et pour affecter les adresses mémoires sur le tableau:
  1. p->pA = (double *)(tableau[1]);


Par contre, ta structure n'est plus compatible (mais c'est contournable), car dans ton cas, tu crées une structure, qui en mémoire prend 10000*8*4 octets, alors que moi, je crée une structure qui prend en mémoire 4*4 octets (la tailles des adresses mémoires).
C'est incompatible car une fois le programme compilé, dans ton cas, les fonctions accèdent directement aux valeurs positionné en mémoire relativement à l'adresse du début de la structure (le calcul des positions relatives étant effectués lors de la compilation). Dans mon cas, c'est le programme compilé qui va lire d'adresse pA et va chercher la donnée.
Tu peux modifier la structure comme je l'ai fais (puisque un tableau même statique n'est qu'un pointeur), aux conditions suivantes:
- mettre "a" et pas "pA" :) 
- tous les passages en paramètres doivent utiliser MONTYPE (pointeur ou pas) et pas une déclaration différente
- tout doit être recompilé avec la nouvelle structure pour que tout le code soit compilé avec ta nouvelle structure pour la raison de compatibilité.

Donc, si tu peux tout recompiler avec ta nouvelle structure (ce qui me parait très probable puisque tu as le code de la structure), c'est bon, alors que si tu as des fonctions utilisant la structure dans une bibliothèque que tu ne maitrises pas, ou si tu sérialise ta structure en transformant la structure en char* (ce qui ne serait pas du code très propre à cause des alignements en mémoire), tu es grillé. :) 

D'un autre côté allouer 10000*4*8 octets en statique n'est pas très propre non plus, car si tu déclares un "MONTYPE valeur;", tu crées la zone mémoire dans le programme compilé (ce qui fait grossir l'exe et peut finir par causer rapidement des problèmes de limites). En dynamique, tu peux avoir des tableaux contigus bien plus grands. Avec de la chance, tu ne fais qu'allouer la structure dynamiquement, ce qui ne ferait pas grossir l'exe. :) 

Enfin, si tu es bloqué par la modification du pointeur, tu peux faire une copie binaire, mais la modification de l'un n'impactera pas l'autre:
  1. memcpy( p->a, tableau[0], 10000*sizeof(double) );
  2. memcpy( p->b, tableau[1], 10000*sizeof(double) );
  3. memcpy( p->c, tableau[2], 10000*sizeof(double) );
  4. memcpy( p->d, tableau[3], 10000*sizeof(double) );


Une dernière astuce, utilise un "#define TAILLE 10000" pour éviter de mettre 10000 partout dans le code, même si le plus propre serait de faire des tableaux dynamiques. ;) 

Si tu faisais tout ça en C++, tu aurais fait une classe avec les méthodes d'accès qui vont bien, et défini des opérateurs mathématiques (comme produits de matrices ou autre) pour ne t'occuper du problème de programmation que dans la classe, et faire abstraction des problèmes techniques de programmation à l'utilisation.
5 Mars 2012 10:37:01

Merci pour ces éclaircissements !

D'habitude je fais tout avec MATLAB : c'est bcp plus souple à utiliser (mais bon ne comparons pas ce qui n'est pas comparable).
- Pour ce qui est de l'allocation dynamique de mémoire, j'avais cru comprendre que si on connaissait déjà la taille du tableau à traiter, il vallait mieux le faire en statique : dans mon cas, je suis sûr et certain qu'il y a 10000 échantillons (pas un de moins). En tous cas je modifierai un peu tout ça une fois mon pb principal corrigé... je pense que si tu le dis c'est que ça doit se faire ainsi. :D 

- J'ai modifié ma structure en lui mettant des pointeurs (car c'est moi qui l'ai créée donc pas de soucis) et le code en conséquence.
J'ai toujours une erreur "exception win32" car lors de la compilation, tout se passe bien. Je pense
que je vais devoir détailler un peu +.

Je compile avec mex, qui permet de faire un lien entre MATLAB et C. A la base j'aurais voulu que mon code fonctionne en C (car j'avais besoin de bcp de rapidité) cpdt je fais appel à des codes éléments finis / solveurs d'équations développés sur MATLAB...et je me vois mal les retranscrire en C (sachant qu'avec matlab on a accès a des toolbox bien pratiques).
Bref, mon programme est écrit en C, et fait appel à une seule fonction MATLAB : on lui fournit des arguments (des vecteurs colonnes), et on récupère sa valeur de retour (une matrice de taille (4,10000)). J'ai déjà testé le système sur cette même fonction matlab en la changeant pour qu'elle ne renvoie qu'un scalaire (matrice 1x1 pour matlab)...et ça a très bien fonctionné : je récupère bien en sortie le scalaire souhaité.

Le point noueux c'est celui-ci apparemment : si la ligne est commentée, le passage d'arguments s'effectue correctement, le tableau que retourne la fonction est visible dans MATLAB et mon programme C ne plante pas ; si la ligne est décommentée, alors j'ai mon erreur Win32 (mais les variables sont bel et bien créées dans MATLAB)
  1. pointeur_MONTYPE -> a = p_X+0; // Affectation de la 1ere
  2. //ligne du résultat dans pointeur_MONTYPE -> a (qui est un double* )

Avec p_X crée de cette façon :
  1. p_X=mxGetPr(resultat);

Ainsi p_X est un double* qui pointe vers ma matrice double 4*10000 que me renvoie matlab (voir le lien explicatif)
http://www.mathworks.fr/help/techdoc/apiref/mxgetpr.htm...

EDIT : J'aurais trouvé + logique de faire
  1. pointeur_MONTYPE -> a = p_X+0;

puisque p_X normalement, c'est un double** si j'ai bien compris...sauf que le compilateur râle en disant qu'il ne peut pas affecter un double dans un double* ...


Je sais que l'erreur ne vient pas de mon code MATLAB, car une fois que ça plante avec C, je peux qd même voir et manipuler les variables sous MATLAB (j'ai accès au tableau 4*10000).

Bon voilà, j'espère avoir donné suffisamment de données.

Merci ;) 
Contenus similaires
6 Mars 2012 19:26:51

Re

J'ai réussi à trouver une solution au pb ... pas des + élégantes mais bon ...
J'ai trafiqué la fct MATLAB pr qu'elle ne renvoie qu'un tableau d'une ligne en concaténant les 4 lignes : ça me renvoie le pointeur p_X
Du coup, en faisant 4 memcpy(structure.px, *(p_X + décalage),size) avec le bon "size" et le bon décalage j'ai un truc qui fonctionne.

Par contre impossible de manipuler un pointeur vers ma structure (pour faire un pStruct->pa au lieu de struct.pa) ... ça déconnait à chaque fois : du coup j'ai tout modifié en conséquence et ouff ça tourne.
Ultérieurement, je verrai pour faire ça avec un malloc etc... car en effet, j'ai testé de mettre 20000 éléments par lignes (donc un tableau de 80000) et ça n'a pas marché. Je comprends mieux l'intérêt ;) 
a b L Programmation
6 Mars 2012 21:37:32

Effectivement, la fonction mxGetPr() retourne un double* au lieu d'un double** (ça permet d'envoyer les 2 types).
Du coup, ce que tu as fait est correct.
Tu peux jouer avec les cast car tu peux transformer n'importe quel pointeur en n'importe quel autre pointeur, mais il faut passer par un pointeur, donc un double[][]*. :) 

Quand ça devient compliqué sur les pointeurs, le mieux est de redéfinir des types pour s'abstraire des pointeurs de pointeurs:
  1. // Définition du type tMatrix de type double [4][10000]
  2. typedef double tMatrixResult[4][10000];
  3. //...
  4.  
  5. // Récupération du résultat brut
  6. double * pRawData = mxGetPr(resultat);
  7.  
  8. // Conversion du pointeur sur un double (premier élément de la matrice) en pointeur sur le tableau en 2 dimension.
  9. tMatrixResult * pResult = (tMatrixResult *) pRawData;
  10. // Le premier élément est (*pResult)[0][0]
  11.  
  12. // Récupération d'une ligne n:
  13. memcpy(structure.px, (*pResult)[n],size);

Un fois compilé, ça fait exactement la même chose que ce que tu as fait. La différence est juste que tu laisses faire le calcul de décalage par le compilateur. ;) 

si l'affectation directe du pointeur structure.pointeur_px = (*pResult)[n] provoque le crash que tu as eu, cela signifie que la mémoire allouée par le retour de mxGetPr est détruit (ça peut être à la sortie de ta fonction si l'allocation est forcée sur la pile du programme, ou au prochain appel à la fonction). Bref, dans ce cas, il faut de toute façon faire la copie.

Citation :
Ultérieurement, je verrai pour faire ça avec un malloc etc... car en effet, j'ai testé de mettre 20000 éléments par lignes (donc un tableau de 80000) et ça n'a pas marché. Je comprends mieux l'intérêt ;) 

avec un double codé sur 8 octets, ça fait du 625kio.
En fait, la limite de taille vient du fait que tu déclares probablement la variable "structure" dans une fonction : c'est mis sur la pile mémoire du programme (qui le dépile à la sortie de la fonction). La pile vérifie les tailles utilisées, car comme il y a tous les contextes des fonctions appelantes, la mémoire peut vite exploser (d'où la raison qu'il faut s'interdire les appels récursifs et gérer les piles récursive soi-même).

Donc, pour résoudre ce problème:
- soit tu fais de l'allocation dynamique en testant si le retour de malloc est non nul (au cas où tu arriverais à exploser quand même la mémoire ;)  ).
- soit tu déclares en variable globale et ça ne se met pas sur la pile, mais sur le tas (comme pour le malloc), mais c'est encore moins propre.

Bref, ce qui est sûr c'est que, sur la pile, tu ne dépasseras pas 0xFFFFF octets, donc tu ne pourras pas dépasser les 2^15 éléments par lignes, et comme la pile n'est pas vide lorsque tu utilises copie ton tableau, la limite à 20000 ne m'étonne pas.
Sur le tas, sur une machine d'adressage 32-bits, la limite est 0x7FFFFFFF soit 2^26 éléments par ligne. Au delà, il te faut soit une machine plus performante soit faire des calculs par morceaux.
11 Mars 2012 10:08:49

Re !

J'ai fait du ménage. Maintenant tout est déclaré dans le programme appelant (le main), ma fonction est un void, qui modifie un des argument (plutôt que de renvoyer le tableau 4*1000, on modifie celui qu'on lui passe en argument via pointeur). Maintenant je n'ai plus aucun souci, ça tourne nickel.
Ce que j'ai aussi compris au fil de la discussion, c 'est qu'il ne faut pas renvoyer un pointeur crée dans le corps d'une fonction ... c'était une erreur qui traînait dans mon code (d'où les pb avec pStruct->a que je ne pouvais pas retourner)...

Merci en tous cas, très enrichissant ;) 

Tom's guide dans le monde
  • Allemagne
  • Italie
  • Irlande
  • Royaume Uni
  • Etats Unis
Suivre Tom's Guide
Inscrivez-vous à la Newsletter
  • ajouter à twitter
  • ajouter à facebook
  • ajouter un flux RSS