web-dev-qa-db-fra.com

Les pointeurs sont-ils considérés comme une méthode d'appel par référence en C?

Dans la classe de programmation C de mon université, le professeur et le livre écrit par elle utilisent le terme appel ou passe par référence lorsqu'il fait référence à pointeurs en C.

Un exemple de ce qui est considéré comme une "fonction d'appel par référence" par mon professeur:

int sum(int *a, int *b);

Un exemple de ce qui est considéré comme une "fonction d'appel par valeur" par mon professeur:

int sum(int a, int b);

J'ai lire C ne prend pas en charge l'appel par référence. À ma connaissance, les pointeurs passent par valeur .

Fondamentalement, est-ce incorrect de dire que les pointeurs sont le moyen de C de passer par référence? Serait-il plus correct de dire que vous ne pouvez pas passer par référence en C mais pouvez utiliser des pointeurs comme alternative?


Mise à jour 11/11/15

D'après l'origine de ma question, je crois qu'un débat de terminologie est né et je vois en fait deux distinctions spécifiques.

  • passe-par-référence (le terme utilisé principalement aujourd'hui): Le terme spécifique tel qu'il est utilisé dans des langages comme C++
  • passe-par-référence (le terme utilisé par mon professeur comme paradigme pour expliquer les pointeurs): Le terme général utilisé avant le développement de langages comme C++ et donc avant la réécriture du terme

Après avoir lu la réponse mise à jour de @Haris, il est logique de comprendre pourquoi ce n'est pas si noir et blanc.

65
Insane

La référence est un terme surchargé ici; en général, une référence est simplement un moyen de se référer à quelque chose. Un pointeur se réfère à l'objet pointé, et passer (par valeur) un pointeur à un objet est le moyen standard de passer par référence en C.

C++ a introduit les types de référence comme un meilleur moyen d'exprimer les références et introduit une ambiguïté dans l'anglais technique, car nous pouvons maintenant utiliser le terme "passer par référence" pour désigner l'utilisation de types de référence pour passer un objet par référence.

Dans un contexte C++, la première utilisation est, IMO, obsolète. Cependant, je pense que la première utilisation est courante dans d'autres contextes (par exemple C pur) où il n'y a pas d'ambiguïté.

32
Hurkyl

C a-t-il même `` passer par référence ''?

Pas vraiment.

À strictement parler, C utilise toujours le passage par valeur. Vous pouvez simuler le passage par référence vous-même, en définissant des fonctions qui acceptent les pointeurs puis en utilisant le & opérateur lors de l'appel , et le compilateur le simulera essentiellement pour vous lorsque vous passerez un tableau à une fonction (en passant un pointeur à la place, voir question 6.4 et al.).

Une autre façon de voir les choses est que si un paramètre a un type, disons, int * alors un entier est passé par référence et un pointeur vers un entier est passé par valeur .

Fondamentalement, C n'a rien de vraiment équivalent à un passage formel par référence ou à des paramètres de référence c ++ .

Pour démontrer que les pointeurs sont transmis par valeur, considérons un exemple d'échange de nombres à l'aide de pointeurs.

int main(void)
{
    int num1 = 5;
    int num2 = 10;

    int *pnum1 = &num1;
    int *pnum2 = &num2;
    int ptemp;

    printf("Before swap, *Pnum1 = %d and *pnum2 = %d\n", *pnum1, *pnum2);
    temp = pnum1;
    pnum1 = pnum2;
    pnum2 = ptemp;

    printf("After swap, *Pnum1 = %d and *pnum2 = %d\n", *pnum1, *pnum2);
}

Au lieu d'échanger des nombres, les pointeurs sont échangés. Maintenant, faites une fonction pour le même

void swap(int *pnum1, int *pnum2)
{
     int *ptemp = pnum1;
     pnum1 = pnum2;
     pnum2 = temp;
}

int main(void)
{
    int num1 = 5;
    int num2 = 10;

    int *pnum1 = &num1;
    int *pnum2 = &num2;

    printf("Before swap, *pnum1 = %d and *pnum2 = %d\n", *pnum1, *pnum2);
    swap(pnum1, pnum2);

    printf("After swap, *pnum1 = %d and *pnum2 = %d\n", *pnum1, *pnum2);
}

Boom! Pas d'échange!


Certains tutoriels mentionnent la référence du pointeur comme appel par référence, ce qui est trompeur. Voir le cette réponse pour le différence entre le passage par référence et le passage par valeur .

17
haccks

De la norme C99 (soulignement le mien):

6.2.5 Types

20 Un nombre illimité de types dérivés peut être construit à partir des types d'objet et de fonction, comme suit:

...

- Un type de pointeur e peut être dérivé d'un type de fonction ou d'un type d'objet, appelé type référencé . n type de pointeur décrit un objet dont la valeur fournit une référence à une entité du type référencé. Un type de pointeur dérivé du type référencé T est parfois appelé ‘‘ pointeur vers [~ # ~] t [~ # ~] ’’. La construction d'un type de pointeur à partir d'un type référencé est appelée "dérivation de type de pointeur". Un type de pointeur est un type d'objet complet.

Sur la base de ce qui précède, ce que votre professeur a dit est logique et correct. Un pointeur est passé par valeur aux fonctions. Si le pointeur pointe vers une entité valide, sa valeur fournit une référence à une entité.

16
R Sahu

"Passer par référence" est un concept. Oui, vous transmettez la valeur d'un pointeur à la fonction, mais dans ce cas, la valeur de ce pointeur est utilisée pour référencer la variable.

Quelqu'un d'autre a utilisé une analogie avec un tournevis pour expliquer qu'il est faux de faire référence au passage de pointeurs comme passant par référence, en disant que vous pouvez visser une vis avec une pièce mais que cela ne signifie pas que vous appelleriez la pièce un tournevis. Je dirais que c'est une grande analogie, mais ils arrivent à la mauvaise conclusion. En fait, bien que vous ne prétendiez pas qu'une pièce de monnaie est un tournevis, vous diriez toujours que vous avez vissé la vis avec. c'est-à-dire que même si les pointeurs ne sont pas les mêmes que les références c ++, ce que vous les utilisez pour faire EST passant par référence.

10
tomd

C transmet les arguments par valeur, période. Cependant, les pointeurs sont un mécanisme qui peut être utilisé pour effectivement passer des arguments par référence. Tout comme une pièce de monnaie peut être utilisée effectivement comme tournevis si vous avez le bon type de vis: certaines vis à fente sont même choisies pour bien fonctionner avec des pièces de monnaie. Ils ne transforment toujours pas les pièces en véritables tournevis.

C++ transmet toujours les arguments par valeur. Les références C++ sont beaucoup plus limitées que les pointeurs (bien qu'ayant plus de conversions implicites) et ne peuvent pas faire partie des structures de données, et leur utilisation ressemble beaucoup plus au code d'appel par référence habituel, mais leur sémantique, tout en étant très bien adaptée pour répondre aux besoins des paramètres d'appel par référence, sont toujours plus tangibles que ceux des implémentations d'appels par référence purs comme les paramètres Fortran ou les paramètres Pascal var et vous pouvez utiliser des références parfaitement bien en dehors des contextes d'appel de fonction .

6
user5548078

Votre professeur a raison. Par valeur, il est copié. Par référence, il n'est pas copié, la référence indique où il se trouve.

Par valeur, vous passez un int à une fonction, elle est copiée, les modifications apportées à la copie n'affectent pas l'original.

Par référence, passez le même int que le pointeur, il n'est pas copié, vous modifiez l'original.

Par référence, un tableau est toujours par référence, vous pourriez avoir un milliard d'éléments dans votre tableau, il est plus rapide de simplement dire où il se trouve, vous modifiez l'original.

5
BobRun

Dans les langages qui prennent en charge le passage par référence, il existe un moyen par lequel une fonction peut recevoir quelque chose qui peut être utilisée pour identifier une variable connue de l'appelant jusqu'à ce que la fonction appelée renvoie, mais qui ne peut être stockée que dans des endroits qui n'existeront plus. Par conséquent, l'appelant peut savoir que tout ce qui sera fait avec une variable à la suite de la transmission d'une fonction à une référence aura été fait au moment où la fonction retourne.

Comparez les programmes C et C #:

// C               // C#
int x=0;           int x=0;
foo(&x);           foo(ref x);
x++;               x++;
bar();             bar();
x++;               x++;
boz(x);            boz(x);

Le compilateur C n'a aucun moyen de savoir si "bar" pourrait changer x, car foo () a reçu un pointeur sans restriction vers lui. En revanche, le compilateur C # sait que bar () ne peut pas changer x, car foo () ne reçoit qu'une référence temporaire (appelée "byref" dans la terminologie .NET) et il n'y a aucun moyen pour une copie de cela byref pour survivre au-delà du point où foo () revient.

Passer des pointeurs vers des choses permet au code de faire les mêmes choses que ce qui peut être fait avec la sémantique pass-by-ref, mais la sémantique pass-by-ref permet au code d'offrir des garanties plus fortes sur les choses qu'il - pas oui.

3
supercat