Pointeurs comme arguments de fonction en C

Si je devais avoir ce code, par exemple:

int num = 5;
int *ptr = #

Quelle est la différence entre les deux fonctions suivantes?

void func(int **foo);
void func(int *foo); 

où j'appelle la fonction:

func(&ptr); 

je me rends compte que le premier des deux prend un pointeur vers un pointeur en paramètre, tandis que le second prend seulement un pointeur.

Si je passe dans func(&ptr), je suis effectivement en passant un pointeur. Quelle différence cela fait-il que le pointeur pointe vers un autre pointeur?

je crois que celui-ci donnera une incompatibilité d'avertissement, mais il semble que les détails n'ont pas d'importance aussi longtemps que vous savez ce que vous faites. Il semble que, peut-être pour des raisons de lisibilité et de compréhension, la première est une meilleure option (pointeur 2 étoiles), mais d'un point de vue logique, quelle est la différence?

24
demandé sur Melebius 2013-09-09 16:32:33

6 réponses

une règle empirique raisonnable est que vous ne pouvez pas exactement changer la chose exacte qui est passée est telle que l'appelant voit le changement. Passer des pointeurs est la solution.

Passage Par Valeur: void fcn(int foo)

en passant par la valeur, vous obtenez une copie de la valeur. Si vous changez la valeur dans votre fonction, l'appelant voit toujours la valeur originale indépendamment de vos changements.

passer par le pointeur à la valeur: void fcn(int* foo)

Passage par pointeur vous donne une copie du pointeur - il pointe vers le même emplacement que l'original. Cet emplacement de la mémoire est l'endroit où l'original est stocké. Cela vous permet de changer la valeur pointée-à. Cependant, vous ne pouvez pas changer le réel pointeur vers les données, puisque vous n'avez reçu une copie du pointeur.

Passage du Pointeur de Pointeur sur Valeur: void fcn(int** foo)

vous contournez ce qui précède en passant un pointeur à un pointeur à une valeur. Comme ci-dessus, vous pouvez modifier la valeur, de sorte que l'appelant voir le changement, car c'est le même emplacement que le code appelant. Pour la même raison, vous pouvez modifier le pointeur de la valeur. Cela vous permet de faire des choses comme allouer de la mémoire dans la fonction et la retourner; &arg2 = calloc(len);. Vous ne pouvez toujours pas changer le pointeur vers le pointeur, puisque c'est la chose dont vous recevez une copie.

51
répondu atk 2013-09-09 13:54:29

la différence est simplement indiquée dans les opérations avec lesquelles le processeur manipulera le code. la valeur elle-même est juste une adresse dans les deux cas, c'est vrai. Mais comme l'adresse est déréférencée, il est important pour le processeur et donc aussi pour le compilateur, de savoir après le déréférencement, avec quoi il traitera.

7
répondu dhein 2013-09-09 12:37:24

Si je devais avoir ce code, par exemple:

int num = 5;
int *ptr = #

Quelle est la différence entre les deux fonctions suivantes?:

void func(int **foo);
void func(int *foo);

Le premier veut un pointeur vers un pointeur vers un int, le second veut un pointeur qui pointe directement vers un int.

où j'appelle la fonction:

func(&ptr);

ptr est un pointeur vers un int, &ptr est une adresse, compatible avec un int **.

La fonction prenant un int * va faire quelque chose de différent, comme avec int **. Le résultat de la conversation sera complètement différent, conduisant à un comportement indéterminé, peut-être provoquer un incident.

si je passe dans func(&ptr) je passe effectivement dans un pointeur. Quelle différence cela fait-il que le pointeur pointe vers un autre pointeur?

               +++++++++++++++++++
adr1 (ptr):    +  adr2           +
               +++++++++++++++++++

               +++++++++++++++++++
adr2 (num):    +  42             +
               +++++++++++++++++++

adr2, nous avons une valeur int, 42.

adr1, nous avons l'adresse adr2, ayant la taille d'un pointeur.

&ptr donne nous adr1, ptr qui contient la valeur de &num, qui est adr2.

Si j'utilise adr1int *,adr2 sera mal traité comme un entier, conduisant à un nombre (peut-être assez grand).

Si j'utilise adr2int **, la première déréférence conduit à 42, qui sera mal interprété comme une adresse et peut-être faire planter le programme.

Il est plus que juste l'optique d'avoir une différence entre int * et int **.

je crois que celui-ci donnera une incompatibilité d'avertissement,

... ce qui a un sens ...

mais il semble que les détails n'ont pas d'importance aussi longtemps que vous savez ce que vous faites.

