Je m'excuse d'ajouter une autre réponse, mais je pense que personne n'a couvert tous les points qui doivent être couverts dans votre question.
1) Chaque fois que vous utilisez malloc()
pour allouer dynamiquement de la mémoire, vous devez également free()
quand vous aurez terminé. Le système d'exploitation va, en général, faire le ménage après vous, mais considérez que vous avez un processus pendant votre exécutable qui utilise de la mémoire. Lorsque ce processus est terminé, si vous free()
que la mémoire de votre processus a plus de mémoire disponible. C'est une question d'efficacité.
Pour utiliser correctement la gratuité :
int* somememory = malloc(sizeyouwant * sizeof(int));
// do something
free(somememory);
Facile.
2) Chaque fois que vous utilisez malloc
Comme d'autres l'ont fait remarquer, l'allocation réelle est en octets, vous devez donc effectuer les opérations suivantes malloc(numofelements*sizeof(type));
. Il existe une autre fonction, moins largement utilisée, appelée calloc
qui ressemble à ceci calloc(num, sizeof(type));
ce qui est peut-être plus facile à comprendre. calloc initialise également votre mémoire à zéro .
3) Il n'est pas nécessaire d'exprimer le type de retour de la fonction malloc
. Je sais que de nombreux livres de programmation suggèrent de le faire et que le C++ impose de le faire (mais en C++, vous devriez utiliser la fonction new
/ delete
). Voir cette question .
4) Votre signature de fonction était en effet incorrecte - les signatures de fonction doivent correspondre à leurs fonctions.
5) Pour ce qui est de renvoyer des pointeurs à partir de fonctions, c'est quelque chose que je décourage mais ce n'est pas mauvais en soi. Deux points à mentionner : gardez toujours le point 1) à l'esprit. I a demandé exactement quel était le problème et il s'agit essentiellement de garder une trace de ces free()
appels. En tant qu'utilisateur plus avancé, il faut également se préoccuper du type d'allocateur.
Un autre point ici, considérez cette fonction :
int* badfunction()
{
int x = 42;
int *y = &x;
return y;
}
C'est mauvais, mauvais, mauvais. Ce qui se passe ici est que nous créons et retournons un pointeur vers x
qui n'existe que tant que vous êtes dans badfunction
. Quand vous revenez, vous avez l'adresse d'une variable qui n'existe plus parce que x
est généralement créé sur la pile. Vous en apprendrez davantage à ce sujet au fil du temps ; pour l'instant, pensez simplement que la variable n'existe pas au-delà de sa fonction.
Notez que int* y = malloc(...
est un cas différent - cette mémoire est créée sur le tas à cause du malloc et survit donc à la fin de ladite fonction.
Que recommanderais-je comme signature de fonction ? En fait, j'opterais pour shybovycha avec une légère modification :
int findFactors(int* factors, const int N);
Mes changements ne sont que des préférences personnelles. J'utilise const
pour que je sache que quelque chose fait partie de l'entrée d'une fonction. Ce n'est pas strictement nécessaire avec un simple int, mais si vous passez des pointeurs, rappelez-vous que la mémoire source peut être modifiée à moins que vous n'utilisiez la fonction const
avant lui, sur lequel votre compilateur devrait vous avertir si vous essayez de le modifier. C'est donc juste une habitude dans ce cas.
Le deuxième changement est que je préfère les paramètres de sortie sur la gauche parce que je pense toujours de cette façon, c'est-à-dire output = func(input)
.
Pourquoi peut-on modifier les arguments d'une fonction lorsqu'un pointeur est utilisé ? Parce que vous avez transmis un pointeur à une variable. Il s'agit simplement d'une adresse mémoire - lorsque nous la "déréférençons" (accédons à la valeur à cette adresse), nous pouvons la modifier. D'un point de vue technique, le C est strictement un système de transmission par valeur. Les pointeurs sont eux-mêmes des variables contenant des adresses mémoire et le contenu de ces variables est copié dans votre fonction. Ainsi, une variable normale (disons int
) est juste une copie de ce que vous avez passé en entrée. int* factors
est une copie de l'adresse de la variable pointeur que vous lui transmettez. Par conception, l'original et cette copie pointent tous deux vers la même mémoire, de sorte que lorsque nous les déréférençons, nous pouvons modifier cette mémoire à la fois dans la fonction appelante et dans la fonction originale.
J'espère que cela clarifie certaines choses.