0% encontró este documento útil (0 votos)
107 vistas28 páginas

Unidad 4

El documento describe los arreglos unidimensionales, que son estructuras de datos formadas por una colección ordenada de elementos del mismo tipo. Los arreglos unidimensionales permiten el acceso directo a cualquier elemento mediante su índice. Se explica cómo declarar e inicializar arreglos, así como realizar operaciones como lectura, escritura y asignación sobre sus elementos.
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
107 vistas28 páginas

Unidad 4

El documento describe los arreglos unidimensionales, que son estructuras de datos formadas por una colección ordenada de elementos del mismo tipo. Los arreglos unidimensionales permiten el acceso directo a cualquier elemento mediante su índice. Se explica cómo declarar e inicializar arreglos, así como realizar operaciones como lectura, escritura y asignación sobre sus elementos.
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

UNIDAD IV ESTRUCUTRAS DE DATOS

4.1. Arreglo Unidimensionales


4.1.1. Concepto y forma general

Un arreglo unidimensional es un tipo de datos estructurado que está formado de una colección
finita y ordenada de datos del mismo tipo. Es la estructura natural para modelar listas de
elementos iguales.

El tipo de acceso a los arreglos unidimensionales es el acceso directo, es decir, podemos


acceder a cualquier elemento del arreglo sin tener que consultar a elementos anteriores o
posteriores, esto mediante el uso de un índice para cada elemento del arreglo que nos da su
posición relativa. Para implementar arreglos unidimensionales se debe reservar espacio en
memoria, y se debe proporcionar la dirección base del arreglo, la cota superior y la inferior.

La declaración de arreglos sigue la estructura general de las declaraciones, es decir consta de


un especificador de clase de almacenamiento opcional, un especificador de tipo de dato, el
identificador del arreglo, el operador u operadores de arreglo con su respectiva dimensión y el
inicializador es opcional.

Sintaxis:

Tipo de dato nombre_del_arreglo [dimensión];

Como ejemplo se tiene:

int a[5];
float arre[50];
char vector[20];

El tipo de acceso a los arreglos unidimensionales es el acceso directo, es decir, podemos


acceder a cualquier elemento del arreglo sin tener que consultar a elementos anteriores o
posteriores, esto mediante el uso de un índice para cada elemento del arreglo que nos da su
posición relativa.

Para implementar arreglos unidimensionales se debe reservar espacio en memoria, y se debe


proporcionar la dirección base del arreglo, la cota superior y la inferior.

Elemento 1

Elemento 2

Elemento 3

Elemento 4

Elemento N

Figura 13. Arreglo Unidimensional

Un arreglo (array) matriz o vector es un conjunto ordenado de elementos homogéneos; esto es,
un arreglo unidimensional va de uno en uno empezando en la localidad número cero y de esta
manera se puede accesar en cualquier instante a la localidad que se desee. Un arreglo se

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

dice que es homogéneo porque todos sus elementos son del mismo tipo, por eso nunca
podremos encontrar un arreglo que contenga en su interior valores enteros conjuntamente con
valores de tipo carácter o veceversa.

Los arreglos están formados por un conjunto de elementos de un mismo tipo de datos que se
almacenan bajo un mismo nombre, y se diferencian por la posición que tiene cada elemento
dentro del arreglo de datos. Al declarar un arreglo, se debe inicializar sus elementos antes de
utilizarlos. Para declarar un arreglo tiene que indicar su tipo, un nombre único y la cantidad de
elementos que va a contener. Por ejemplo, las siguientes instrucciones declaran tres arreglos
distintos:

float costo_partes[50];

int edad_empleados [100];

float precios_acciones [25];

int calificaciones [100];

tipo nombre_arreglo tamaño del arreglo

Calificaciones [0]

Calificaciones [1]

Calificaciones [2]

Calificaciones [99]

Figura 14. Estructura de un arreglo

Para acceder a valores específicos del arreglo, use un valor de índice que apunte al elemento
deseado. Por ejemplo, para acceder al primer elemento del arreglo calificaciones debe utilizar
el valor de índice 0 (calificaciones[0]). Los programas en C siempre indican el primer elemento
de un arreglo con 0 y el último con un valor menor en una unidad al tamaño del arreglo.

El índice siempre va entre corchetes e indica la posición y orden de un vector, por ejemplo
estos serían ejemplos de arreglos y sus índices.

A[1], A[2], A[3], … , A[N]

Arr[0], arr[1], arr[2], … , arr[n]

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

La inicialización de arreglos se realiza por medio del conjunto de valores iniciales de los
distintos elementos del arreglo, agrupado por medio de llaves.
La asignación de valores al arreglo unidimensional es en orden de secuencia con respecto al
valor del índice [0] a [N].

Asignación de un arreglo de tipo entero llamado “ a “ y uno de tipo real llamado “ datos “:

int a[5] = {1,2,3,5,6};

float datos[6] = { 3.4, 6.8, 2.2, 10, 6.6, 9.9 }

En el siguiente ejemplo se muestra el llenado de un arreglo (vector) con valores de cero a 10,
empezando a llenar en la localidad cero y terminando en la localidad nueve.

Algoritmo:

inicio
constante Tam 10
declarar variables
entero i, a[Tam]
para (i = 0; i < Tam; i++) hacer
a[i] ← 0
fin_para
para (i = 0; i < Tam; i++) hacer
escribir ( ‘ a[i] ‘ )
fin_para
return 0;
fin

Programa:

#include <stdio.h>
#define Tam 10
int main(void)
{
int i, a[Tam];

for(i = 0; i < Tam; i++)


a[i] = 0;
for(i = 0; i < Tam; i++)
printf ("%d\n", a[i]);
return 0;
}

Cuando se usan arreglos, una operación común es usar una variable índice para acceder a los
elementos de un arreglo. Suponiendo que el arreglo se llamará valores, y la variable índice i
contiene el número 3, la siguiente instrucción asignará el valor 400 a dicho vector, como si
fuera una variable simple al ocupar una posición de memoria:

Valores [3] ← 400

Valores [3] = 400;

Partes de un arreglo:

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

Nombre X[0] X[1] X[2] X[3] X[4] X[5] X[6]


e índice
14.0 8.0 10.5 4.5 12.0 5.5 10.0

Elemento1 Elemento2 Elemento3 Elemento7

Figura 15. Elementos de un arreglo

Los componentes. Hacen referencia a los elementos que forman el arreglo, es decir, a los
valores que se almacenan en cada una de las casillas del mismo. Los índices, permiten hacer
referencia a los componentes del arreglo, en forma individual especifican cuántos elementos
tendrá el arreglo y además, de qué modo podrán accesarse a esos componentes.

S1 S2 S3 . . . SN

Componentes

Figura 16. Componentes de un arreglo

Las operaciones que se pueden realizar con vectores durante el proceso de resolución de un
problema son:

· Lectura/ escritura