en avez-vous?

il semble que peut-être pour des raisons de lisibilité et de compréhension de la première est une meilleure option (Pointeur 2 étoiles), mais d'un point de vue logique, quelle est la différence?

Cela dépend de ce que la fonction ne avec le pointeur.

5
répondu glglgl 2013-09-09 13:24:55

Il y a deux principales différences concrètes:

  1. en Passant un pointeur vers un pointeur permet à la fonction de modifier le contenu de ce pointeur en sorte que le visiteur peut voir. Un exemple classique est le deuxième argument de strtol(). Suite à un appel à strtol(), le contenu de ce pointeur doit pointer vers le premier caractère de la chaîne qui n'a pas été analysé pour calculer le long valeur. Si vous venez de passer le pointeur de strtol(), alors tout changement qu'il a fait serait être local, et il serait impossible d'informer l'appelant que l'emplacement a été. En passant l'adresse de ce pointeur, strtol() peut modifier de manière que le visiteur peut voir. C'est comme passer l'adresse d'une autre variable.

  2. plus fondamentalement, le compilateur a besoin de connaître le type qui est pointé pour déréférencer. Par exemple, lors du déréférencement d'un double *, le compilateur interprétera (sur une implémentation où double consume 8 bytes) les 8 bytes commençant à l'emplacement de la mémoire comme la valeur du double. Mais, sur une implémentation 32 bits, en déréférencant un double **, le compilateur interprétera les 4 octets commençant à cet endroit comme l'adresse d'un autre double. Lors du déréférencement d'un pointeur, le type pointé est la seule information que le compilateur a sur la façon d'interpréter les données à cette adresse, donc connaître le type exact est critique, et c'est pourquoi ce serait une erreur de penser "ils sont tous juste pointeurs, alors quelle est la différence"?

2
répondu Paul Griffiths 2013-09-09 14:20:00

généralement la différence indique que la fonction assignera au pointeur, et que cette assignation ne doit pas être seulement locale à la fonction. Par exemple (et garder à l'esprit ces exemples ont pour but d'examiner la nature de la foo et pas des fonctions complètes, pas plus que le code dans votre post original est censé être réel travail code):

void func1 (int *foo) {
    foo = malloc (sizeof (int));
}

int a = 5;
func1 (&a);

est similaire à

void func2 (int foo) {
    foo = 12;
}

int b = 5;
func2 (b);

Dans le sens que foo peut égal 12 dans func2(), mais quand func2() renvoie, b sera toujours égal à 5. Dans func1(), foo pointe vers un nouveau type int, mais a encore a quand func1() retourne.

Que faire si nous voulions changer la valeur de a ou */ if(p == NULL) return 0; *retptr = p; return 1; } int main() { char *string = "Hello, world!"; char *copystr; if(allocstr(strlen(string), &copystr)) strcpy(copystr, string); else fprintf(stderr, "out of memory\n"); return 0; }

je me demandais pourquoi allocstr besoin d'un double pointeur. Si c'est un pointeur, cela signifie que vous pouvez passer et il sera changé après retourner...

Si vous n'cet exemple, il fonctionne très bien. Mais si vous modifiez l' allocstr juste *pointeur à la place de **pointeur (et copystr au lieu de ©str dans main), vous obtenez segmentation fault. Pourquoi? J'ai mis des printfs dans le code et il fonctionne très bien jusqu'à ce que la ligne strcpy. Donc je devine qu'il n'a pas allouer la mémoire pour copystr. Encore une fois, pourquoi?

Revenons à ce que cela signifie de passer par un pointeur. Cela signifie que vous passez l'emplacement de mémoire et vous pouvez y écrire directement la valeur que vous voulez. Vous pouvez modifier la valeur, car vous avez accès à l'emplacement mémoire de votre valeur.

De même, lorsque vous passez un pointeur vers un pointeur, vous passez à l'emplacement mémoire de votre pointeur - en d'autres termes, l'emplacement de la mémoire de votre emplacement de mémoire. Et maintenant (pointer vers pointer) vous pouvez changer l'emplacement de la mémoire comme vous pourriez changer la valeur quand vous utilisiez un pointeur.

La raison pour laquelle le code fonctionne, c'est que vous passer l'adresse d'un emplacement mémoire. La fonction allocstr modifie la taille de cet emplacement de mémoire pour qu'il puisse contenir "Hello world!"et il renvoie un pointeur vers cet endroit de mémoire.

C'est comme passer un pointeur, mais au lieu d'une valeur, On a un emplacement mémoire.

0
répondu adi 2018-03-25 11:19:49