Hola y bienvenido de nuevo.
En esta conferencia, quiero hablar sobre un concepto llamado pasar por referencia.
Cuando empezamos a hablar de punteros, mencioné que una de las ventajas es que
no es necesario utilizar tanto variables globales.
Porque cuando pasa un puntero a
una función, las funciones pueden usar ese parámetro. Y cuando modifique ese parámetro, los datos se
modificarán fuera de la función.
Así que este es un concepto valioso y valioso.
Vamos a hablar de esto. Esto es más un concepto. C en realidad no emplea esto.
Y les mostraré cómo y les daré algunos ejemplos
básicamente de cómo funciona esto.
Así que existe este concepto de pasar por valor. Esto tiene que ver con funciones.
Hablamos de pasar datos a una función, pero cómo se pasan realmente en
términos del lenguaje en el compilador.
Entonces hay un concepto de pasar por valor. Y esto es de
lo que hemos estado hablando hasta ahora. Siempre que llamamos a la función
además de la función scanf, pero cada dos veces llamamos a la
función, cuando modificas esos datos dentro de la función, no se
modificaron fuera de la función.
Así que hablamos de variables locales que es un concepto diferente.
Pero cuando pasa datos a una función, hay dos formas de pasarlos.
Puede pasarlo por valor, puede pasarlo por referencia.
C pasa exclusivamente por valor y también
Java, todos
pasan por valor. Pero simula pasar por referencia
porque copia direcciones.
Y así, aunque este concepto
pase por referencia no es empleado específicamente por
c, lo simula. Entonces, para entender qué significa pasar por
referencia y por qué es importante, vamos a hablar de eso.
Pero pasar por valor es cuando una función copia el valor real de un argumento
en el parámetro formal de la función.
Y esta es la razón por la que si tiene
un número entero, un flotante o un doble y lo pasa como un argumento a
una función, esa variable real no se modifica fuera de la función porque se copia.
Por lo tanto, los cambios realizados en el parámetro dentro de la función no tienen ningún efecto en el argumento.
El argumento que se pasa no se modifica.
Entonces casi actúa como una variable local porque es una copia.
La forma en que realmente está sucediendo bajo el capó es que se está copiando y pasando a la función.
El programa C usa la llamada por valor para pasar argumentos.
Todo se llama por valor paso por valor.
Estos se usan indistintamente estos términos.
De nuevo, esto significa que el código dentro de una función no puede alterar los argumentos utilizados para llamar a la función.
No hay cambios en los valores, aunque se han modificado dentro de la función.
Parece que son variables locales.
Y es por eso que antes, hasta este
punto, tenía que crear datos globales si deseaba compartir los datos de una función a la siguiente.
Y así es como funciona c.
Pero, ¿qué sucede cuando en realidad pasa una
copia de una dirección? Entonces puede imitar el pase por referencia.
Pero aquí hay un ejemplo de cómo hemos estado usando el paso por valor, se demuestra muy bien aquí.
Esta rutina de intercambio es un caso de uso frecuente para demostrar el
paso por valor porque lo que hace es crear una temperatura e intercambia los parámetros
que se pasan los argumentos.
Entonces, si tenemos una función llamada swap que toma dos
enteros, creamos una variable local temp.
Si asignamos el parámetro x a temp, temp
será igual ax. Si decimos que x es igual
ay, y ahora será igual ax. Si decimos que y es igual a temp,
y será igual a temp.
Pero cuando llame a esta función pasando esos valores,
xey serán locales a esta función.
Estas variables no se van a modificar fuera de esta función.
Y esta función lo demuestra muy útil porque estamos
asignando ay y estamos asignando ax.
Y xey es local a esta función porque estamos pasando por valor, no
se modifican fuera de esta función y es por eso que tenemos que usar variables globales.
Pero son números enteros. Son dos números enteros x e y. No son direcciones,
son números enteros y eso es significativo.
Entonces, si llamamos
swap y vemos cómo se ve esto,
notará que los valores no cambian.
Entonces, si creamos una variable llamada 100 b
con 200 e imprimimos las variables antes de intercambiar,
100 y 200 luego llamamos swap,
las variables no se cambiarán
después de imprimirlas nuevamente.
Así que veamos esto en bloques de código y esto demostrará el paso por valor.
Así que aquí está nuestro ejemplo. Tenemos nuestra rutina de intercambio.
Estamos intercambiando valores de datos pasados xey.
Lo llamamos con 100 200.
Vamos a imprimir antes de llamar a la función.
Vamos a imprimir después.
Y lo que notará es que las variables ayb nunca cambian.
Y la razón de esto es porque estamos pasando por valor.
Entonces, si ejecuto este programa, podemos
ver que antes de intercambiar el valor de a era 100 después de intercambiar el valor de a era
100 y lo mismo para b 200 y 200.
Y la razón de esto es porque pasamos por valor. Si pasáramos por referencia,
esas variables ayb cambiarían después de la llamada a swap.
Ahora, ¿qué tal si empleamos esta misma técnica pasar por valor
pero copiamos punteros, qué crees que va a pasar?
Así que los punteros y las funciones se llevan bastante bien juntos.
Puede pasar un puntero como argumento a una
función y también puede hacer que una función devuelva un puntero como resultado.
También vamos a mostrar cómo hacer eso.
Pero pasar por referencia copia la dirección de un argumento en el parámetro formal.
La dirección se utiliza para acceder al argumento real utilizado en la llamada.
Entonces, lo que esto significa es que los cambios realizados en
el parámetro dentro de la función afectan el argumento que está en la llamada.
Y así, aunque técnicamente no se pasa por referencia bajo el
capó, imita o simula el paso por referencia.
Estamos copiando la dirección. Si copia la dirección,
tiene una nueva dirección y memoria para ese valor la dirección.
Pero si intenta asignar a esa dirección, se asignará a la dirección.
Entonces, aunque se transmite por valor, se está copiando.
Y si está copiando un entero regular, no realiza cambios. Si está copiando una dirección, la dirección está
en la memoria, por lo que cuando haga cambios, hará cambios en esa dirección.
Entonces, para pasar el valor por
referencia, los punteros de argumento se pasan a las funciones como cualquier otro valor.
Debe declarar los parámetros de la función como tipos de puntero con el asterisco.
Los cambios dentro de la función también se reflejan fuera de la función.
No es necesario utilizar tanto variables globales.
A diferencia de una llamada por valor donde los cambios no se reflejan fuera de la función.
Todavía estamos copiando, seguimos pasando por valor, es solo el valor de una copia de la dirección.
Así que este es un concepto importante de entender.
Ahora, cuando creamos nuestra función de intercambio,
la vamos a crear con la declaración dentro de la
función como asterix, star y star.
Eso es decir que esta función toma punteros.
Ahora, cuando asignamos dentro de esta función, tenemos que
usar el operador de indirección para eliminar la referencia porque
queremos asignar los valores. Vamos a asignar el valor que
esa x que x apunta a la temperatura.
Vamos a asignar el valor de x apunta al valor al
que apunta y y luego vamos a asignar el valor al que apunta y, a temp.
Temp es un número entero, por lo que no tenemos que desreferenciarlo.
Ahora, cuando lo
invocamos con a100 y b200
pasando ayb, ayb no son punteros, por lo que tenemos que pasar
la dirección porque la función toma punteros.
Siempre que tenga una función que tome punteros y no declare un
puntero, debe usar la dirección del operador y es lo mismo que usamos scanf.
Pero ahora notará que cuando invocamos
esto después de la llamada para intercambiar, las variables ayb
se cambiarán, se intercambiarán. A va a ser 200 b va a ser 100.
Así que ahora veamos este ejemplo con nuestro cambio de método de intercambio a punteros en bloques de código.
Estamos en bloques de código. Modificamos la función de intercambio para tomar punteros.
Así que tenemos que desreferenciarlos cuando los asignamos.
Ahora también pasamos la dirección de ayb porque ahora son números enteros, y
el intercambio ahora toma punteros. Entonces, cuando ejecutemos esto, veremos
que la llamada y las posteriores ahora se cambiarán.
Entonces, antes de llamar a swap, a es igual a 100 después de llamar a swap es 200.
Antes de llamar a swap por b, es 200. Luego, son 100.
Entonces podemos cambiar los valores de las variables dentro de la función
porque estamos haciendo una copia de la dirección. Muy, muy importante y una de las
grandes ventajas de los punteros.
Entonces resumamos la sintaxis. Ya lo hice, pero veamos con más detalle.
Puede comunicar dos tipos de información sobre una variable a una función.
Puede transmitir el valor de x
y la función debe declararse con el mismo tipo que x.
Lo sabemos. Siempre que pasamos datos a
una función, tenemos que tener los mismos tipos cuando la llamamos.
Y aquí tenemos int function1 para invocarlo y está definido como int num.
Entonces, cuando transmitimos el valor de x en la función, deben declararse con el mismo tipo.
Y luego, si realmente desea invocar una dirección de x, requiere que
la definición de la función incluya un puntero al tipo correcto.
Por lo tanto, siempre que use punteros, debe asegurarse de que si está
pasando datos y esos datos no son un puntero, debe obtener la dirección del mismo.
Lo que esto resume es que si define una función que no toma un
puntero, puede simplemente llamarla y pasar los datos.
Asegúrate de que no sea un puntero.
Si tiene una función que tiene un puntero de
asterisco, cuando la
invoca, debe invocarla con el operador add o sub.
Tienes que pasar la dirección.
Esa es la sintaxis.
Ahora también puede calificar un parámetro de función usando la palabra clave constante.
Esto indica que la función tratará el argumento que se pasa para este parámetro como una constante.
Solo es útil cuando el parámetro es un puntero.
Porque si pasa por valor y no pasa punteros, no importa porque no afectará el
cambio en el exterior de la función, entonces, ¿por qué
lo convertiría en una constante, a quién le importa si lo cambia? .
La única razón por la que hace que un puntero sea constante es porque no desea
que los valores externos a la función cambien.
Y entonces puede aplicar la palabra clave constante a un parámetro que es
un puntero para especificar que la función no cambiará el valor al que apunta el argumento.
Muy importante.
Entonces, en este ejemplo, tenemos una función de envío de mensaje. Se necesita un puntero
de mensaje p.
Ponemos la palabra clave constante antes de la estrella de caracteres.
Entonces, este tipo de mensaje de parámetro p es un puntero a un carácter constante.
Es el valor del carácter lo que es constante, no su dirección.
Entonces, en este caso, lo que estamos haciendo es que el puntero al que se apunta no puede cambiar.
Podemos cambiar la dirección, pero no podemos cambiar lo que apunta al valor.
La sintaxis es similar a cuando creamos constantes en otros punteros.
La palabra clave constante es que es lo primero que se muestra, luego no puede cambiar el valor.
Ahora podría especificar que el puntero en sí sea constante,
pero esto tiene poco sentido porque la dirección se pasa por valor.
No puede cambiar el puntero
de renderizado en la función de llamada. Entonces, esto es solo si intenta que la dirección no se pueda
cambiar, ¿cuál es el punto porque se pasa por valor? Entonces, en realidad, está
creando una nueva ubicación de
memoria para contener esa dirección específica. Entonces, si lo cambia, a quién le importa que no lo cambie en el exterior.
Solo desea poder hacer que el parámetro sea
una constante para que no cambie el valor de lo que apunta,
no puede cambiar el
puntero original y la función de llamada de todos modos porque se
pasa por valor transmitido por copia.
El compilador sabe que un argumento que apunta a datos constantes será
seguro, por lo que esto tiene sus ventajas. No puede modificar accidentalmente a qué apunta el valor.
Por lo tanto, puede volver a hacer esto si no desea que los cambios se reflejen en el exterior de la función.
Si pasa un puntero a datos constantes como argumento para un parámetro,
entonces el parámetro debe usarse como en el anterior.
Cuando invoca la función y la pasa a constante, entonces tiene que hacer
que la declaración de la función también tome un puntero de carácter constante, al
igual que otros punteros normales que son constantes.
Deben definirse y las definiciones deben coincidir con el prototipo.
Devolver punteros de una función.
Devolver un puntero desde una función es una capacidad particularmente poderosa.
Proporciona una forma de devolver no solo un valor único, sino un conjunto completo de valores.
Entonces, nuevamente, una de las otras ventajas que mencioné
sobre los punteros es que ahora puede devolver más datos de una función.
En el pasado, solo se podía devolver un valor.
Pero con un puntero porque un puntero podría
apuntar a una estructura de la que no hemos hablado todavía o
incluso a otras cosas, puede
devolver múltiples valores. Así que sabemos cómo pasar punteros a funciones,
ahora queremos hablar sobre devolver punteros.
Tendría que declarar una función que devuelva un puntero. Entonces, la declaración de la función debe coincidir.
Es muy fácil hacer eso. Simplemente diga que use el asterisco cuando
declare la función o la defina y tenga el tipo de retorno para ese puntero a lo que apunta.
Así que tenga cuidado, aunque existen peligros específicos relacionados con la devolución de un puntero.
Utilice variables locales para evitar interferir con la variable a la que apunta el argumento.
Es algo problemático cuando devuelve punteros porque si modifica ese puntero dentro de la función y
luego lo devuelve, podría ser algo
que no tiene la intención de que haga.
Por lo tanto, use siempre variables locales si no desea afectar una variable a
la que apunta su argumento.
Entonces, esta lección describió cómo se
pueden pasar punteros a funciones
y cómo se pueden devolver punteros desde funciones.
Y al usar este mecanismo de pasar punteros
a una función, demuestra o imita el paso por
referencia porque está copiando en una dirección.
Y cuando copia una dirección, si escribe en esa dirección, actualizará lo que
sea que apunte a esa
dirección, lo que efectivamente significa que está cambiando el valor de los
datos dentro de la función, lo reflejará fuera de la función.
Usamos la rutina de intercambio para demostrar eso.
Este es un concepto muy poderoso. Y desea imitar el paso por referencia,
puede usar punteros para hacer eso y desea hacerlo cuando desee cambiar cosas fuera de la función.
Gracias.