Hola y bienvenido de nuevo.
En esta conferencia, quiero hablar sobre el caso de
uso de los punteros cuando los vamos a usar, cómo los vamos a usar. Sabemos cómo crearlos.
declararlos y definirlos.
Sabemos cómo asignarles la dirección de una variable existente usando la dirección del operador.
Y sabemos cómo quitarles la referencia,
cómo obtener el valor al que apunta el puntero.
Ahora, ¿cómo empezamos a usarlos?
Entonces c ofrece varias operaciones básicas que puede realizar con punteros.
Puede asignar una dirección a un
puntero que ya conocemos.
Esta es una variable existente donde la variable tiene una dirección.
Y solo mira la dirección de esa variable usando la dirección del operador,
y tenemos razón en eso. También podríamos asignar un puntero existente a otro puntero.
Esas son 2 formas de asignar una dirección a un puntero.
Por lo tanto, puede ser un nombre de matriz por sí solo.
Todavía no hemos hablado de matrices, pero cuando asigna un aumento a un puntero, todo
lo que tiene que hacer es especificar el nombre. Ni siquiera tiene que utilizar la dirección del operador.
Hasta ahora nos hemos centrado en utilizar una variable existente y
pasar la dirección de la propia variable.
Pero allí se mencionan las 3 formas en que puede hacerlo.
También puede eliminar la referencia a un puntero. Nos acabamos de enterar de que ahí es donde se usa,
el asterisco, el operador de indirección.
Esto le dará el valor que está almacenado en el puntero a. El puntero es el valor del
puntero, que es una dirección a la que apunta
el punto, solo utilícelo. También puede tomar una dirección de puntero.
Esta es la dirección real del puntero cuando crea el
puntero donde se almacena el puntero.
También puede usar la dirección que tengo operador en ese puntero.
Y también puede realizar aritmética de punteros. Vamos a tener una conferencia separada sobre esto.
Aquí es donde puede usar los operadores más
y menos para sumar enteros a un puntero o restar enteros de un puntero.
Y esto le permite tener una notación específica y le
permite saltar en la memoria más rápido, especialmente si está utilizando matrices.
Entonces, lo que normalmente hace es cuando agrega un número
entero a un puntero usando
el operador más,
simplemente establecerá la dirección de ese puntero en tantos bytes.
Si resta
un número de un puntero, establecerá
esa dirección menos una cierta cantidad de bytes.
También puede incrementar el decremento en uno,
y verá que esto es muy común cuando lo usamos en matrices.
Entonces, cuando está en una matriz y desea pasar al siguiente elemento en lugar de usar el
índice, puede usar un puntero e incrementarlo en uno.
Si desea moverse al elemento anterior en lugar de usar un
índice, puede disminuirlo apuntando en uno.
Entonces, hablaremos de aritmética de punteros exclusivamente, pero eso es
algo que puedes hacer con punteros.
Y nuevamente, es una notación particular la que le permite moverse rápidamente
en la memoria, ser
más eficiente.
También puede encontrar la diferencia entre 2 punteros.
Entonces, si tiene 2 punteros y los elementos están en la misma
matriz, tal vez desee averiguar qué tan
separados están, los elementos, puede restarlos.
Esta sería la diferencia de
2 punteros y. Entonces, si está en el índice 5
y luego tenía un punto de puntero en el índice 5, tenía otro
que apuntaba al índice 15, podría
restarlos y eso
le daría cuántos elementos están lejos, porque sería la dirección hexadecimal menos
la otra dirección hexadecimal asumiendo la matriz en
la memoria continua que es, mostrará qué tan separados están.
Puede utilizar operadores relacionales para comparar los valores de 2 punteros.
Los punteros deben ser del mismo tipo. Entonces, si ambos apuntan a números enteros, ambos tienen que
ser del mismo tipo.
Esto solo le dirá si las direcciones son mayores o menores que.
Nuevamente, puede identificar en qué parte de la matriz se encuentra.
También hay 2 formas de resta.
Así que acabo de hablar de
la resta, en la que puedes tomar un puntero y restarlo de
otro, lo que
obtendrás un puntero o puedes restar un puntero de
un número entero, lo que te dará un puntero.
Entonces, reste un puntero de otro para obtener un número entero o para
restar un número entero de un puntero para obtener un puntero. Esos son los 2 casos.
Así que tendremos en cuenta que veremos algunos ejemplos más.
Tenga cuidado cuando utilice el incremento o la disminución de punteros y haga que
la matriz se salga de los límites. Entonces, la mayoría de estas cosas que hace
con la aritmética de punteros es cuando está operando en matrices.
Porque nuevamente, te permite moverte rápidamente, es una sintaxis simple.
Pero recuerde, las matrices tienen un error fuera de límites.
Entonces, si aumenta demasiado, se saldrá de los
límites en la matriz. Si disminuye demasiado, tendrá un
límite inferior al inferior, ingresará en un número negativo.
La computadora en sí no va a realizar un seguimiento de si un puntero todavía apunta a un elemento de la matriz.
Entonces, al usar el punto de la aritmética en matrices, debes tener cuidado.
Si desea utilizar punteros en expresiones, también puede hacerlo.
Así que hablamos de esto un poco antes, desreferenciamos el puntero y le
agregamos 15. Creo que tenemos 20.
Entonces, el valor del puntero pi se puede usar en expresiones aritméticas.
Entonces, si una variable se define como de tipo puntero a entero, entonces
se evalúa usando las reglas de la aritmética de enteros.
Y lo demostramos con la desreferencia. Entonces, si tiene
una variable y tiene un puntero, inicialícela en nulo, luego firme su
puntero con la dirección de esa variable. A continuación, puede decir que el número de estrella más es igual a 25.
Recuerde, star pnumber obtiene el valor al que apunta.
Entonces, lo que está haciendo es decir que el valor de pnumber
es igual al valor de pnumber más 25.
Entonces el valor será igual a 35.
Esto no cambia a lo que apunta porque estamos usando un asterisco, por lo que no
cambia la dirección a la que apuntamos, no
cambia la dirección en sí porque no tenemos la dirección del operador.
Estamos cambiando el valor al que apunta.
Entonces, al incrementar el valor de la variable numérica en 25, la estrella indica que
está preguntando el contenido al que apunta la variable llamada pnumber.
Si un puntero apunta a la variable x, ese
puntero se ha definido como un puntero al mismo tipo de datos que x.
Entonces, si crea un puntero final, un puntero flotante, solo
puede tener una dirección de
un tipo de datos que sea flotante o int, dependiendo de cuál haya declarado.
Entonces, el uso del operador de indirección en
una expresión es idéntico al uso de x en la misma expresión.
Y eso es todo lo que dice es que si usa un flotador o tiene
un int y usa el operador de indirección, lo desreferencia, puede hacer lo
que pueda si tuviera un flotador en él, lo que sea que esté apuntando.
Una variable definida como un puntero a un int puede almacenar la dirección de
cualquier variable de tipo in, eso lo sabemos.
Entonces, aquí el valor int es igual a 999, podemos asignar ese int a
pnumber siempre que pnumber sea un puntero a un int,
y luego podemos agregarle.
La instrucción operará con el nuevo valor de variable
y el nuevo valor de contenido será 1024.
Pnumber apunta al valor, la
dirección del valor. Entonces, si cambia el puntero
al valor que apunta, cambiará el valor de esta variable.
Así que recuerde que este es el concepto de cambiar una variable en un
punto del puntero y lo está cambiando en múltiples variables.
Un puntero puede contener la dirección de cualquier variable del tipo apropiado.
Por lo tanto, puede usar una variable de puntero para cambiar los valores de muchas variables
diferentes si hay enteros, dobles o flotantes. Siempre que sean del tipo compatible con el tipo
de puntero, puede cambiar ese valor. No puede cambiar el valor
de pnumbers si pnumber apunta a un flotante y el valor
es un número entero. No podrías hacer eso. Tendría un error de compilación.
Otro ejemplo aquí. Aquí le mostraremos cómo
obtener la dirección de algunas variables. Así que aquí en este main,
estamos haciendo aritmética
de punteros en las expresiones reales.
Por lo tanto, no estamos haciendo nada en la matriz, por lo que no aumentamos ni disminuimos
los punteros en eso. Entonces estamos disminuyendo valores. Entonces tenemos 2 tipos
largos num1 y num2 y luego tenemos un puntero de tipo largo, lo inicializamos a nulo.
Asignamos la dirección de num1 a nuestro puntero pnum.
Y luego asignamos el valor de pnum a L,
lo que significa que num1 ahora será 2l.
Si incrementamos num2, ahora num2 no ha hecho
nada todavía.
Entonces el valor de pnum es 2l, eso significa que el valor de num1 es 2l.
L es solo el sufijo para significar que es largo.
Si ahora incrementamos el valor largo num2,
es 0l, será 1l.
Si decimos que num2 más es igual
a la estrella pnum, pnum es 2l, entonces será
1l más 2l, num2 ahora será igual a 3l.
Y luego, si asignamos la dirección de num2 a pnum, pnum
ahora será igual a la dirección de num2.
Entonces, si incrementamos pnum a
lo que estamos apuntando, ahora tendremos 4l.
Y veremos esto en el interior de bloques fríos,
pero te lo voy a guiar. Num1 será igual a 2l.
Num2 va a ser igual a que
apunta a pnum.
Pnum's 3l, así que creo que serán 3l.
Pnum va a ser 3l, y luego pneum más num2 va
a ser 6l.
Entonces, veamos si estoy en lo cierto, repase esto en bloques de código.
Así que aquí estamos en bloques de código, traté de revisar esto a mano, pero
ahora queremos ver si estamos en lo cierto. Usemos el depurador.
Lo pondremos aquí y veremos a medida que avanzamos en el programa cómo cambian los valores.
Así que construyamos esto y depurémoslo.
Esto va a comenzar y se detendrá en la línea 10.
Y si abrimos nuestra ventana de reloj, lo que
podemos hacer yendo a depurar en relojes de Windows, relojes, podemos ver
los valores a medida que avanzan. Entonces ahora num1
es cero, num2 es cero, pnum apunta a nulo.
Por eso es 0x0,
porque sabemos que nulo representa 0 en pnumber.
Y podemos usar eso cuando lo comparamos en declaraciones if.
Entonces, después de ejecutar la línea 10, pnum ahora debería tener la dirección num1.
Así que pasemos por encima.
Ahora pnum tiene una dirección real 0x60ff08.
Después de ejecutar la línea 11, diremos que el valor de pnum es igual a 2l, y no veremos nada
en la ventana de observación para eso. Pero sigamos adelante y ejecútelo, pnum todavía
tiene la misma dirección.
Sin embargo, Num1 ahora se ha cambiado porque pnum
tenía la dirección num1 y la cambiamos.
Entonces ahora num1 es 2. Ahora en las líneas 12, vamos a incrementar num2.
Entonces num2 simplemente se convertirá en 1l.
Va a ser 1. La dirección de pnum sigue siendo la misma.
Ahora vamos a agregar a num2 lo
que sea pnum. Pnum recuerda está apuntando
a num1, por lo que num2 debería ser 3.
Luego vamos a 14, num2 es 3, num1 sigue siendo 2.
Ahora en la línea 15, vamos a
asignar a pnum una nueva dirección de num2. Entonces, después
de esto, ya no seremos 0x60ff08.
Vamos a ser 060ff04.
Entonces, si incrementamos el valor al que apunta pnum, que es el número 2
y el número 2 es 3, el número 2 debería ser 4.
Y vemos que es 4.
Ahora vamos a imprimir todos estos valores y verá que el valor de num1
debería ser 2.
El valor de num2 debe ser 4.
Pnum apunta a num2, por lo que debería ser 4 todavía.
Y luego vamos a decir que el valor de pnum que es 4
más, num2 que es 4 debería ser 8.
Así que podría haberme ido con 6.
Así que ahora no podremos ver eso en la ventana de visualización. Así que juguemos
y veamos la salida como declaraciones printf. Deberíamos ver 2448.
Así que ejecutemos esto ahora en modo sin
depuración, y vemos 2 4 4 8.
Así que ahí vamos. Espero que tenga sentido.
Regresemos y continuemos nuestra discusión.
Entonces, ¿qué tal cuando recibimos información, cómo podemos usar punteros de esa manera?
Hasta ahora, cuando usamos scanf para ingresar valores, tenemos que usar el operador ampersand siempre que
tengamos un tipo de datos inter o flotante. La única vez que lo tenemos es cuando teníamos
una matriz de caracteres y la razón es porque el carácter a en
realidad es un puntero de carácter del que vamos a hablar.
Pero si usamos scanf para ingresar valores y los usamos en tipos de datos
normales que no son matrices de caracteres, siempre hemos tenido que usar este operador adicional o secundario.
Y la razón de esto es que el segundo argumento de scanf es un puntero.
Entonces tenemos que enviarle una dirección. Los punteros solo tienen valores de direcciones.
Entonces, cuando tiene un puntero que ya contiene una dirección, puede usar el nombre del puntero como argumento para scanf.
Por lo tanto, ahora no tiene que usar el operador ampersand si realmente creó
un puntero, porque el segundo argumento del scanf es un puntero, por lo que
solo puede decir el nombre del puntero.
En el pasado, si queríamos almacenar un valor en
valor, diríamos ampersand value
en el segundo argumento de scanf.
Pero si tenemos un puntero que creamos y ya apunta a una
dirección como lo hace en este ejemplo,
entonces cuando usamos scanf, todo lo que tenemos que decir es el nombre del valor,
el nombre del puntero.
Así que aquí hay un caso en el que tenemos un número
entero, pero no tenemos que usar ampersand porque tenemos un puntero a un número entero.
Simplemente decimos porcentaje d, valor p.
Entonces, hay otras ocasiones en las que puede pasar
nombres regulares de variables a scanf además de matrices de caracteres si
tiene un puntero.
También puede imprimir lo que ingresaron después y notará que
el valor realmente se cambiará a lo que ingresaron
porque apuntamos a esa dirección.
Prueba de nulo.
Hay una regla que debes grabar en tu memoria. Esto es muy, muy importante.
Hablamos de desreferenciar, pero ¿qué sucede si desreferencia un puntero
no intencional, un puntero que apunta a nada
o un puntero que no tiene memoria asignada?
Entonces, ¿qué pasa si hacemos algo como esto? Declaramos un puntero,
pero en realidad nunca lo inicializamos, y luego intentamos asignarle.
Le quitamos la referencia y le asignamos, eso es un gran error.
Entonces, la segunda línea básicamente significa que desea
almacenar el valor 5 en la ubicación a la que
apunta pt, pero pt no apunta a nada porque no se ha inicializado.
Entonces pt ahora tendrá un valor aleatorio.
Por lo tanto, no se sabe dónde se colocará el 5, podría colocarse en cualquier lugar de la memoria.
Esto puede resultar inofensivo. Podría sobrevalorar los
datos existentes o las variables de estado existentes en su programa.
Podría provocar que el programa se bloquee. Es muy problemático.
Entonces, cuando crea un puntero y asigna memoria para almacenar el puntero en sí, no
asigna memoria para almacenar los datos.
Entonces, al decir instar pt, esa
memoria asignada para el puntero, pero no
asignó memoria para el valor del
puntero, la dirección. Recuerde, los punteros contienen direcciones.
Creó el puntero pero no creó ninguna memoria para lo que apunta.
Así que no se puede eliminar la referencia.
Por tanto, no asigna memoria para almacenar los datos.
Antes de usar un puntero, debe asignársele una ubicación de memoria que ya ha sido
asignada o debe usar la asignación de memoria dinámica usando malloc, del que hablaremos.
Así que asigna la dirección de una variable existente al puntero o
usa malloc, que es la asignación de memoria dinámica.
Pero tenga eso en cuenta cada vez que use un puntero si lo
creó y era nulo, desea verificar si es nulo de
antemano, hablaremos de eso en un segundo. Pero si creó un puntero
que no se inicializó, no le asigne nada a
menos que esté apuntando a otra variable inicializada de esa manera o con memoria asignada.
Entonces aquí es donde entra en juego null.
De hecho, puede probar nulo para asegurarse de
que no tiene un error de desreferencia. Asegúrese de que su programa no se bloquee.
Entonces ya sabemos que null básicamente no apunta
a nada y debemos inicializar todas nuestras variables, nuestros
apuntadores a null.
Así que siempre deberíamos hacer algo como esto.
Nulo es un símbolo especial en c que representa el puntero equivalente
a cero con números ordinarios.
Lo siguiente también establece un puntero en nulo usando cero.
Entonces también podemos, si no queremos pasarlo o asignarlo a nulo, podemos simplemente decir 0 y eso es
el equivalente a decir que no apunta a nada.
Entonces, debido a que nulo es el equivalente
de 0, si desea probar si el valor p es nulo, puede hacer
algo como usar pruebas explícitas. Puede decir que el puntero es igual a nulo.
Y si el puntero es igual a nulo, lo que significa
que es cero, entonces puedes hacer algo.
Entonces, si no es cero o no es nulo, es un valor verdadero o cualquier
otro, esa es una forma de verificar que no haya fallas.
Antes de intentar eliminar la referencia a algo, asegúrese
de que esto le dirá si tiene memoria asignada o no.
Por lo tanto, desea verificar si hay nulo antes de eliminar la referencia a un puntero.
Y a menudo hace esto en funciones
en porque las funciones tomarán punteros como parámetros.
Y si la persona
que llama está pasando datos incorrectos o pasando un puntero no tiene ninguna
memoria asignada, podría bloquear su código. Entonces, lo que desea hacer es asegurarse de que
la persona que llama tenga una dirección válida para que pueda verificar si es nula.
Básicamente, lo que esto significa tener siempre estos controles if
para decir no valor p o no puntero.
Y si no es un puntero, puede eliminar la referencia.
Hablamos mucho de punteros. Hemos hablado bastante.
Estoy seguro de que se está volviendo un poco abrumador. Es posible que desee tomar un
descanso y repasar las conferencias anteriores para tratar de sincronizar realmente este concepto en su cabeza.
Los punteros solo contienen direcciones.
Pero tiene 3 partes, por lo que puede volverse problemático.
Pero mantén una actitud positiva. Sigue conmigo y sé
que es confuso.
Hemos hablado de trabajar con direcciones.
Hemos hablado de trabajar con valores. Hemos hablado de trabajar con el puntero
en sí y hemos hablado de trabajar con variables que apuntan a un puntero.
Así que hay todas estas
cosas que debes recordar y pueden ser muy, muy abrumadoras y frustrantes.
Y siempre es difícil tratar de averiguar qué está pasando y se necesitará mucha práctica.
La mejor forma de entender este concepto y todo lo que hemos
hablado hasta ahora es
practicar, escribir programas cortos que utilicen todos los conceptos que he mencionado.
Así que escriba un programa que obtenga
los valores usando un puntero, esto sería quizás usando el
operador de indirección, desreferenciar. Escribe un programa que cambie valores usando punteros.
Entonces esto sería desreferenciar, obtener los valores usando punteros sería solo el
nombre del puntero que sería la dirección. Imprimiendo direcciones, hemos revisado toda esa sintaxis.
Practica estas cosas una y otra vez, así es como te volverás bueno en eso.
Tienes que ganar confianza y estudiar estos conceptos en
detalle para dominarlos.
Todavía tenemos mucho más de qué hablar sobre los consejos, pero este puede ser un momento para simplemente dar un paso atrás
y repasar lo que hemos hablado hasta ahora para que se le incruste en la cabeza.
Gracias.