· Asignación

· Actualización (inserción, eliminación, modificación)

· Recorrido (acceso secuencial)

· Ordenación

· Búsqueda

Ejemplos:

Sea arre un arreglo de 70 elementos enteros con índices enteros. Su representación nos
queda:

23 778 10 51 100 ……. 11 99

“0” “1” “2” “3” “4” …….. “68” “69”

Figura 17. Arreglo de enteros

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

Lectura
El proceso de lectura de un arreglo consiste en leer y asignar un valor a cada uno de sus
elementos. Normalmente se realizan con estructuras repetitivas, aunque pueden usarse
estructuras selectivas. Usamos los índices para recorrer los elementos del arreglo:

Algoritmo:

para ( i = 1; i <= 70; i++ ) hacer


leer ( arre[i])
fin_desde

Programa:

for ( i = 1; i <= 70; i++ )


scanf ( “%d” , & arre [i]);

Escritura:
Es similar al caso de lectura, sólo que en vez de leer el componente del arreglo, lo escribimos.

Algoritmo:

leer (N)
para ( i = 1 ; i <= N ; i++ ) hacer
escribir ( arre[i] )
fin_para

Programa:

scanf ( “%d” , & N);


for ( i = 1; i <= N; i++ )
printf ( “ %d \n ” , arre [i]);

Asignación:
No es posible asignar directamente un valor a todo el arreglo; sino que se debe asignar el valor
deseado en cada componente. Con una estructura repetitiva se puede asignar un valor a todos
los elementos del vector.

Por ejemplo:

arre[1] = 120;(asignación de un valor constante único a una casilla del vector)

arre[3] = arre[1] / 4; (asignar una operación)

Se puede asignar un valor constante a todos los elementos del vector:

Algoritmo:

para ( i = 1; i <= 5; i++ ) hacer


arre[i] ← 3
fin_desde

Programa:

for ( i = 1; i <= 5; i++ )


arre [i] = 3;

O bien

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

arre = 3 (con arre del tipo arreglo)

Inicialización
Para inicializar con cero todos los elementos del arreglo:

Algoritmo:

para ( i = 1; i <= 70; i++ ) hacer


arre[i] ← 0
fin_para

Programa:

for ( i = 1; i <= 70; i++ )


arre [i] = 0;

0 0 0 0 0 ……. 0 0

1 2 3 4 5 ........ 69 70
Arre [ 70 ]

Figura 18. Arreglo con for

Acceso Secuencial. (Recorrido)

El acceso a los elementos de un vector puede ser para leer en él o para escribir (visualizar su
contenido). Recorrido del vector es la operación de efectuar una acción general sobre todos los
elementos de ese vector.

Actualización.
Incluye añadir (insertar), borrar o modificar algunos de los ya existentes. Se debe tener en
cuenta si el arreglo está o no ordenado. Añadir datos a un vector consiste en agregar un nuevo
elemento al final del vector, siempre que haya espacio en memoria.

Ejemplo.

Algoritmo:

inicio
declarar variables
entero i
entero a[ 5 ]
para ( i = 0; i < 4; i++ ) hacer
excribir ( ‘ Ingrese el elemento: ‘ )
leer ( a [ i ] )
fin_para
para ( i = 0; i < 4; i++ ) hacer
excribir ( ‘ Elemento: ‘ , a [ i ] )
fin_para
pausa
fin

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

Programa:

#include<stdio.h>
#include<conio.h>
void main()
{
int a[5]; // Definición de un arreglo de 5 posiciones
int i;
// Pedimos el ingreso de 5 números
for(i=0; i<4; i++)
//No olvidar que los arreglos van de 0 a longitud-1
{
printf(“ Ingrese el elemento: ”);
scanf(“%d”,&a[i]);
}
// Para imprimir será
for(i=0; i<4; i++)
{
printf(“ Elemento: %d ”,a[i]);
}
getch();
}

4.1.2. Arreglos numéricos y de caracteres

Un vector a de 10 enteros de tipo int se declara así:

int a[10];

El vector a comprende los elementos a[0], a[1], a[2], . . . , a[9], todos de tipo int. Los índices de
los vectores en C empiezan en cero.

Al igual que ocurría con las variables “normales”, podemos dar valor a los elementos de un
arreglo al principio del programa.

Esta vez los indicaremos todos entre llaves, separados por comas

Algoritmo:

inicio
declarar variables
entero numero [ 5 ] = { 200, 150, 100, -50, 300 }
entero suma
suma = numero [0] + numero[1] + numero [2] + numero[3] + numero [4]
escribir ( ‘ Su suma es ‘, suma )
pausa
fin

Programa:

#include <stdio.h>
main()
{
int numero[5] ={200,150,100,-50,300};
//Un array de 5 números enteros
int suma; // Un entero que será la suma

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

suma = numero[0]+numero[1]+numero[2]+numero[3]+numero[4];
printf("Su suma es %d", suma);
getch();
}

En una misma línea puedes declarar más de un vector, siempre que todos compartan el mismo
tipo de datos para sus componentes.

Las cadenas en C son vectores de caracteres (elementos de tipo char) con una peculiaridad: el
texto de la cadena termina siempre en un carácter nulo.
El carácter nulo tiene código ASCII 0 y podemos representarlo tanto con el entero 0 como con
el carácter ’\0’.

Declaración de cadenas

Las cadenas se declaran como vectores de caracteres, así que debes proporcionar el número
máximo de caracteres que es capaz de almacenar: su capacidad. Esta cadena, por ejemplo, se
declara con capacidad para almacenar 10 caracteres:

char a[10];

Puedes inicializar la cadena con un valor en el momento de su declaración:

char a[10] = "cadena";

Se muestra la declaración como un vector de 10 caracteres y lo hemos inicializado asignándole


la cadena "cadena". Es decir, es como si hubiésemos inicializado la cadena de este otro
modo, equivalente:

char a[10] = { ’c’, ’a’, ’d’, ’e’, ’n’, ’a’, ’\0’ };

4.2. Arreglos bidimensionales


4.2.1. Concepto y forma general

Los arreglo bidimensionales son una forma de lograr que nuestro programa pueda hacer
tablas, utilizando un índice que será siempre un dato del tipo entero. La mayoría de las
definiciones de qué es una tabla son como la siguiente, "Una tabla es un conjunto de variables
que comparten el mismo tipo de dato y nombre y a las que se hace referencia a través de un
índice" (el índice como ya se dijo antes debe ser un tipo de dato entero).

Este tipo de arreglos al igual que los anteriores es un tipo de dato estructurado, finito ordenado
y homogéneo.

El acceso a ellos también es en forma directa por medio de un par de índices. Los arreglos
bidimensionales se usan para representar datos que pueden verse como una tabla con filas y
columnas. La primera dimensión del arreglo representa las columnas, cada elemento contiene
un valor y cada dimensión representa una relación.

