Está en la página 1de 8

INSTITUTO TECNOLOGICO DE

TLALNEPANTLA
UNIDAD 6
ALGORITMOS Y PROGRAMACION
GRUPO: 2M2
ALUMNO: JOSE VICTOR MUCIÑO
PEREZ
PROFESOR: JOSÉ MARTÍN
ALEJANDRE ARGUIJO
UNIDAD 6 APUNTADORES
Apuntadores.
Los apuntadores son uno de los tabúes del lenguaje de programación C (otro tabú
es la recursividad, aunque ésta no está asociada con ningún lenguaje sino con la
programación).
Esta entrada introduce a los conceptos y manipulación de apuntadores; la principal
intención es que los apuntadores dejen de ser un tabú y pasen a ser parte del
repertorio de herramientas del programador, ya que los apuntadores son, en más
de un sentido, la piedra angular del lenguaje de programación C.
Definición, estructura y representación.
La sencillez de los apuntadores se basa en su definición la cual es bastante fácil
de recordar y es, en esencia, casi todo lo que se tiene que saber respecto a los
apuntadores y de ahí la importancia de su comprensión.
Un apuntador almacena direcciones de memoria. Básicamente hay dos tipos de
apuntadores:
Apuntadores a variables.
Apuntadores a funciones.
Si el apuntador es a una variable, entonces el apuntador almacena la dirección en
memoria de dicha variable. Si el apuntador es a una función, entonces el
apuntador almacena la dirección en memoria
del inicio del código de dicha función. Por ser
en principio más simples, se presentarán
primero los apuntadores a variables.
Las siguientes figuras muestran la
representación de un apuntador, y su
explicación se verá reforzada cuando se
analice el Ejemplo
Mientras tanto, tome en cuenta que un
apuntador sólo puede hacer referencia o
apuntar a objetos de su mismo tipo de datos,
esto es: un apuntador a int, sólo puede hacer referencia a variables de tipo int, un
apuntador a char, sólo puede hacer referencia a variables de tipo char, etc.
La declaración de una variable de tipo apuntador tiene la siguiente estructura
general en el lenguaje de programación C:
tipo_de_dato * nombre_del_apuntador;
en donde:
tipo_de_dato es cualquier tipo de dato válido en C.
nombre_del_apuntador es un identificador válido en C.
* es el operador que denota que la variable nombre_del_apuntador es una variable
de tipo apuntador a tipo_de_dato.
Una variable de tipo apuntador debe ser congruente, es decir, sólo puede hacer
referencia (apuntar) a variables de su mismo tipo de datos; así por ejemplo, una
variable de tipo apuntador a int puede hacer referencia a una variable de tipo int,
mientras que una variable de tipo apuntador a float puede hacer referencia a una
variable de tipo float y así sucesivamente para los demás tipos de datos.
Piedra angular de la piedra angular
El Ejemplo anterior es fundamental para entender a los apuntadores; por lo que
resulta indispensable su total y absoluta comprensión para poder continuar y
construir, en base a él, los conceptos subsecuentes. Recuerde apoyarse de las
figuras anteriores para la mejor comprensión del ejemplo.
La línea 9 muestra la declaración de dos variables de tipo apuntador a entero: aPtr
y bPtr (note que como parte del identificador para cada una de las variables, se ha
utilizado Ptr (Pointer) a forma de sufijo, esto no es obligatorio ni por sí mismo
vuelve a la variable de tipo apuntador, pero es una buena práctica de
programación el hacerlo así, debido a que refuerza, por auto documentación, que
la variable es de tipo apuntador, ya que después de la declaración, no hay nada
particular en la variable que en sí misma denote que es o no de tipo apuntador).
Observe que el operador "*" no se distribuye en las variables como lo hace el tipo
de dato, por lo que por cada variable de tipo apuntador que se necesite, se deberá
escribir dicho operador para especificar que la variable asociada es de tipo
apuntador.
Las líneas 11, 12 y 13 inicializan a la variable a con 40178, y a aPtr y bPtr con la
dirección en memoria de a. El operador "&" obtiene la dirección en memoria del
operando (variable) asociado(a), y se conoce como el operador de dirección.
Una variable de tipo apuntador, al ser una variable cuyo contenido es la dirección
en memoria de otra variable, almacena direcciones de memoria obtenidas por el
operador de dirección (el único valor que puede asignarse directamente a una
variable de tipo apuntador es el 0, denotando así que el apuntador no hace
referencia a nada, es decir, que no apunta a ninguna dirección válida de memoria
en cuyo caso se dice que es un apuntador nulo), y aunque en principio es posible
asignarle a un apuntador un valor específico, esto generará, con toda seguridad
una violación de memoria y el programa será terminado por la mayoría de
sistemas operativos (ningún sistema operativo debería permitir la intromisión en
áreas de memoria que no sean las propias del programa, ya que ésto incurriría en
graves errores de seguridad). Las figuras anteriores (a) y (b) ilustran lo que ocurre
en las líneas 11-13.
Las funciones printf de las líneas 15 y 16 contienen el especificador de formato
"%p" (pointer), el cual permite visualizar en la salida estándar direcciones de
memoria. Note cómo la dirección de memoria que se imprime en estas líneas es la
misma, tal y como lo muestra la siguiente figura

