[C] allocation mémoire via fonction

Salut,
pour ne pas rouiller, je me lance dans un petit projet en C. Ca se passe bien mais je me pose toujours des questions d’optimisations pour avoir un code élégant, modulable, rapide… bah ouais…

Je vais ecrire une fonction pour allouer la mémoire à mes tableaux (nombreux et en 2D) plutôt que d’écrire 50 lignes répétitives dans le main().
Mon prototype:

mafonction (montab,tailletab) { montab = malloc etc... }
La fonction travaille directement à partir du pointeur déclaré dans le main(). question à part: dois-je utiliser à tout prix return() et rajouter void devant mafonction?

Je suis tombé sur un gars (je n’ai plus le lien qui fait):

montab = mafonction(tailletab) { double **x; x = malloc etc... return (x); }
En gros, il passe par un pointeur temporaire.

Qu’en pensez vous?

C’est beaucoup plus propre de passer par un pointeur temporaire et de le renvoyer avec return, et le compilateur arrivera mieux à l’optimiser (Return Value Optimization).

Yo Syam! Merci pour l’info j’aurai jamais deviné ça.

Ca me met le doute sur la méthode à employer si je veux travailler dessus, une fois la mémoire allouée. On est d’accord que le seule solution valide serait une fonction du type

où l’on travaille directement sur le tableau. Si je dois passer par un pointeur temporaire, j’utiliserai 2 fois plus de mémoire que necessaire.

Non on n’est pas d’accord du tout. :mrgreen:

Un pointeur c’est juste quelques octets (4 sur du 32 bits, ou 8 sur du 64 bits) quelque soit la taille de la mémoire vers laquelle il pointe. Quand tu copies un pointeur, tu ne fais que copier l’adresse de ton tableau et non pas le contenu du tableau lui-même donc tu n’utiliseras pas 2 fois plus de mémoire.

Cela dit, ta question n’est pas très claire. Ta fonction void mafonction (montab,tailletab,para1,para2) prend-elle un tableau déjà alloué, ou bien est-ce qu’elle doit allouer le tableau elle-même ?
Enfin ça ne change rien, tu peux très bien faire ceci :

int* creer_et_initialiser(int taille, int valeur) { int* tableau = malloc(taille * sizeof(int)); for (int i = 0; i < taille; ++i) tableau[i] = valeur; return tableau; }
Seul malloc alloue de la mémoire, et ce qui se “balade” entre l’appelant et l’appelé n’est que l’adresse du tableau.

Je baisse ou c’est plutot

int* creer_et_initialiser(int taille, int valeur) 
{
  int* tableau = malloc(taille * sizeof(int));
  
  for (int i = 0; i < taille; ++i)
    tableau[i] = valeur;
  // return i;
  return tableau;
}

Sinon ça va faire un beau segfault (adresse retournée = taille du tableau alloué) et on aura de la mémoire allouée dont l’adresse sera paumée.

Oups. :blush: :blush: :blush:
C’est juste moi qui n’ai pas les yeux en face des trous aujourd’hui.

Merci, c’est corrigé. :wink:

On est d’accord pour l’allocation de mémoire. Tu te sers de int* tableau pour allouer la mémoire puis return(tableau) permet de donner l’adresse de cette mémoire au pointeur de ton choix. Je me documenterai sur RVO pour comprendre l’interêt.

Pour ma 2ème question, oui, ma fonction prend un tableau déjà alloué. Dans ce cas, j’ai tout interêt à travailler à partir du pointeur directement.

Voila un bout de code:
J’ai une fonction qui alloue la mémoire pour un tableau [nc][nr]. J’ai suivi ton conseil:

[code]double** array2d (int nc, int nr) {
int i;
double **x;

x = (double**)malloc(nc*sizeof(double*));
for (i = 0; i < nr; i++)
	x[i] = (double*)malloc(nr*sizeof(double));
return (x);

}[/code]

et plusieurs fonctions différentes qui vont bosser sur les tableaux. En voila une qui remplit un tableau [n][3]:

[code]void initpos (double x, double min, double max, int n) {
int i;

for (i = 0; i < n; i++)
        x[i] = [min+max*rand()/(RAND_MAX+1),0,0];

}[/code]
Ca vous semble propre comme manière de gèrer les tableaux?

P.S: Je connaissais pas le coup des déclarations faites dans les instructions.
EDIT: Bout de code pourri.

En fait tu n’as pas vraiment le choix. :wink:

Ok jusque là ça va.

[quote=“silver.sax”]et plusieurs fonctions différentes qui vont bosser sur les tableaux. En voila une qui remplit un tableau [n][3]:

[code]void initpos (double x, double min, double max, int n) {
int i;

for (i = 0; i < n; i++)
x[i] = [min+max*rand()/(RAND_MAX+1),0,0];
}[/code][/quote]
Plusieurs remarques…

  1. dans ton prototype, n’oublie pas de spécifier que ton x est un tableau de tableaux : void initpos (double** x, double min, double max, int n)

  2. beaucoup plus important, tu ne peux pas faire cette affectation x[ i] = [min+max*rand()/(RAND_MAX+1),0,0];
    Le type de x[ i] est double* donc de deux choses l’une :
    a) si l’affectation était valable, tu ne remplacerais pas comme tu le crois les éléments du tableau mais en fait tu remplacerais le pointeur vers ton tableau de double (celui alloué avec malloc) par un pointeur vers ton nouveau tableau [min+max*rand()/(RAND_MAX+1),0,0] avec pour conséquence une fuite mémoire.
    b) de toutes façons cette affectation n’est pas valable, la syntaxe n’est pas correcte.

Concernant le code de cette fonction initpos particulière, la bonne manière de faire serait :

[code]void initpos (double** x, double min, double max, int n) {
int i;

for (i = 0; i < n; i++) {
x[i][0] = min+max*rand()/(RAND_MAX+1);
x[i][1] = 0;
x[i][2] = 0;
}
}[/code]
Mais ça ne t’aidera pas beaucoup à comprendre en quoi ton code était faux.
J’ai l’impression que tu es plutôt confus sur les relations entre pointeurs et tableaux (et c’est normal, c’est assez déroutant au début, surtout si tu viens d’un langage plus haut niveau comme Python, PHP, …). Plutôt que d’essayer de t’expliquer ça, j’aurais plutôt tendance à te conseiller d’aller lire un bon tutoriel à ce sujet. J’avoue que les trucs bas niveau comme ça j’ai du mal à expliquer, ça me semble “naturel” quand je le fais moi-même mais dès que j’essaye de mettre des mots dessus y’a toute la complexité qui me revient dans la gueule. :mrgreen:

Hmm c’est un réflexe de C++ ça… En C ça n’est valable qu’à partir d’un certain standard (C99 si je me trompe pas).

Merci Syam!
Oui l’oubli double** x a été vite corrigé… J’ai tendance à ecrire double **x.
J’ai aussi passé une bonne heure à me casser la tête. J’etais persuadé que je pouvais attacher un tableau à x[i] qui effectivement est un pointeur. Puis je me suis résolu à le remplir “à la main”. On est donc obligé de faire des boucles pour remplir chaque cases du tableau.
Au final, ma fonction est identique à celle que tu m’a donné. Sauf que j’ai passé 2 heures dessus!

Oui j’ai vu en essayant de compiler. Je me suis fait un Makefile pour essayer tout ça.

Encore merci.