Elemento 1,1 ….. Elemento 1,n


Elemento 2,1 …. Elemento 2,n
Elemento 3,1 …. Elemento 3,n
…. …. ….
Elemento m,1 …. Elemento m,n

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

Figura 19. Matriz M x N

También es importante mencionar que algunos autores hacen uso del termino "vector" en los
lenguajes de programación (igual que en las matemáticas) para referirse a las tablas de una
sola dimensión y que matriz siempre se refiere a tablas bidimensionales.

Sintaxis de un arreglo bidimensional:

Tipo de dato nombre del arreglo [filas][columnas];

La representación en memoria se realiza de dos formas: almacenamiento por columnas o por


renglones.

Un arreglo bidimensional “ a “, que contienen tres filas y cuatro columnas (es decir un arreglo
de tres por cuatro). En general, a este tipo de arreglo con m filas y n columnas se le llama
arreglo de n x m (bidimensional).

Es un conjunto de datos homogéneo, finito y ordenado, donde se hace referencia a cada


elemento por medio de dos índices. El primero se utiliza para los renglones (filas) y el segundo
para las columnas. También puede definirse como un arreglo de arreglos. Internamente en
memoria se reservan MxN posiciones consecutivas para almacenar todos los elementos del
arreglo.

Declaración de una matriz:

arreglo [ liminf1, limsup1, liminf2, limsup2 ]

filas columnas

Por ejemplo:

1 <= I <= M
1 <= J <= N

Se representaría como se muestra a continuación en forma de una tabla

1 2 J N
1

2
... … … …
I
M

Figura 20. Declaración de una matriz

Algoritmo para el recorrido por filas:

constantes
M =valor1
N = valor2
declarar variables
real Matriz [M][N]
para ( i = 1; i<= M; i++ ) hacer

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

para ( j = 1; j<= N; j++ ) hacer


escribir ( Matriz [ i] [j ] )
fin_desde
fin_desde

Programa:

#include <stdio.h>
#define M valor1
#define N valor2
main()
{
float Matriz[M][N]
//Un array de M x N números reales
for(i=1;i<=M;i++)
for ( j = 1; j<= N; j++ )
printf(" %f ", Matriz [M][N]);
getch();
}

El recorrido por columnas se hace de manera similar, invirtiendo el sentido de los índices.

constantes
M  valor1
N  valor2
declarar variables
real Matriz [N][M]
para ( i = 1; i<= N; i++ ) hacer
para ( j = 1; j<= M; j++ ) hacer
escribir ( Matriz [ i] [j ] )
fin_desde
fin_desde

Programa:

#include <stdio.h>
#define M valor1
#define N valor2
main()
{
float Matriz[N][M]
//Un array de M x N números reales
for(i=1;i<=N;i++)
for ( j = 1; j<= M; j++ )
printf(" %f ", Matriz [N][M]);
getch();
}

Ejemplos.
1) Rellenar una matriz identidad de 4 por 4 elementos.

Una matriz identidad es aquella en la que la diagonal principal está llena de unos y el resto de
los elementos son cero. Para llenar la matriz identidad se debe verificar que cuando los índices
i y j sean iguales, la posición vale 1, en caso contrario se asigna cero al elemento i, j.

1 0 0 0
0 1 0 0

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

0 0 1 0
0 0 0 1
Figura 21. Matriz Identidad

Algoritmo
inicio
para ( i = 1; i<= 4; i++ ) hacer
para ( j = 1; j <= 4; j++) hacer
si ( i = j ) entonces
Matriz [i] [j]  1
si_no
Matriz[i] [j]  0
fin_si
fin_desde
fin_desde
fin

Programa:

#include <stdio.h>
void main()
{
for(i=1;i<=4;i++)
for ( j = 1; j<= 4; j++ )
if (i == j )
Matriz [i][j] = 1;
else
Matriz [i][j] = 0;
getch();
}

Las operaciones en arreglos pueden clasificarse de la siguiente forma:

a) lectura
Este proceso consiste en leer un dato de un arreglo y asignar un valor a cada uno de sus
componentes.

La lectura se realiza de la siguiente manera:

Algoritmo:

escribir ( ‘ Cuantos valores deseas que tenga el arreglo ‘ )


leer ( N )
para ( i=1; i<= N; i++ ) hacer
leer ( arreglo[i] )

Programa:

printf ( “ Cuantos valores deseas que tenga el arreglo “);


scanf (“%d”, &N);
for ( i=1; i<= N; i++ )
scanf ( “%d”, & arreglo [i] );

b) escritura
Consiste en después de asignarle valores a cada elemento del arreglo, mostrarlos en pantalla.
La escritura se realiza de la siguiente manera:

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

Algoritmo:

escribir ( ‘ Cuantos valores deseas que tenga el arreglo ‘ )


leer ( N )
para ( i=1; i<= N; i++ ) hacer
leer ( arreglo[i] )

para ( i=1; i<= N; i++ ) hacer


escribir ( arreglo [i] );

Programa:

printf ( “ Cuantos valores deseas que tenga el arreglo “);


scanf (“%d”, &N);
for ( i=1; i<= N; i++ )
scanf ( “%d”, &arreglo [i] );

for ( i=1; i<= N; i++ )


printf( “ %d “, arreglo [i] );

c) asignación
No es posible asignar directamente un valor a todo el arreglo, por lo que se realiza mediante
los ciclo de repetición cualquiera de ellos como el for, el do – while ó el while con cualquiera de
ellos el resultado es exactamente el mismo, y es de la manera siguiente:

Algoritmo:

escribir ( ‘ Cuantos valores deseas que tenga el arreglo ‘ )


leer ( N )
para ( i=1; i<= N; i++ ) hacer
arreglo[i]  i

Programa:

printf ( “ Cuantos valores deseas que tenga el arreglo “);


scanf (“%d”, &N);
for ( i=1; i<= N; i++ )
arreglo [i] = i;

En el caso anterior el arreglo es llenado propiamente con la misma variable del ciclo for, por lo
tanto; el arreglo contiene valores que van del 1 al valor N que dio el usuario.

d) actualización
Dentro de esta operación se encuentran las operaciones de eliminar, insertar y modificar datos.
Para realizar este tipo de operaciones se debe tomar en cuenta si el arreglo está o no
ordenado.

La definición formal del parámetro le permite al compilador determinar las características del
valor del puntero que será pasado en tiempo de ejecución.

4.2.2. Arreglos numéricos y de caracteres

En una cadena que definamos como “char texto[40]” lo habitual es que realmente no ocupemos
las 39 letras que podríamos llegar a usar. Si guardamos 9 letras (y el carácter nulo que marca
el final), tendremos 30 posiciones que no hemos usado. Pero estas 30 posiciones
generalmente contendrán “basura”, lo que hubiera previamente en esas posiciones de
memoria, porque el compilador las reserva para nosotros pero no las “limpia”, por lo tanto el
carácter que marca el final de nuestra cadena es el nulo “/0”.

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