La línea 19 muestra el uso del operador de des referencia "*", el cual se utiliza
para acceder al valor de la variable a la que se apunta o se hace referencia a
través del apuntador.
Para variables de tipo apuntador, el operador "*" tiene una dualidad:
En la declaración de variables: se utiliza o sirve para denotar que la variable
asociada es de tipo apuntador.
En la des referencia de variables: se utiliza o sirve para acceder al valor al que
hace referencia o apunta el apuntador.
Finalmente, la línea 21 modifica el valor de la variable a a través del apuntador
bptr (recuerde que la variable a está siendo referida (apuntada) por las variables
aPtr y bPtr (líneas 12 y 13 respectivamente)), y del operador de des referencia, por
lo que el valor a imprimir en la salida estándar para la variable a, será 2018, tal y
como lo muestra la figura anterior.
Antes de continuar, asegúrese de entender en su totalidad el Ejemplo y lo que se
ha descrito de él, ya que se insiste en que su comprensión es fundamental para
entender los conceptos descritos en las entradas siguientes.
PRECEDENCIA DE OPERADORES DE APUNTADORES
La precedencia de un operador indica qué tan "estrechamente" se unen dos
expresiones juntas. Por ejemplo, en la expresión 1 + 5 * 3 , la respuesta es 16 y no
18 porque el operador de multiplicación ("*") tiene una precedencia mayor que el
operador de adición ("+"). Los paréntesis pueden ser usados para forzar la
precedencia, si es necesario. Por ejemplo: (1 + 5) * 3 se evalúa como 18.
Cuando los operadores tienen igual precedencia su asociatividad decide cómo se
agrupan. Por ejemplo "-" tiene asociatividad a izquierda, así 1 - 2 - 3 se agrupa
como (1 - 2) - 3 y se evalúa a -4. "=", por otra parte, tiene asociatividad a derecha,
así $a = $b = $c se agrupa como $a = ($b = $c).
Los operadores de igual precedencia que no son asociativos no pueden usarse
unos junto a otros, por ejemplo, 1 < 2 > 1 es ilegal en PHP. La expresión 1 <= 1 ==
1, por otro lado, es legal, ya que el operador == tiene menos precedencia que el
operador <=.
El uso de paréntesis, incluso cuando no es estrictamente necesario, a menudo
puede aumentar la legibilidad del código haciendo grupos explícitamente en lugar
de confiar en la precedencia y asociatividad implícitas del operador.
La siguiente tabla enumera los operadores en orden de precedencia, con los de
más alta precedencia al inicio. Los operadores en la misma línea tienen igual
precedencia, en cuyo caso la asociatividad decide el agrupamiento.

