Bonjour et bienvenue à nouveau.
Dans cette conférence, je veux parler d'un concept appelé pass by reference.
Lorsque nous avons commencé à parler de pointeurs, j'ai mentionné que l'un des avantages est que
vous n'avez pas à utiliser autant de variables globales.
Parce que lorsque vous passez un pointeur
vers une fonction, les fonctions peuvent utiliser ce paramètre. Et quand il modifie ce paramètre, les données seront
modifiées en dehors de la fonction.
C'est donc un concept précieux et précieux.
Nous allons en parler. C'est plus un concept. C n'emploie en fait pas cela.
Et je vais vous montrer comment et vous donner quelques
exemples essentiellement comment cela fonctionne.
Il y a donc ce concept de passe par valeur. Cela a à voir avec les fonctions.
Nous avons parlé de transmettre des données à une fonction, mais comment sont-elles réellement transmises
en termes de langage dans le compilateur.
Il y a donc un concept de passe par valeur. Et c'est ce
dont nous parlons jusqu'à présent. Chaque fois que nous appelons la fonction
en plus de la fonction scanf, mais chaque fois que nous appelons la fonction, lorsque
vous modifiez ces données à l'intérieur de la fonction, elles ne sont pas
modifiées en dehors de la fonction.
Nous avons donc parlé de variables locales qui sont un peu un concept différent.
Mais lorsque vous transmettez des données à une fonction, vous pouvez les transmettre de deux manières.
Vous pouvez le passer par valeur, vous pouvez le transmettre par référence.
C passe exclusivement par valeur, tout comme
java, tous
passent par valeur. Mais il simule le passage par référence
car vous copiez des adresses.
Et donc même si ce
concept passe par référence n'est pas spécifiquement employé par
c, il le simule. Et donc pour comprendre ce que signifie
passer par référence et pourquoi c'est important, nous allons en parler.
Mais le passage par valeur se produit lorsqu'une fonction copie la valeur réelle d'un argument
dans le paramètre formel de la fonction.
Et c'est pourquoi si vous avez un entier, un flottant
ou un double et que vous le transmettez en tant qu'argument à une fonction,
cette variable réelle n'est pas modifiée en dehors de la fonction car elle est copiée.
Ainsi, les modifications apportées au paramètre à l'intérieur de la fonction n'ont aucun effet sur l'argument.
L'argument transmis n'est pas modifié.
Donc, il agit presque comme une variable locale car c'est une copie.
Comment cela se passe sous le capot, c'est qu'il est copié et transmis à la fonction.
Le programme C utilise l'appel par valeur pour passer des arguments.
Tout est appelé par valeur au passage par valeur.
Ces termes sont utilisés de manière interchangeable.
Encore une fois, cela signifie que le code d'une fonction ne peut pas modifier les arguments utilisés pour appeler la fonction.
Il n'y a aucun changement dans les valeurs bien qu'elles aient été modifiées à l'intérieur de la fonction.
Il semble que ce soient des variables locales.
Et c'est pourquoi, jusqu'à ce stade, vous
deviez créer des données globales si vous vouliez partager les données d'une fonction à l'autre.
Et c'est ainsi que c fonctionne.
Mais que se passe-t-il lorsque vous transmettez réellement
une copie d'une adresse. Ensuite, il peut imiter le passage par référence.
Mais voici un exemple de la façon dont nous utilisons le passage par valeur, c'est très bien démontré ici.
Cette routine d'échange est souvent un cas d'utilisation pour démontrer le
passage par valeur, car elle crée un temp et échange les paramètres
que les arguments sont passés.
Donc, si nous avons une fonction appelée swap qui prend deux
entiers, nous créons une variable locale temp.
Si nous affectons le paramètre x à temp, temp sera
égal à x. Si nous disons que x est égal à
y, y va maintenant être égal à x. Si nous disons que y est égal à
temp, y sera égal à temp.
Mais lorsque vous appelez cette fonction en passant ces valeurs, x
et y seront locaux à cette fonction.
Ces variables ne seront pas modifiées en dehors de cette fonction.
Et cette fonction le démontre très utile car nous affectons
à y et nous affectons à x.
Et x et y sont locaux à cette fonction car nous passons par valeur,
ils ne sont pas modifiés en dehors de cette fonction et c'est pourquoi nous devons utiliser des variables globales.
Mais ce sont des nombres entiers. Ce sont deux entiers x et y. Ce ne sont pas des
adresses, ce sont des nombres entiers et c'est significatif.
Donc, si nous appelons
swap et que nous voyons à quoi cela ressemble,
vous remarquerez que les valeurs ne changent pas.
Donc, si nous créons une variable appelée a 100 b avec
200 et que nous imprimons les variables avant de swap, 100 et
200 alors nous appelons swap, les variables
ne seront pas modifiées après que
nous les imprimons à nouveau.
Alors regardons cela dans les blocs de code et cela démontrera le passage par valeur.
Voici donc notre exemple. Nous avons notre routine d'échange.
Nous échangeons les valeurs des données transmises par x et y.
Nous l'appelons avec 100 200.
Nous allons imprimer avant d'appeler la fonction.
Nous allons imprimer après.
Et ce que vous remarquerez, c'est que les variables a et b ne changent jamais.
Et la raison en est que nous faisons passer par valeur.
Donc, si j'exécute ce programme, nous
pouvons voir qu'avant l'échange, la valeur de a était de 100 après l'échange, la valeur de a était de 100
et la même chose pour b 200 et 200.
Et la raison en est que nous sommes passés par valeur. Si nous passions par référence,
ces variables a et b changeraient après l'appel à swap.
Maintenant, que diriez-vous si nous employons cette même technique passe par valeur mais que
nous copions des pointeurs, que pensez-vous qu'il va se passer?
Les pointeurs et les fonctions s'entendent donc assez bien.
Vous pouvez passer un pointeur en tant qu'argument à une
fonction et vous pouvez également demander à une fonction de renvoyer un pointeur comme résultat.
Nous allons également montrer comment faire cela.
Mais passer par référence copie l'adresse d'un argument dans le paramètre formel.
L'adresse est utilisée pour accéder à l'argument réel utilisé dans l'appel.
Donc, ce que cela signifie, c'est que les modifications apportées au
paramètre à l'intérieur de la fonction affectent l'argument qui se trouve dans l'appel.
Et donc même si ce n'est techniquement pas sous le capot passé par
référence, il imite ou simule un passage par référence.
Nous copions l'adresse. Si vous copiez l'adresse, vous
avez une nouvelle adresse et une nouvelle mémoire pour cette valeur l'adresse.
Mais si vous essayez d'attribuer à cette adresse, elle va l'attribuer à l'adresse.
Donc, même si c'est passé par valeur, c'est une copie.
Et si vous copiez un entier normal, vous n'apportez pas de modifications. Si vous copiez une adresse, l'adresse est en
mémoire, donc lorsque vous apportez des modifications, vous apportez des modifications à cette adresse.
Donc, pour passer la valeur par
référence, les pointeurs d'argument sont passés aux fonctions comme n'importe quelle autre valeur.
Vous devez déclarer les paramètres de fonction en tant que types de pointeur avec l'astérisque.
Les modifications à l'intérieur de la fonction sont également reflétées à l'extérieur de la fonction.
Vous n'avez pas à utiliser autant de variables globales.
Contrairement à un appel par valeur où les modifications ne reflètent pas en dehors de la fonction.
Nous sommes toujours en train de copier, nous passons toujours par valeur, c'est juste la valeur d'une copie de l'adresse.
C'est donc un concept important à comprendre.
Maintenant, lorsque nous créons notre fonction d'échange,
nous allons la créer avec la déclaration à l'intérieur de
la fonction comme astérix, étoile et étoile.
Cela veut dire que cette fonction prend des pointeurs.
Maintenant, lorsque nous affectons à l'intérieur de cette fonction, nous
devons en fait utiliser l'opérateur d'indirection pour déréférencer parce que
nous voulons affecter les valeurs. Nous allons attribuer la valeur
que x que x pointe à la température.
Nous allons attribuer la valeur de x points à la valeur
vers laquelle y pointe, puis nous allons attribuer la valeur vers laquelle y pointe, à temp.
Temp est un entier, nous n'avons donc pas à le déréférencer.
Maintenant, lorsque nous
l'appelons avec a100 et b200 en passant
a et b, a et b ne sont pas des pointeurs, nous devons donc
passer l'adresse car la fonction prend des pointeurs.
Chaque fois que vous avez une fonction qui prend des pointeurs et que vous ne déclarez pas de
pointeur, vous devez utiliser l'adresse de l'opérateur et c'est la même chose que la façon dont nous utilisons scanf.
Mais maintenant, vous allez remarquer que lorsque nous
invoquons ceci après l'appel pour permuter les variables a et b vont être
changées, elles vont être permutées. A va être 200 b va être 100.
Alors maintenant, regardons cet exemple avec notre changement de méthode d'échange aux pointeurs dans les blocs de code.
Nous sommes dans des blocs de code. Nous avons modifié la fonction swap pour prendre des pointeurs.
Nous devons donc les déréférencer lorsque nous les attribuons.
Nous passons également maintenant l'adresse de a et b car ce sont maintenant des entiers,
et le swap prend maintenant des pointeurs. Donc, lorsque nous exécuterons ceci, nous
allons voir que l'appel et après va maintenant être changé.
Donc, avant d'appeler swap, a est égal à 100 après que nous appelons swap est de 200.
Avant d'appeler swap pour b, c'est 200. Après, c'est 100.
Nous sommes donc en mesure de modifier les valeurs des variables à l'intérieur
de la fonction car nous faisons une copie de l'adresse. Très, très important et l'un des
grands avantages des pointeurs.
Alors résumons la syntaxe. Je l'ai déjà fait mais passons en revue plus en détail.
Vous pouvez communiquer deux types d'informations sur une variable à une fonction.
Vous pouvez transmettre la valeur de
x et la fonction doit être déclarée avec le même type que x.
Nous savons que. Chaque fois que nous transmettons des
données à une fonction, nous devons avoir les mêmes types lorsque nous l'appelons.
Et ici, nous avons int function1 pour l'invoquer et il est défini comme un int num.
Ainsi, lorsque nous transmettons la valeur de x dans la fonction, ils doivent être déclarés avec le même type.
Et puis, si vous voulez réellement appeler une adresse de x, la
définition de la fonction doit inclure un pointeur vers le type correct.
Ainsi, chaque fois que vous utilisez des pointeurs, vous devez vous assurer que si vous
transmettez des données et que ces données ne sont pas des pointeurs, vous devez en obtenir l'adresse.
Ce que cela résume, c'est que si vous définissez une fonction qui ne prend pas de
pointeur, vous pouvez simplement l'appeler et transmettre les données.
Assurez-vous que ce n'est pas un pointeur.
Si vous avez une fonction qui prend un pointeur
astérisque, lorsque vous
l'invoquez, vous devez l'invoquer avec l'opérateur add ou sub.
Vous devez transmettre l'adresse.
Voilà la syntaxe.
Vous pouvez maintenant également qualifier un paramètre de fonction à l'aide du mot-clé constant.
Cela indique que la fonction traitera l'argument qui est passé pour ce paramètre comme une constante.
Ce n'est utile que lorsque le paramètre est un pointeur.
Parce que si vous passez par valeur et que vous ne passez pas de pointeurs, cela n'a
pas d'importance car cela n'affectera pas le changement à l'extérieur de la
fonction, alors pourquoi en feriez-vous une constante, peu importe si vous la modifiez .
La seule raison pour laquelle vous créez une constante de pointeur est que vous ne voulez pas
que les valeurs de la fonction changent en dehors.
Vous pouvez donc appliquer le mot-clé constant à un paramètre qui est un
pointeur pour spécifier que la fonction ne changera pas la valeur vers laquelle pointe l'argument.
Très important.
Donc, dans cet exemple, nous avons une fonction d'envoi de message. Il faut un pointeur de
message a p.
Nous mettons le mot-clé constant avant l'étoile char.
Donc, ce type de message de paramètre p est un pointeur vers un caractère constant.
C'est la valeur du caractère qui est constante, pas son adresse.
Donc, dans ce cas, ce que nous faisons, c'est que le pointeur vers lequel on pointe ne peut pas changer.
Nous pouvons changer l'adresse, mais nous ne pouvons pas changer ce qu'elle indique la valeur.
La syntaxe est similaire à celle utilisée lorsque nous créons des constantes sur d'autres pointeurs.
Le mot-clé constant est que c'est la première chose qui s'affiche alors vous ne pouvez pas changer la valeur.
Vous pouvez maintenant spécifier que le pointeur lui-même est constant,
mais cela n'a guère de sens car l'adresse est passée par valeur.
Vous ne pouvez pas modifier le
pointeur de rendu dans la fonction appelante. Donc, c'est juste si vous essayez de faire en sorte que l'adresse ne puisse pas
être modifiée, à quoi ça sert parce qu'elle est passée par valeur. Vous créez donc en
fait un nouvel emplacement mémoire
pour contenir cette adresse spécifique. Donc, si vous le changez, peu importe, cela ne changera pas de l'extérieur.
Vous voulez seulement pouvoir faire du paramètre une constante
afin de ne pas changer la valeur de ce vers quoi il pointe,
vous ne pouvez pas
changer le pointeur d'origine et la fonction appelante de toute façon car il est
passé par valeur passée par copie.
Le compilateur sait qu'un argument qui est un pointeur vers des données constantes sera
sûr, donc il y a des avantages à cela. Vous ne pouvez pas modifier accidentellement ce vers quoi la valeur pointe.
Vous pouvez donc recommencer si vous ne souhaitez pas que les modifications soient reflétées à l'extérieur de la fonction.
Si vous passez un pointeur vers des données constantes comme argument d'un
paramètre, le paramètre doit être utilisé comme dans l'exemple ci-dessus.
Lorsque vous invoquez la fonction et que vous la passez en constante, vous devez
faire en sorte que la déclaration de fonction prenne également un pointeur de caractère constant, tout
comme les autres pointeurs normaux qui sont des constantes.
Ils doivent être définis et les définitions doivent correspondre au prototype.
Retour des pointeurs d'une fonction.
Le renvoi d'un pointeur à partir d'une fonction est une capacité particulièrement puissante.
Il vous permet de renvoyer non seulement une valeur unique, mais un ensemble complet de valeurs.
Donc, encore une fois, l'un des autres avantages que j'ai mentionnés à
propos des pointeurs est que vous pouvez maintenant renvoyer plus de données à partir d'une fonction.
Auparavant, vous ne pouviez renvoyer qu'une seule valeur.
Mais avec un pointeur parce qu'un pointeur pourrait
pointer vers une structure dont nous n'avons pas encore parlé ou même
d'autres choses, vous pouvez renvoyer
plusieurs valeurs.Nous savons donc comment passer des pointeurs à des fonctions, nous
voulons maintenant parler de renvoyer des pointeurs.
Vous auriez à déclarer une fonction renvoyant un pointeur. La déclaration de fonction doit donc correspondre.
C'est très facile de faire ça. Vous dites simplement utiliser l'astérisque lorsque vous déclarez
la fonction ou que vous la définissez et que vous avez le type de retour de ce pointeur vers lequel il pointe.
Soyez donc prudent, même s'il existe des risques spécifiques liés au retour d'un pointeur.
Utilisez des variables locales pour éviter d'interférer avec la variable vers laquelle pointe l'argument.
C'est quelque peu problématique lorsque vous renvoyez des pointeurs car si vous modifiez ce pointeur à l'intérieur de
la fonction puis le renvoyez, cela peut être
quelque chose que vous n'avez pas l'intention de faire.
Utilisez donc toujours des variables locales si vous ne souhaitez pas affecter une variable
vers laquelle pointe votre argument.
Et donc cette conférence a décrit comment vous pouvez
transmettre des pointeurs à des fonctions
et comment vous pouvez renvoyer des pointeurs à partir de fonctions.
Et en utilisant ces mécanismes de passage de pointeurs
vers une fonction, il démontre ou imite le passage par
référence parce que vous copiez dans une adresse.
Et lorsque vous copiez une adresse si vous écrivez à cette adresse, vous allez mettre
à jour tout ce qui pointe
vers cette adresse, ce qui signifie en fait que vous modifiez la valeur des données
à l'intérieur de la fonction, cela va la refléter en dehors de la fonction.
Nous utilisons la routine d'échange pour le démontrer.
C'est un concept très puissant. Et vous voulez imiter le passage par référence, vous
pouvez utiliser des pointeurs pour le faire et vous voulez le faire lorsque vous voulez changer des choses en dehors de la fonction.
Merci.