Hay una función de cadena predefinida por lenguaje C que nos dice cuantas letras hemos
usado realmente en nuestra cadena sin necesidad de contabilizarlo nosotros mismos, lo hace
automáticamente y es “strlen”, que se usa así:

Algoritmo:

inicio
declarar variables
carácter texto[ 40 ]
escribir ( ‘ Introduce una palabra: ‘ )
leer ( texto )
escribir ( ‘ Has tecleado ‘ , strlen (texto) , ‘ letras ‘ )
pausa
fin

Programa:

#include <stdio.h>
#include <string.h>
main()
{
char texto[40];
printf("Introduce una palabra: ");
scanf("%s", texto);
printf("Has tecleado %d letras", strlen(texto));
getch();
}

Como es de esperar, si escribimos “Hola”, esta orden nos dirá que hemos tecleado 4 letras (no
cuenta el carácter nulo “\0” que se añade automáticamente al final).
Si empleamos esta orden, o alguna de las otras órdenes relacionadas con cadenas de texto
que veremos en este tema, debemos incluir la cabecera <string.h>, que es donde se definen
todas ellas.

Hemos visto que si leemos una cadena de texto con “scanf”, se paraba en el primer espacio en
blanco y no seguía leyendo a partir de ese punto. Existen otras órdenes que están diseñadas
específicamente para manejar cadenas de texto, y que nos podrán servir en casos como éste.

Para leer una cadena de texto (completa, sin parar en el primer espacio), usaríamos la orden
“gets”, así:

gets(texto);

De igual modo, para escribir un texto en pantalla podemos usar “puts”, que muestra la cadena
de texto y avanza a la línea siguiente:

puts(texto);

Sería equivalente a esta otra orden:

printf("%s\n", texto);

Cuando queremos dar a una variable el valor de otra, normalmente usamos construcciones
como a = 2, o como a = b. Pero en el caso de las cadenas de texto, esta NO es la forma
correcta, no podemos hacer algo como saludo = "hola" ni algo como texto1 = texto2. Si
hacemos algo así, haremos que las dos cadenas estén en la misma posición de memoria, y
que los cambios que hagamos a una de ellas se reflejen también en la otra. La forma correcta
de guardar en una cadena de texto un cierto valor es:

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

strcpy (destino, origen);

Es decir, debemos usar una función llamada strcpy (cadena destino, cadena origen), que se
encuentra también en <string.h>. Vamos a ver dos ejemplos de su uso:

strcpy (saludo, "hola");


strcpy (textoDefinitivo, textoProvisional);

En este ejemplo el mensaje cadena “hola” es copiado en la variable llamada saludo.


Es nuestra responsabilidad que en la cadena de destino haya suficiente espacio reservado
para copiar lo que queremos. Si no es así, estaremos sobrescribiendo direcciones de memoria
en las que no sabemos qué hay.

Para evitar este problema, tenemos una forma de indicar que queremos copiar sólo los
primeros n bytes de origen, usando la función strncpy, así:

strncpy (destino, origen, n);

Vamos a ver un ejemplo, que nos pida que tecleemos una frase y guarde en otra variable sólo
las 4 primeras letras:

Algoritmo:

inicio
declarar variables
carácter texto1[ 40 ]
carácter texto2[ 40 ]
carácter texto3[ 10 ]
limpiar pantalla
escribir ( ‘ Introduce una frase: ‘ )
leer ( texto1 )
copiar_cadena (text2, texto1)
escribir ( ‘ Una copia de tu texto es: ‘, texto2 )
copiar_cadena_n ( texto3, texto1, 4)
escribir ( ‘ Y sus cuatro primeras letras son: ‘, texto3 )
pausa
fin

Programa:

#include <stdio.h>
#include <string.h>
main()
{
char texto1[40], texto2[40], texto3[10];
clrscr();
printf("Introduce un frase: ");
gets(texto1);
strcpy(texto2, texto1);
printf("Una copia de tu texto es: %s\n", texto2);
strncpy(texto3, texto1, 4);
printf("Y sus cuatro primeras letras son %s\n", texto3);
getch();
}

Finalmente, existe otra orden relacionada con estas dos: podemos añadir una cadena al final
de otra (concatenarla), con:

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

strcat (destino, origen);

Vamos a ver un ejemplo de su uso, que nos pida nuestro nombre, nuestro apellido y cree una
nueva cadena de texto que contenga los dos, separados por un espacio:

Algoritmo:

inicio
declarar variables
carácter texto1[ 40 ]
carácter texto2[ 40 ]
carácter texto3[ 40 ]
limpiar pantalla
escribir ( ‘ Introduce tu nombre: ‘ )
leer ( texto1 )
escribir ( ‘ Introduce tu apellido: ‘ )
leer ( texto2 )
concatenar ( texto1, “ “ )
concatenar (texto1, texto2 )
escribir ( ‘ Te llamas ‘, texto1 )
pausa
fin

Programa:

#include <stdio.h>
#include <string.h>
main()
{
char texto1[40], texto2[40], texto3[40];
clrscr();
printf("Introduce tu nombre: ");
gets(texto1);
printf("Introduce tu apellido: ");
gets(texto2);
strcat(texto1, " "); /* Añado un espacio al nombre */
strcat(texto1, texto2); /* Y luego el apellido */
printf("Te llamas %s\n", texto1);
getch();
}

4.3. Apuntadores
Hasta ahora teníamos una serie de variables que declaramos al principio del programa o de
cada función. Estas variables, que reciben el nombre de ESTÁTICAS, tienen un tamaño
asignado desde el momento en que se crea el programa.

Un Apuntador es una variable que contiene una dirección de memoria, la cual corresponderá a
un dato o a una variable que contiene el dato. Los apuntadores también deben de seguir las
mismas reglas que se aplican a las demás variables, deben tener nombre únicos y deben de
declararse antes de usarse.

Cada variable que se utiliza en una aplicación ocupa una o varias posiciones de memoria.
Estas posiciones de memoria se accesan por medio de una dirección.

*h
1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN  h
UNIDAD IV ESTRUCUTRAS DE DATOS

1000 1001 1002 1003 1004 1005

H e l l o
Figura 22. Puntero

En la figura el texto “ Hello “ esta guardado en memoria, comenzando en la dirección 1000.


Cada carácter ocupa un espacio de dirección único en memoria. Los apuntadores proporcionan
un método para conservar y llegar a estas direcciones en memoria. Los apuntadores facilitan el
manejo de datos, debido a que conservan la dirección de otra variable o ubicación de datos.

