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

Dans la classe de programmation en C de mon université, le professeur et le livre subséquent écrit par elle utilisent le terme « appel» ou «renvoi par référence» pour désigner les pointeurs dans 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 «appel par valeur» par mon professeur:

 int sum(int a, int b); 

J’ai lu C ne supporte pas l’appel par référence. À mon sens, les pointeurs passent par la valeur .

Fondamentalement, est-il incorrect de dire que les pointeurs sont la manière de passer de C 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

Du sharepoint vue de l’origine de ma question, je pense qu’un débat sur la terminologie est né et que, en fait, je vois deux distinctions spécifiques.

  • pass-by-reference (le terme utilisé principalement aujourd’hui): Le terme spécifique utilisé dans les langages comme C ++
  • pass-by-reference (le terme utilisé par mon professeur comme paradigme pour expliquer les pointeurs): Le terme général utilisé avant les langages tels que C ++ a été développé et donc avant la réécriture du terme

Après avoir lu la réponse mise à jour de @Haris, il est logique que ce ne soit pas si noir et blanc.

    vous ne pouvez pas passer par référence en C, mais vous pouvez utiliser des pointeurs comme alternative

    Ouais, c’est correct.


    Elaborer un peu plus. Quoi que vous transmettiez en tant qu’argument aux fonctions c, celles-ci ne sont transmises que par des valeurs. Que ce soit la valeur d’une variable ou l’adresse de la variable.

    Ce qui fait la différence, c’est ce que vous envoyez.

    Lorsque nous passons par valeur, nous passons la valeur de la variable à une fonction. Lorsque nous passons par référence, nous passons un alias de la variable à une fonction. C peut passer un pointeur dans une fonction mais cela rest toujours une valeur de passage. Il copie la valeur du pointeur, l’adresse, dans la fonction.


    • Si vous envoyez la valeur d’une variable, seule la valeur sera reçue par la fonction, et toute modification ne modifiera pas la valeur d’origine.

    • Si vous envoyez l’adresse d’une variable, seule la valeur (l’adresse dans ce cas) est également envoyée, mais comme vous avez l’adresse d’une variable, elle peut être utilisée pour changer la valeur d’origine.


    À titre d’exemple, nous pouvons voir du code C ++ pour comprendre la différence réelle entre l’appel par valeur et l’appel par référence. Tiré de ce site.

     // Program to sort two numbers using call by reference. // Smallest number is output first. #include  using namespace std; // Function prototype for call by reference void swap(float &x, float &y); int main() { float a, b; cout << "Enter 2 numbers: " << endl; cin >> a >> b; if(a>b) swap(a,b); // This looks just like a call-by-value, but in fact // it's a call by reference (because of the "&" in the // function prototype // Variable a contains value of smallest number cout << "Sorted numbers: "; cout << a << " " << b << endl; return 0; } // A function definition for call by reference // The variables x and y will have their values changed. void swap(float &x, float &y) // Swaps x and y data of calling function { float temp; temp = x; x = y; y = temp; } 

    Dans cet exemple C ++, la variable de référence (qui n'est pas présente dans C) est utilisée. Pour citer ce site,

    "Une référence est un alias, ou un nom alternatif à une variable existante ..." ,

    et

    "L’utilisation principale des références agit comme des parameters formels de fonction pour prendre en charge la référence par référence ..."

    Ceci est différent de l'utilisation de pointeurs comme parameters de fonction car,

    "Une variable de pointeur (ou un pointeur en abrégé) est fondamentalement la même que les autres variables, qui peuvent stocker une donnée. Contrairement à la variable normale qui stocke une valeur (comme un int, un double, un caractère), un pointeur stocke une adresse mémoire. "

    Donc, essentiellement quand on envoie une adresse et que l'on reçoit via des pointeurs, on n'envoie que la valeur, mais quand on envoie / reçoit une variable de référence, on envoie un alias ou une référence.


    MISE À JOUR: 11 novembre 2015

    Il y a eu un long débat dans le C Chatroom , et après avoir lu les commentaires et les réponses à cette question, j'ai réalisé qu'il pouvait y avoir une autre façon de regarder cette question, une autre perspective.

    Regardons un code C simple

     int i; int *p = &i; *p = 123; 

    Dans ce scénario, on peut utiliser la terminologie que la valeur de p est une référence à i . Donc, si tel est le cas, alors si nous envoyons le même pointeur ( int* p ) à une fonction, on peut avancer que, puisque la référence de i est envoyée à la fonction, cela peut être appelé pass-by- référence

    Donc, c'est une question de terminologie et de façon de regarder le scénario.

    Je ne suis pas complètement en désaccord avec cet argument. Mais pour une personne qui suit complètement le livre et les règles, ce serait faux.


    REMARQUE: mise à jour inspirée par cette discussion.

    La référence est un terme surchargé ici; en général, une référence est simplement un moyen de faire référence à quelque chose. Un pointeur fait référence à l’object pointé, et passer (par valeur) un pointeur sur un object est le moyen standard de passer par référence dans C.

    C ++ a introduit les types de référence comme un meilleur moyen d’exprimer des références et introduit une ambiguïté dans l’anglais technique, car nous pouvons maintenant utiliser le terme “référence par référence” pour faire référence aux types de référence pour passer un object par référence.

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

    Est-ce que C a même «passer par référence»?

    Pas vraiment.

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

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

    Fondamentalement, C n’a rien de vraiment équivalent aux parameters de référence formelle ou de référence c ++ .

    Pour démontrer que les pointeurs sont passés par valeur, considérons un exemple d’échange de nombres utilisant des 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 numéros, des 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 de pointeur comme appel par référence, ce qui est trompeur. Voir la réponse à cette question pour la différence entre le passage par référence et le passage par la valeur .

    De la norme C99 (accent mis sur le mien):

    6.2.5 Types

    20 Un nombre quelconque de types dérivés peut être construit à partir des types d’object et de fonction, comme suit:

    – Un pointeur typ e peut être dérivé d’un type de fonction ou d’un type d’object, appelé type référencé . Un type de pointeur décrit un object 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 sur T “. La construction d’un type de pointeur à partir d’un type référencé est appelée “dérivation de type pointeur”. Un type de pointeur est un type d’object complet.

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

    “Passer par référence” est un concept. Oui, vous passez 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 de tournevis pour expliquer qu’il est erroné de faire référence au passage de pointeurs 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 à une mauvaise conclusion. En fait, même si vous ne prétendriez pas qu’une pièce de monnaie était un tournevis, vous diriez quand même que vous avez vissé la vis avec elle. 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 fait en passant par référence.

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

    C ++ transmet toujours des arguments par valeur. Les références C ++ sont bien plus limitées que les pointeurs (bien qu’elles aient des conversions plus implicites) et ne peuvent pas faire partie des structures de données, et leur utilisation ressemble beaucoup au code d’appel par référence, mais leur sémantique, pour répondre aux besoins des parameters call-by-reference, sont encore plus tangibles que ceux des implémentations pure call-by-reference comme les parameters Fortran ou les parameters Pascal var et vous pouvez parfaitement utiliser des références en dehors des contextes d’appels de fonctions.

    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 transmettez un int à une fonction, il est copié, les modifications apscopes à 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.

    Dans les langages qui prennent en charge la référence par référence, il existe un moyen par lequel une fonction peut être utilisée pour identifier une variable connue de l’appelant jusqu’à ce que la fonction appelée revienne, mais qui ne peut être stockée que dans des endroits qui ont gagné n’existe pas après cela . Par conséquent, l’appelant peut savoir que tout ce qui sera fait avec une variable à la suite du passage d’une fonction, une référence à celle-ci aura été effectué 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 () y a reçu un pointeur illimité. 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’ya aucun moyen de la copier. byref pour survivre au-delà du point où foo () revient.

    Passer des pointeurs à des choses permet au code de faire la même chose que la sémantique par référence, mais la sémantique par référence permet au code d’offrir des garanties plus fortes sur les choses qu’il ne fera pas .