Ejemplo de Asociatividad
<?php
$a = 3 * 3 % 5; // (3 * 3) % 5 = 4
// la asociatividad del operador ternario difiere de C/C++
$a = true ? 0 : true ? 1 : 2; // (true ? 0 : true) ? 1 : 2 = 2
$a = 1;
$b = 2;
$a = $b += 3; // $a = ($b += 3) -> $a = 5, $b = 5
?>
La precedencia y asociatividad de los operadores solamente determinan cómo se
agrupan las expresiones, no especifican un orden de evaluación.
PHP no especifica (en general) el orden en que se evalúa una expresión y se
debería evitar el código que se asume un orden específico de evaluación, ya que
el comportamiento puede cambiar entre versiones de PHP o dependiendo de
código circundante.
Ejemplo de Orden de evaluación no definido
<?php
$a = 1;
echo $a + $a++; // podría mostrar 2 o 3
$i = 1;
$array[$i] = $i++; // podría establecer el índice a 1 o 2
?>
Ejemplo +, - y . tienen la misma precedencia
<?php
$x = 4;
// esta línea podría resultar en una salida inesperada:
echo "x menos uno igual a " . $x-1 . ", o eso espero\n";
// ya que se evalúa como esta línea:
echo (("x menos uno igual a " . $x) - 1) . ", o eso espero\n";
// la precedencia deseada se puede forzar con paréntesis:
echo "x menos uno igual a " . ($x-1) . ", o eso espero\n";
?>
El resultado del ejemplo sería:
-1, o eso espero
-1, o eso espero
x menos uno igual a 3, o eso espero
Nota:
Aunque = tiene una precedencia menor que la mayoría de los demás operadores,
PHP aun permitirá expresiones similares a lo siguiente: if (!$a = foo()), en cuyo
caso el valor devuelto de foo() es puesto en $a.
Llamadas por referencia
Usos de las llamadas por referencia
Las llamadas por referencia son más rápidas, ya que no creamos nuevos valores
en la memoria.
Aparte del rendimiento, las funciones por referencia son usadas cuando queremos
devolver dos variables; eso no es posible con una función normal, pero con una
función por referencia sí lo es.
Veamos el caso de una función (sin sentido, pero sirve para ejemplificar) que
devuelve dos números enteros. No puede hacer un return, en cambio mejor recibe
a ambos números y a cada uno de ellos le asigna un valor.
#include<stdio.h>
// Es una buena práctica definir el prototipo de las funciones aquí arriba
// ojo: sólo el prototipo, no el cuerpo
int devolverEnteros(int *numero1, int *numero2);
int main(int argc, char const *argv[])
{
int numero1 = 0, numero2 = 0;
printf("Antes de llamar a la función, numero1 es %d y numero2 es
%d\n", numero1, numero2);
// Con el operador & obtenemos la dirección de numero
devolverEnteros(&numero1, &numero2);
printf("Despues de llamar a la función, numero1 es %d y numero2 es
%d\n", numero1, numero2);}
// Ahora sí definimos la función con todo y cuerpo
//Notar el * antes de numero
int devolverEnteros(int *numero1, int *numero2){
(*numero1) = 10;
(*numero2) = 20;}
view rawdevolver_enteros.c hosted with ❤ by GitHub
Con la siguiente salida:
Antes de llamar a la función, numero1 es 0 y numero2 es 0
Después de llamar a la función, numero1 es 10 y numero2 es 20
Utilización de los Apuntadores
Los apuntadores son usados principalmente en la creación de arboles, grafos y
otras estructuras de datos como métodos de búsqueda y para la asignación de
memoria dinámica.
A continuación, un ejemplo en C++ donde una función opera 2 punteros y modifica
su valor original por su cuadrado, para su posterior uso.

Trabajar a nivel de memoria genera muchas ventajas ente ellas rapidez y la


sencillez para manipular los datos. Dentro de las aplicaciones más útiles de los
apuntadores se encuentran las que tienen que ver con arreglos.
Como ya se mencionó, el acceso a un arreglo se puede hacer médiate un índice a
cada localidad del mismo. Sin embargo, otra forma de recorrer un arreglo es
mediante un apuntador, haciendo más eficiente el acceso a los datos por la
rapidez que proporciona éste, esto debido al concepto aritmética de apuntadores
Dentro de la aritmética de los apuntadores, suponiendo que apArr es un apuntador
a algún elemento de arreglo, entonces apArr++: incrementa apArr para apuntar a
la siguiente localidad de memoria.
ApArr+=i: incrementa apArr para apuntar a la i-ésima localidad de memoria a partir
del valor inicial de apArr

También podría gustarte