Los apuntadores dan flexibilidad a los programas en C y en C++; también permiten que estos
crezcan dinámicamente. Utilizando un apuntador hacia un bloque de memoria que se asigna al
momento de ejecución, un programa puede ser más flexible que uno que asigna toda su
memoria de una sola vez. También, un apuntador es más fácil de guardar que una estructura
grande o un objeto de una clase. Debido a que un apuntador sólo guarda una dirección, puede
fácilmente pasarse a una función. Uno de las desventajas que pueden presentar los
apuntadores es que un apuntador sin control o no inicializado puede provocar fallas en el
sistema, además de que su uso incorrecto puede generar fallas muy complejas de encontrar.

Este tipo de variables son sencillas de usar y rápidas... si sólo vamos a manejar estructuras de
datos que no cambien, pero resultan poco eficientes si tenemos estructuras cuyo tamaño no
sea siempre el mismo.

Es el caso de una agenda: tenemos una serie de fichas, e iremos añadiendo más. Si
reservamos espacio para 10, no podremos llegar a añadir la número 11, estamos limitando el
máximo. Una solución sería la de trabajar siempre en el disco: no tenemos límite en cuanto a
número de fichas, pero es muchísimo más lento.

Lo ideal sería aprovechar mejor la memoria que tenemos en el ordenador, para guardar en ella
todas las fichas o al menos todas aquellas que quepan en memoria. Una solución “típica” (pero
mala) es sobredimensionar: preparar una agenda contando con 1000 fichas, aunque
supongamos que no vamos a pasar de 200. Esto tiene varios inconvenientes: se desperdicia
memoria, obliga a conocer bien los datos con los que vamos a trabajar, sigue pudiendo verse
sobrepasado, etc.

La solución suele ser crear estructuras DINÁMICAS, que puedan ir creciendo o disminuyendo
según nos interesen. Ejemplos de este tipo de estructuras son:

Las pilas. Como una pila de libros: vamos apilando cosas en la cima, o cogiendo de la
cima.

Las colas. Como las del cine (en teoría): la gente llega por un sitio (la cola) y sale por
el opuesto (la cabeza).

Las listas, en las que se puede añadir elementos, consultarlos o borrarlos en cualquier
posición.

Y la cosa se va complicando: en los árboles cada elemento puede tener varios sucesores, etc.
Todas estas estructuras tienen en común que, si se programan bien, pueden ir creciendo o
decreciendo según haga falta, al contrario que un array, que tiene su tamaño prefijado.
En todas ellas, lo que vamos haciendo es reservar un poco de memoria para cada nuevo
elemento que nos haga falta, y enlazarlo a los que ya teníamos. Cuando queramos borrar un
elemento, enlazamos el anterior a él con el posterior a él (para que no “se rompa” nuestra
estructura) y liberamos la memoria que estaba ocupando.

4.3.1. Concepto

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

Un puntero no es más que una dirección de memoria. Lo que tiene de especial es que
normalmente un puntero tendrá un tipo de datos asociado: por ejemplo, un “puntero a entero”
será una dirección de memoria en la que habrá almacenado (o podremos almacenar) un
número entero.

Hay 2 operadores que se usan cuando trabajan con direcciones en un programa en C; el


Operador de Indirección ( * ) y el de Dirección (&). Estos operadores son diferentes de los
tratados anteriormente.

El Operador de Dirección ( &) regresa la dirección de una variable. Este operador está
asociado con la variable a su derecha: &h; esta línea regresa la dirección de memoria de la
variable h, la que contiene nuestra máquina internamente.

El Operador de Indirección ( * ) trabaja a la inversa del operador de Dirección. También esta


asociado con la variable a su derecha, toma la dirección y regresa el dato que contiene esa
dirección de memoria. Por ejemplo, la siguiente línea determina la dirección de la variable h y
luego usa el operador de Indirección para accesar la variable y darle un valor de 42:

*(&h)=42;

La declaración de un puntero de manera general es:

tipo *nombre del apuntador;

Tipo : Especifica el tipo de objeto apuntado y puede ser cualquier tipo visto con anterioridad.

Nombre de apuntador: Es el identificador del apuntador.

El espacio de memoria requerido para un apuntador, es el número de bytes necesarios para


especificar una dirección de memoria, debiendo apuntar siempre al tipo de dato correcto.

Considere el siguiente programa y observe a las variables de dirección e Indirección trabajar:

Algoritmo:

inicio
declarar variables
entero x  1, y  2
entero *ip
limpiar pantalla
escribir ( ‘ Antes del uso de los apuntadores ‘ )
escribir ( ‘ X = ‘, x , ‘ Y = ‘, y, ‘ *ip = ‘ , *ip )
ip  &x
y  *ip
*ip  10
escribir ( ‘ Después del uso de los operadores ‘ )
escribir ( ‘ X = ‘, x , ‘ Y = ‘, y, ‘ *ip = ‘ , *ip )
pausa
fin

Programa:

#include<stdio.h>
#include<conio.h>
int x=1; y=2;

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

int *ip; /* ip es apuntador a int */


void main()
{
clrscr();
printf(“ Antes del uso de los apuntadores “);
printf(“ \n X = %d Y = %d *ip = %d “, x,y,*ip);
ip = &x; /* ip ahora apunta a x */
y = *ip; /* y vale 1 */
*ip = 10; /* ahora x vale 10 */
printf (“ Después del uso de los operadores “);
printf(“ \n X = %d Y = %d *ip = %d “, x,y,*ip);
getch();
}

La salida (corrida) del programa anterior es:

Antes del uso de los operadores:


X= 1 y=2 *ip = 0

Después del uso de los operadores:

X= 10 y=1 *ip = 10

Vamos a ver qué símbolo usamos en C para designar los punteros:

int num; /* "num" es un número entero */


int *pos; /* "pos" es un "puntero a entero" (dirección de
memoria en la que podremos guardar un entero) */

Es decir, pondremos un asterisco entre el tipo de datos y el nombre de la variable. Ese


asterisco puede ir junto a cualquiera de ambos, también es correcto escribir int* pos; esta
nomenclatura ya la habíamos utilizado aun sin saber que era eso de los punteros. Por ejemplo,
cuando queremos acceder a un fichero, hacemos FILE* fichero; antes de entrar en más
detalles, y para ver la diferencia entre trabajar con “arrays” o con punteros, vamos a hacer dos
programas que pidan varios números enteros al usuario y muestren su suma. El primero
empleará un “array” (una tabla, de tamaño predefinido) y el segundo empleará memoria que
reservaremos durante el funcionamiento del programa.

El primero podría ser así:

Algoritmo:

inicio
declarar variables
entero datos [100]
entero cuantos
entero i
largo suma  0
limpiar pantalla
hacer
escribir ( ‘ Cuantos números desea sumar ? ‘ )
leer ( cuantos )
si ( cuantos > 100 ) hacer
escribir ( ‘ Demasiados. Solo se puede hasta 100. ‘ )
fin_si
mientras ( cuantos > 100 )
para ( i = 0; i < cuantos; i++ ) hacer

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

