Si C ne prend pas en charge le passage d'une variable par référence, pourquoi cela fonctionne-t-il?
#include <stdio.h>
void f(int *j) {
(*j)++;
}
int main() {
int i = 20;
int *p = &i;
f(p);
printf("i = %d\n", i);
return 0;
}
Production:
$ gcc -std=c99 test.c
$ a.exe
i = 21
&
) avant d'appeler la fonction et la déréférencer explicitement (avec*
) dans la fonction.f(&i);
est une implémentation de passage par référence, qui n'existe pas uniquement en C. C passage par référenceRéponses:
Parce que vous transmettez la valeur du pointeur à la méthode et que vous déréférencez-la pour obtenir l'entier pointé.
la source
func
:func(&A);
Cela passerait un pointeur vers A vers la fonction sans copier quoi que ce soit. Il s'agit d'une valeur de passage, mais cette valeur est une référence, vous êtes donc en train de «passer par référence» la variable A. Aucune copie n'est requise. Il est valable de dire que c'est un passage par référence.Ce n'est pas un passage par référence, c'est un passage par valeur comme d'autres l'ont dit.
La règle est la suivante:
Essayons de voir les différences entre les paramètres scalaires et pointeurs d'une fonction.
Variables scalaires
Ce programme court montre le passage par valeur en utilisant une variable scalaire.
param
est appelé le paramètre formel etvariable
à l'invocation de la fonction est appelé paramètre réel. Notez que l'incrémentationparam
dans la fonction ne change pasvariable
.Le résultat est
Illusion de passe-par-référence
Nous modifions légèrement le morceau de code.
param
est un pointeur maintenant.Le résultat est
Cela vous fait croire que le paramètre a été transmis par référence. Ce n'était pas. Il a été transmis par valeur, la valeur param étant une adresse. La valeur de type int a été incrémentée, et c'est l'effet secondaire qui nous fait penser qu'il s'agissait d'un appel de fonction passe-par-référence.
Pointeurs - transmis par valeur
Comment pouvons-nous montrer / prouver ce fait? Eh bien, nous pouvons peut-être essayer le premier exemple de variables scalaires, mais au lieu de scalaires, nous utilisons des adresses (pointeurs). Voyons voir si cela peut aider.
Le résultat sera que les deux adresses sont égales (ne vous inquiétez pas de la valeur exacte).
Exemple de résultat:
À mon avis, cela prouve clairement que les pointeurs sont transmis par valeur. Sinon, ce
ptr
seraitNULL
après l'invocation de la fonction.la source
Source: www-cs-students.stanford.edu
la source
Parce qu'il n'y a pas de référence de passage dans le code ci-dessus. L'utilisation de pointeurs (tels que
void func(int* p)
) est une adresse de passage. Ceci est passe-par-référence en C ++ (ne fonctionnera pas en C):la source
Votre exemple fonctionne parce que vous passez l'adresse de votre variable à une fonction qui manipule sa valeur avec l' opérateur de déréférence .
Bien que C ne prenne pas en charge les types de données de référence , vous pouvez toujours simuler le passage par référence en passant explicitement des valeurs de pointeur, comme dans votre exemple.
Le type de données de référence C ++ est moins puissant mais considéré plus sûr que le type de pointeur hérité de C. Ce serait votre exemple, adapté pour utiliser des références C ++ :
la source
Vous passez un pointeur (emplacement de l'adresse) par valeur .
C'est comme dire "voici l'endroit avec les données que je veux que vous mettiez à jour".
la source
p est une variable pointeur. Sa valeur est l'adresse de i. Lorsque vous appelez f, vous passez la valeur de p, qui est l'adresse de i.
la source
Pas de passage par référence en C, mais p "se réfère" à i, et vous passez p par valeur.
la source
En C, tout est pass-by-value. L'utilisation de pointeurs nous donne l'illusion que nous passons par référence car la valeur de la variable change. Cependant, si vous imprimez l'adresse de la variable de pointeur, vous verrez qu'elle ne sera pas affectée. Une copie de la valeur de l'adresse est transmise à la fonction. Ci-dessous est un extrait illustrant cela.
la source
Parce que vous passez un pointeur (adresse mémoire) à la variable p dans la fonction f. En d'autres termes, vous passez un pointeur et non une référence.
la source
Réponse courte: Oui, C implémente le passage de paramètres par référence à l'aide de pointeurs.
Lors de la mise en œuvre du passage de paramètres, les concepteurs de langages de programmation utilisent trois stratégies différentes (ou modèles sémantiques): transférer des données vers le sous-programme, recevoir des données du sous-programme ou faire les deux. Ces modèles sont communément appelés en mode, en mode sortie et en mode sortie, en conséquence.
Plusieurs modèles ont été imaginés par les concepteurs de langage pour implémenter ces trois stratégies de passage de paramètres élémentaires:
Pass-by-Value (sémantique en mode) Pass-by-Result (sémantique en mode out) Pass-by-Value-Result (sémantique en mode inout) Pass-by-Reference (sémantique en mode inout) Pass-by-Name (mode inout sémantique)
Le passage par référence est la deuxième technique pour le passage de paramètres en mode inout. Au lieu de copier des données entre le sous-programme principal et le sous-programme, le système d'exécution envoie un chemin d'accès direct aux données du sous-programme. Dans cette stratégie, le sous-programme a un accès direct aux données partageant efficacement les données avec la routine principale. Le principal avantage de cette technique est qu'elle est absolument efficace dans le temps et l'espace car il n'y a pas besoin de dupliquer l'espace et il n'y a pas d'opérations de copie de données.
L'implémentation de passage de paramètres en C: C implémente une sémantique passe-par-valeur et passe-par-référence (mode inout) en utilisant des pointeurs comme paramètres. Le pointeur est envoyé au sous-programme et aucune donnée réelle n'est copiée du tout. Cependant, comme un pointeur est un chemin d'accès aux données du sous-programme principal, le sous-programme peut modifier les données dans le sous-programme principal. C a adopté cette méthode d'ALGOL68.
Implémentation de passage de paramètres en C ++: C ++ implémente également la sémantique de passage par référence (mode inout) à l'aide de pointeurs et à l'aide d'un type spécial de pointeur, appelé type de référence. Les pointeurs de type référence sont implicitement déréférencés à l'intérieur du sous-programme, mais leur sémantique est également passe-par-référence.
Donc, le concept clé ici est que le passage par référence implémente un chemin d'accès aux données au lieu de copier les données dans le sous-programme. Les chemins d'accès aux données peuvent être des pointeurs explicitement déréférencés ou des pointeurs déréférencés automatiquement (type de référence).
Pour plus d'informations, veuillez vous reporter au livre Concepts of Programming Languages de Robert Sebesta, 10e éd., Chapitre 9.
la source
Vous ne passez pas un int par référence, vous passez un pointeur vers un int par valeur. Syntaxe différente, même sens.
la source
void func(int* ptr){ *ptr=111; int newValue=500; ptr = &newvalue }
avecint main(){ int value=0; func(&value); printf("%i\n",value); return 0; }
, il imprime 111 au lieu de 500. Si vous passez par référence, il doit imprimer 500. C ne prend pas en charge le passage de paramètre par référence.ptr = &newvalue
serait interdit. Quelle que soit la différence, je pense que vous faites remarquer que le "même sens" n'est pas exactement vrai car vous avez également des fonctionnalités supplémentaires en C (la possibilité de réaffecter la "référence" elle-même).ptr=&newvalue
s'il est passé par référence. Au lieu de cela, nous écrivonsptr=newvalue
Voici un exemple en C ++:void func(int& ptr){ ptr=111; int newValue=500; ptr = newValue; }
La valeur du paramètre passé dans func () deviendra500
.param = new Class()
intérieur d'une fonction n'aurait aucun effet pour l'appelant s'il est passé par valeur (pointeur). Siparam
est passé par référence, les modifications seraient visibles pour l'appelant.En C, pour passer par référence, vous utilisez l'opérateur d'adresse
&
qui doit être utilisé contre une variable, mais dans votre cas, puisque vous avez utilisé la variable de pointeurp
, vous n'avez pas besoin de la préfixer avec l'opérateur d'adresse. Il aurait été vrai si vous avez utilisé&i
comme paramètre:f(&i)
.Vous pouvez également ajouter ceci, pour déréférencer
p
et voir comment cette valeur correspondi
:la source
les pointeurs et les références sont deux thigngs différents.
Deux ou trois choses que je n'ai pas vues ont été mentionnées.
Un pointeur est l'adresse de quelque chose. Un pointeur peut être stocké et copié comme n'importe quelle autre variable. Il a donc une taille.
Une référence doit être considérée comme un ALIAS de quelque chose. Il n'a pas de taille et ne peut pas être stocké. Il DOIT référencer quelque chose, c'est-à-dire. il ne peut pas être nul ou modifié. Eh bien, le compilateur a parfois besoin de stocker la référence en tant que pointeur, mais c'est un détail d'implémentation.
Avec les références, vous n'avez pas de problèmes avec les pointeurs, comme la gestion de la propriété, la vérification nulle, le déréférencement lors de l'utilisation.
la source
«Passer par référence» (en utilisant des pointeurs) est en C depuis le début. Pourquoi pensez-vous que ce n'est pas le cas?
la source
j
( not*j
) inf()
n'a aucun effet suri
inmain()
.Je pense que C soutient en fait passer par référence.
La plupart des langages nécessitent le sucre syntaxique pour passer par référence au lieu de valeur. (C ++ par exemple nécessite & dans la déclaration de paramètre).
C nécessite également du sucre syntaxique pour cela. C'est * dans la déclaration du type de paramètre et & sur l'argument. Donc * et & est la syntaxe C pour passer par référence.
On pourrait maintenant faire valoir que le véritable passage par référence ne devrait nécessiter que la syntaxe sur la déclaration de paramètre, pas sur le côté argument.
Mais vient maintenant C # qui prend en charge le passage de référence et nécessite du sucre syntaxique à la fois du côté des paramètres et de l'argument.
L'argument selon lequel C n'a pas de référence by-pass fait que les éléments syntaxiques pour l'exprimer présentent l'implémentation technique sous-jacente n'est pas du tout un argument, car cela s'applique plus ou moins à toutes les implémentations.
Le seul argument restant est que le fait de passer par ref en C n'est pas une caractéristique monolithique mais combine deux caractéristiques existantes. (Prenez l'argument ref par &, attendez-vous à taper ref par *.) C #, par exemple, nécessite deux éléments syntaxiques, mais ils ne peuvent pas être utilisés l'un sans l'autre.
C'est évidemment un argument dangereux, car beaucoup d'autres fonctionnalités dans les langues sont composées d'autres fonctionnalités. (comme la prise en charge des chaînes en C ++)
la source
Ce que vous faites, c'est passer par valeur et non pas par référence. Parce que vous envoyez la valeur d'une variable 'p' à la fonction 'f' (en principal comme f (p);)
Le même programme en C avec passage par référence ressemblera, (!!! ce programme donne 2 erreurs car le passage par référence n'est pas supporté en C)
Production:-
la source