escribir ( ‘ X = ‘, x , ‘ Y = ‘, y, ‘ *ip = ‘ , *ip )


pausa
fin

Programa:

#include <stdio.h>
main() {
int datos[100];
/* Preparamos espacio para 100 numeros */
int cuantos;
/* Preguntaremos cuantos desea introducir */
int i;
/* Para bucles */
long suma=0;
/* La suma, claro */
clrcsr();
do {
printf(" Cuantos numeros desea sumar? ");
scanf(" %d ", &cuantos);
if (cuantos>100)
/* Solo puede ser 100 o menos */
printf("Demasiados. Solo se puede hasta 100.");
} while (cuantos>100);
/* Si pide demasiado, no le dejamos */
/* Pedimos y almacenamos los datos */
for (i=0; i<cuantos; i++) {
printf(" Introduzca el dato número %d: ", i+1);
scanf(" %d ", &datos[i]);
}
/* Calculamos la suma */
for (i=0; i<cuantos; i++)
suma += datos[i];
printf(" Su suma es: %ld\n ", suma);
}

Los más avispados se pueden dar cuenta de que si sólo quiero calcular la suma, lo podría
hacer a medida que leo cada dato, no necesitaría almacenar todos. Vamos a suponer que sí
necesitamos guardarlos (en muchos casos será verdad, si los cálculos son más complicados).

Entonces nos damos cuenta de que lo que hemos estado haciendo hasta ahora no es eficiente:
• Si quiero sumar 1000 datos, o 500, o 101, no puedo. Nuestro límite previsto era de 100, así
que no podemos trabajar con más datos.
• Si sólo quiero sumar 3 números, desperdicio el espacio de 97 datos que no uso.
• Y el problema sigue: si en vez de 100 números, reservamos espacio para 5000, es más difícil
que nos quedemos cortos pero desperdiciamos muchísima más memoria.

La solución es reservar espacio estrictamente para lo que necesitemos, y eso es algo que
podríamos hacer así:

Algoritmo:

inicio
declarar variables
entero *datos
entero cuantos
entero i
largo suma  0
limpiar pantalla

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

hacer
escribir ( ‘ Cuantos números desea sumar ? ‘ )
leer ( cuantos )
datos  (entero *) malloc (cuantos * sizeof(entero))
si (datos = NULL)
escribir ( ‘ No caben tantos datos en memoria. ‘ )
fin_si
mientras ( datos = NULL )
fin_hacer
escribir ( ‘ Introduzca el dato número ‘, i+1 )
leer ( datos + i )
fin_para
para ( i = 0; i < cuantos; i++ ) hacer
suma  suma + *datos + i
fin_para
escribir ( ‘ Su suma es: ‘, suma )
liberar (datos)
pausa
fin

Programa:

#include <stdio.h>
#include <stdlib.h>
main()
{
int* datos; /* Necesitaremos espacio para varios numeros */
int cuantos; /* Preguntaremos cuantos desea introducir */
int i; /* Para bucles */
long suma=0; /* La suma, claro */
do
{
printf("Cuantos numeros desea sumar? ");
scanf("%d", &cuantos);
datos = (int *) malloc (cuantos * sizeof(int));
if (datos == NULL) /* Solo puede ser 100 o menos */
printf("No caben tantos datos en memoria.");
} while (datos == NULL); /* Si pide demasiado, no le dejamos */
/* Pedimos y almacenamos los datos */
for (i=0; i<cuantos; i++) {
printf("Introduzca el dato número %d: ", i+1);
scanf("%d", datos+i);
}
/* Calculamos la suma */
for (i=0; i<cuantos; i++)
suma += *(datos+i);
printf("Su suma es: %ld \n", suma);
free(datos);
}

Este programa funciona perfectamente si sólo queremos sumar 5 números, pero también si
necesitamos sumar 120.000 (y si caben tantos números en la memoria disponible de nuestro
equipo, claro).

Vamos a ver las diferencias:

En primer lugar, lo que antes era int datos[100] que quiere decir “a partir de la posición de
memoria que llamaré datos, quede espacio para a guardar 100 números enteros”, se ha

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

convertido en int * datos que quiere decir “a partir de la posición de memoria que llamaré datos
voy a guardar varios números enteros (pero aún no sé cuantos)”.

Luego reservamos el espacio exacto que necesitamos, haciendo datos = (int *) malloc (cuantos
* sizeof ( int ); Esta orden suena complicada, así que vamos a verla por partes:

• “malloc” es la orden que usaremos para reservar memoria cuando la necesitemos (es la
abreviatura de las palabra “memory” y “allocate”).

• Como parámetro, le indicamos cuanto espacio queremos reservar. Para 100 números
enteros, sería “100*sizeof(int)”, es decir, 100 veces el tamaño de un entero. En nuestro caso,
no son 100 números, sino el valor de la variable “cuantos”. Por eso hacemos “malloc
(cuantos*sizeof(int))”.

• Para terminar, ese es el espacio que queremos reservar para nuestra variable “datos”.
Y esa variable es de tipo “int *” (un puntero a datos que serán números enteros). Para que todo
vaya bien, debemos “convertir” el resultado de “malloc” al tipo de datos correcto, y lo hacemos
forzando una conversión como vimos en el apartado 2.4 (operador “molde”), con lo que nuestra
orden está completa:

datos = (int *) malloc (cuantos * sizeof(int));

• Si “malloc” nos devuelve NULL como resultado (un “puntero nulo”), quiere decir que no ha
encontrado ninguna posición de memoria en la que nos pudiera reservar todo el espacio que le
habíamos solicitado.

• Para usar “malloc” deberemos incluir “stdlib.h” al principio de nuestro fuente.


La forma de guardar los datos que teclea el usuario también es distinta. Cuando trabajábamos
con un “array”, hacíamos scanf( “ %d “, &datos[i]) (“el dato número i”), pero con punteros
usaremos scanf(“ %d “, datos+i) (en la posición datos + i). Ahora ya no necesitamos el símbolo
“ampersand” (&). Este símbolo se usa para indicarle a C en qué posición de memoria debe
almacenar un dato. Por ejemplo, float x; es una variable que podremos usar para guardar un
número real. Si lo hacemos con la orden “scanf”, esta orden no espera que le digamos en qué
variable deber guardar el dato, sino en qué posición de memoria. Por eso hacemos scanf("%f",
&x); En el caso que nos encontramos ahora, int* datos ya se refiere a una posición de memoria
(un puntero), por lo que no necesitamos & para usar “scanf”.

Finalmente, la forma de acceder a los datos también cambia. Antes leíamos el primer dato
como datos[0], el segundo como datos[1], el tercero como datos[2] y así sucesivamente. Ahora
usaremos el asterisco (*) para indicar que queremos saber el valor que hay almacenado en una
cierta posición: el primer dato será *datos, el segundo *(datos+1), el tercero será *(datos+2) y
así en adelante. Por eso, donde antes hacíamos suma += datos[i]; ahora usamos suma +=
*(datos+i).

También aparece otra orden nueva: free. Hasta ahora, teníamos la memoria reservada
estáticamente, lo que supone que la usábamos (o la desperdiciábamos) durante todo el tiempo
que nuestro programa estuviera funcionando. Pero ahora, igual que reservamos memoria justo
en el momento en que la necesitamos, y justo en la cantidad que necesitamos, también
podemos volver a dejar disponible esa memoria cuando hayamos terminado de usarla. De eso
se encarga la orden “free”, a la que le debemos indicar qué puntero es el que queremos liberar.

El ejemplo anterior era “un caso real”. Generalmente, los casos reales son más aplicables que
los ejemplos puramente académicos, pero también más difíciles de seguir. Por eso, antes de
seguir vamos a ver un ejemplo más sencillo que nos ayude a asentar los conceptos:

Reservaremos espacio para un número real de forma estática, y para dos números reales de
forma dinámica, daremos valor a dos de ellos, guardaremos su suma en el tercer número y
mostraremos en pantalla los resultados.

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

Algoritmo:

inicio
declarar variables
real n1
real *n2, *suma
limpiar pantalla
n1  5.0
n2  (float *) malloc (sizeof(float))
*n2  6.7
suma  (float *) malloc (sizeof(float))
*suma  n1 + *n2
escribir ( ‘ El valor prefijado para la suma era ‘, *suma)
escribir ( ‘ Ahora es tu turno: Introduce el primer número ‘)
leer ( n1 )
escribir ( ‘ Introduce el segundo número ‘ )
leer ( n2 )
*suma  n1 + *n2
escribir ( ‘ Ahora la suma es ‘, *suma)
liberar (n2)
liberar (suma)
pausa
fin

Programa:

#include <stdio.h>
#include <stdlib.h>
main() {
float n1; /* Primer número, estático */
float *n2, *suma; /* Los otros dos números */
n1 = 5.0; /* Damos un valor prefijado a n1 (real)
*/
n2 = (float *) malloc (sizeof(float));
/* Reservamos espacio para n2 */
*n2 = 6.7; /* Valor prefijado para n2 (puntero a
real) */
suma = (float *) malloc (sizeof(float));
/* Reservamos espacio para suma */
*suma = n1 + *n2; /* Calculamos la suma */
printf("El valor prefijado para la suma era %4.2f\n",
*suma);
printf("Ahora es tu turno: Introduce el primer número ");
scanf("%f",&n1); /* Leemos valor para n1 (real) */
printf("Introduce el segundo número ");
scanf("%f",n2); /* Valor para n2 (puntero a real) */
*suma = n1 + *n2; /* Calculamos nuevamente la suma */
printf("Ahora la suma es %4.2f\n", *suma);
free(n2); /* Liberamos la memoria reservada */
free(suma);
}

Las diferencias son:

n1 es un “float”, así que le damos valor normalmente: n1 = 0; Y pedimos su valor con scanf
usando & para indicar en qué dirección de memoria se encuentra: scanf("%f", &n1).

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

n2 (y también “suma”) es un “puntero a float”, así que debemos reservarle espacio con “malloc”
antes de empezar a usarlo, y liberar con “free” el epacio que ocupaba cuando terminemos de
utilizarlo. Para guardar un valor en la dirección de memoria “a la que apunta”, usamos un
asterisco: *n2 = 0; Y pedimos su valor con scanf, pero sin necesidad de usar &, porque el
puntero ES una dirección de memoria: scanf("%f", n2).

En este ejemplo, no hemos comprobado si el resultado de “malloc” era NULL, porque sólo
pedíamos espacio para dos variables, y hemos dado por sentado que sí habría memoria
disponible suficiente para almacenarlas; en un caso general, deberemos asegurarnos siempre
de que se nos ha concedido ese espacio que hemos pedido.

4.3.2. Tipos de Apuntadores

Como se ha visto a lo largo de este manual todas las variables se enlazan a tipos específicos,
con lo cual es de concluirse que una variable apuntador también; por lo cual se enlazan a tipos
de datos específicos del lenguaje (apuntadores a variables de cierto tipo), de forma tal que a un
apuntador sólo se le pueden designar direcciones de variables del mismo tipo, que se
específico en la declaración del apuntador. Este termino es algo complejo de entender por
lo tanto se verá un ejemplo de su manejo:

Algoritmo:

entero *apun1
real *apun2
entero a
apun1  &a
apun2  &a

Programa:

int *apun1;
float *apun2;
int a;
apun1 = &a; // Esto es válido
apun2 = &a; // Esto no es válido (ya que el puntero 2 es de
tipo float, y por lo tanto debe apuntar hacia
una variable del mismo tipo y en este caso no
es así, con lo cual se genera un error)

Cotidianamente, un apuntador inicializado de una manera adecuada apunta a alguna posición


específica de la memoria. Sin embargo, algunas veces es posible que un apuntador no
contenga una dirección válida, en cuyo caso es incorrecto desreferenciarlo (obtener el valor al
que apunta) porque el programa tendrá un comportamiento impredecible y probablemente
erróneo, aunque es posible que funcione bien. Un apuntador puede contener una dirección
inválida debido a dos razones:

1. Cuando un apuntador se declara, al igual que cualquier otra variable, el mismo posee un
valor cualquiera que no se puede conocer con anticipación, hasta que se inicialice con algún
valor (dirección), por ejemplo:

Algoritmo:

real *apun;
escribir ( ‘ El valor apuntado por apun es: ‘ , *apun ) // Incorrecto
*apun  3.5 // Incorrecto

Programa:

float *apun;
1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

printf ( " El valor apuntado por apun es: " , *apun );// Incorrecto
*apun = 3.5; // Incorrecto

2. Después de que un apuntador ha sido inicializado, la dirección que posee puede dejar de ser
válida si se libera la memoria reservada en esa dirección, ya sea porque la variable asociada
termina su ámbito o porque ese espacio de memoria fue reservado dinámicamente y luego se
liberó1, ejemplo de esto es el siguiente:

Algoritmo:

entero *apun
entero b
function func()
entero a  40
apun  &a
b  *apun; // Correcto
*apun  23; // Correcto
Fin_func

inicio
limpiar pantalla
llamar a la función func()
b  *apun // Incorrecto
*apun  25 // Incorrecto
pausa
fin

Programa:

int *apun, b;
void func()
{
int a = 40;
apun = &a;
b = *apun; // Correcto
*apun = 23; // Correcto
}
void main()
{
clrscr();
func();
b = *apun; // Incorrecto
*apun = 25; // Incorrecto
getch();
}

Si se intenta desreferenciar un apuntador que contiene una dirección inválida pueden ocurrir
cosas como las siguientes:

Se obtiene un valor incorrecto en una o más variables debido a que no fue debidamente
inicializada la zona de memoria que se accede a través de la dirección en cuestión. Esto puede
ocasionar que el programa genere resultados incorrectos.

Si casualmente la dirección es la misma de otra variable utilizada en el programa, o está dentro


del rango de direcciones de una zona de memoria utilizada, existe el riesgo de sobreescribir
datos de otras variables. Existe la posibilidad de que la dirección esté fuera de la zona
de memoria utilizada para almacenar datos y más bien esté, por ejemplo, en la zona donde se
almacenan las instrucciones del programa. Al intentar escribir en dicha zona, fácilmente puede

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

ocurrir que el programa genere un error de ejecución y el sistema operativo lo detenga, o que el
programa no responda y deje al sistema operativo inestable.

En muchos casos el sistema operativo detecta el acceso inadecuado a una dirección de


memoria, en cuyo caso detiene abruptamente el programa.

4.3.3. Operaciones con apuntadores

Si declaramos una variable como int n=5 y posteriormente hacemos n++, debería resultar claro
que lo que ocurre es que aumenta en una unidad el valor de la variable n, pasando a ser 6.
Pero ¿qué sucede si hacemos esa misma operación sobre un puntero?

Algoritmo:

Declarer variables
entero *n
n  (int *) malloc (sizeof(int))
*n  3
nn+1

Programa:

int *n;
n = (int *) malloc (sizeof(int));
*n = 3;
n++;

Después de estas líneas de programa, lo que ha ocurrido no es que el contenido de la posición


n sea 4. Eso lo conseguiríamos modificando “*n”, de la misma forma que le hemos dado su
valor inicial. Es decir, deberíamos usar (*n) ++.

En cambio, nosotros hemos aumentado el valor de “n”. Como “n” es un puntero, estamos
modificando una dirección de memoria. Por ejemplo, si “n” se refería a la posición de memoria
número 10.000 de nuestro ordenador, ahora ya no es así, ahora es otra posición de memoria
distinta, por ejemplo la 10.001.
Porque, como ya sabemos, el espacio que ocupa una variable en C depende del sistema
operativo. Así, en un sistema operativo de 32 bits, un “int” ocuparía 4 bytes, de modo que la
operación n++.

Haría que pasáramos de mirar la posición 10.000 a la 10.004. Generalmente no es esto lo que
se quiere, sino modificar el valor que había almacenado en esa posición de memoria. Olvidar
ese * que indica que queremos cambiar el dato y no la posición de memoria puede dar lugar a
fallos muy difíciles de descubrir (o incluso a que el programa se interrumpa con un aviso de
“Violación de segmento” porque estemos accediendo a zonas de memoria que no hemos
reservado.

4.3.4. Relación de apuntadores con arreglos

En C hay muy poca diferencia “interna” entre un puntero y un array. En muchas ocasiones,
podremos declarar un dato como array (una tabla con varios elementos iguales, de tamaño
predefinido) y recorrerlo usando punteros. Vamos a ver un ejemplo:

Algoritmo:

inicio
declarar variables
entero datos [ 10 ]
entero i

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

limpiar pantalla
para ( i = 1; i < 10; i++ ) hacer
datos [ i ]  i * 2
fin_para
para ( i = 1; i < 10; i++ ) hacer
escribir ( datos + i )
fin_para
pausa
fin

Programa:

main()
{
int datos[10];
int i;
/* Damos valores normalmente */
clrscr();
for (i=0; i<10; i++)
datos[i] = i*2;
/* Pero los recorremos usando punteros */
for (i=0; i<10; i++)
printf ("%d ", *(datos+i));
getch();
}

Pero también podremos hacer lo contrario, declarar de forma dinámica una variable usando
“malloc” y recorrerla como si fuera un array:

Algoritmo:

inicio
declarar variables
entero *datos
entero i
limpiar pantalla
datos  ( entero * )
escribir ( ‘ Uso como puntero … ‘ )
para ( i = 0; i < 20; i++ ) hacer
*(datos + i )  i * 2
fin_para
para ( i = 0; i < 20; i++ ) hacer
escribir ( *( datos + i ) )
fin_para
escribir ( ‘ Uso como array … ‘ )
para ( i = 0; i < 20; i++ ) hacer
datos [ i ]  i * 3
fin_para
para ( i = 0; i < 20; i++ ) hacer
escribir ( datos [ i ] )
fin_para
liberar (datos)
pausa
fin

Programa:

#include <stdio.h>
#include <stdlib.h>

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

main()
{
int *datos;
int i;
/* Reservamos espacio */
datos = (int *) malloc (20*sizeof(int));
/* Damos valores como puntero */
clrscr();
printf(" Uso como puntero... ");
for (i=0; i<20; i++)
*(datos+i) = i*2;
/* Y los mostramos */
for (i=0; i<10; i++)
printf ("%d ", *(datos+i));
/* Ahora damos valores como array */
printf("\n Uso como array... ");
for (i=0; i<20; i++)
datos[i] = i*3;
/* Y los mostramos */
for (i=0; i<10; i++)
printf ("%d ", datos[i]);
/* Liberamos el espacio */
free(datos);
getch();
}

Existe otra posibilidad de usar punteros y arreglos, que son los arreglos de punteros,

Igual que creamos “arrays” para guardar varios datos que sean números enteros o reales,
podemos hacerlo con punteros, esto es podemos reservar espacio para “20 punteros a
enteros” haciendo:

int *datos[20];

Tampoco es algo especialmente frecuente en un caso general, porque si fijamos la cantidad de


datos, estamos perdiendo parte de la versatilidad que podríamos tener al usar memoria
dinámica. Pero sí es habitual cuando se declaran varias cadenas:

char *mensajesError[3]={"Fichero no encontrado", "No se puede escribir", "Fichero sin datos"};

Un ejemplo de su uso sería este:

Algoritmo:

inicio
declarar variables
carácter *mensajesError[3]  {"Fichero no encontrado",
"No se puede escribir", "Fichero sin datos"}
limpiar pantalla
escribir ( ‘ El primer mensaje de error es: ‘, mensajesError[0])
escribir ( ‘ El segundo mensaje de error es: ‘, mensajesError[1])
escribir ( ‘ El tercer mensaje de error es: ‘, mensajesError[2])
pausa
fin

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN
UNIDAD IV ESTRUCUTRAS DE DATOS

Programa:

#include <stdio.h>
main() {
char *mensajesError[3]={"Fichero no encontrado","No se puede
escribir","Fichero sin datos"};
clrscr();
printf("El primer mensaje de error es: %s\n",mensajesError[0]);
printf("El segundo mensaje de error es: %s\n",mensajesError[1]);
printf("El tercer mensaje de error es: %s\n",mensajesError[2]);
getch();
}

1
Autores: Alejandra Gutierrez Reyes - Enrique Martínez Roldán - Academia de Computación – Ingenieria Eléctrica
ESIME Zacatenco IPN

También podría gustarte