Está en la página 1de 21

Instituto Politécnico Nacional

Escuela Superior de Ingeniería

Mecánica y Eléctrica

Unidad Culhuacán

Carrera de Ingeniería en Computación

1CM45

Fundamentos de Programación

Unidad IV Estructura de datos

Profesora: Salaz Jiménez Verónica

Alumno: Castellanos Ayala Vanessa Fernanda

Semestre 2023-2
ESTRUCTURAS DE DATOS

4.1 ARREGLOS UNIDIMENCIONALES


Los arreglos son una colección de variables del mismo tipo que se referencian
utilizando un nombre común. Un arreglo consta de posiciones de memoria contigua.
La dirección más baja corresponde al primer elemento y la más alta al último. Un
arreglo puede tener una o varias dimensiones. Para acceder a un elemento en
particular de un arreglo se usa un índice.

El formato para declarar un arreglo unidimensional es:

tipo nombre_arr [ tamaño ]

Por ejemplo, para declarar un arreglo de enteros llamado a con diez elementos se
hace de la siguiente forma:

int a[10];

En C, todos los arreglos usan cero como índice para el primer elemento. Por tanto,
el ejemplo anterior declara un arreglo de enteros con diez elementos desde a[0]
hasta a[9].

La forma como pueden ser accesados los elementos de un arreglo, es de la


siguiente forma:

a[2] = 15; /* Asigna 15 al 3er elemento del arreglo a*/

num = a[2]; /* Asigna el contenido del 3er elemento a la variable num */

El lenguaje C no realiza comprobación de contornos en los arreglos. En el caso de


que sobrepase el final durante una operación de asignación, entonces se asignarán
valores a otra variable o a un trozo del código, esto es, si se dimensiona un arreglo
de tamaño N, se puede referenciar el arreglo por encima de N sin provocar ningún
mensaje de error en tiempo de compilación o ejecución, incluso aunque
probablemente se provoque un error en el programa.
Como programador se es responsable de asegurar que todos los arreglos sean lo
suficientemente grandes para guardar lo que pondrá en ellos el programa, C permite
arreglos con más de una dimensión, el formato general es:

tipo nombre_arr [ tam1 ][ tam2 ] ... [ tamN];

Por ejemplo un arreglo de enteros bidimensionales se escribirá como:

int b[50][50];

Observar que para declarar cada dimensión lleva sus propios paréntesis cuadrados.
Para acceder los elementos se procede de forma similar al ejemplo del arreglo
unidimensional, esto es:

b[2][3] = 15; /* Asigna 15 al elemento de la 3ª fila y la 4ª columna*/ num =


b[25][16];

En C se permite la inicialización de arreglos, debiendo seguir el siguiente formato:


tipo nombre_arr[ tam1 ][ tam2 ] ... [ tamN] = {lista-valores};

Por ejemplo: int c[10] =

{1,2,3,4,5,6,7,8,9,10}; int num[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};

El arreglo unidimensional posee un nombre o identificador, al igual que un vector y


también un subíndice que indica la posición a la cual queremos acceder, de todas
las que componen el vector. Solo existen algunas diferencias en cuanto a la
nomenclatura. Otra asociación matemática, haciendo uso del concepto de conjunto,
es la siguiente: podemos pensar un arreglo como un conjunto de elementos que
tienen una propiedad en común en este caso es el tipo de dato y el nombre del
conjunto y podemos acceder a estos elementos a través de un nombre y un
subíndice es más nosotros podremos inicializar un arreglo asignándoles los datos
con la misma nomenclatura que usamos en conjuntos, solo que en este caso se
debe tener en cuenta el orden.
4.1.2 Arreglos numéricos y caracteres:

Los arreglos son estructuras de datos consistentes en un conjunto de datos del


mismo tipo. Los arreglos tienen un tamaño que es la cantidad de objetos del mismo
tipo que pueden almacenar. Los arreglos son entidades estáticas debido a que se
declaran de un cierto tamaño y conservan ´este todo a lo largo de la ejecución del
Para declarar o crear un arreglo utilizaremos la siguiente forma:

Escribe el tipo de dato que almacenará el arreglo

Escribe el nombre del arreglo

Entre corchetes, escribe la cantidad de elementos de ese tipo que se almacenarán

Por ejemplo:

//Crear un arreglo

float grupo[10];

int i;

Los arreglos nos sirven para almacenar datos. Si deseamos guardar datos en un
arreglo podemos escribir la ubicación exacta donde deseamos almacenarlos.

En C++, a diferencia de algunos otros lenguajes de programación, los vectores y las


matrices presentan un "inconveniente" con el tamaño. Es decir, no es posible crear
de una manera sencilla un vector capaz de almacenar una cantidad de información
indefinida, es necesario ingresar con antelación la cantidad de datos (tamaño) que
el vector o la matriz tendrá.

En C++ a la hora de inicializar un array, arreglo o Vector, estamos acostumbrados


a que si inicializamos inmediatamente después de declarar el vector, debemos
poner la misma cantidad de elementos al interior de las llaves de manera que
corresponda con el tamaño del vector, pues bien, estos es lo más recomendable,
sin embargo si ponemos una cantidad de elementos menor a la del tamaño real del
vector, estamos queriendo decir que estos elementos toman los valores puestos
entre las llaves y los demás serian cero, para el caso del código anterior el primer
elemento (el del índice cero) va a tener un valor de 10.5 y los otros 4 elementos van
a valer cero.

char Nombre[Numero]= “Datos a guardar”; //Declarando arreglo tipo Char


(caracteres)
int Nombre[Numero]= “Datos a guardar”; //Declarando arreglo tipo int
(numérico)

Donde:
-Nombre: Es el nombre de la variable de nuestro arreglo
-Numero: Es un valor numérico que indicará el tamaño de nuestro arreglo (por
ejemplo 5,10 o 20)
-Datos a guardar: Es el dato que queremos que sea guardado en nuestro arreglo.

En un arreglo, cada carácter que forma el dato que queremos almacenar es


guardado en una casilla por separado, por ejemplo, si queremos guardar la palabra
“Venezuela” en una variable llamada “País”, se guardaría de la siguiente manera:

Para definir cuál será la cantidad de caracteres que queremos almacenar


debemos pensar un numero razonable y restarle uno, es decir, si queremos que
nuestro tenga 20 caracteres, el numero indicado será 19, esto es debido a que en
Lenguaje C (y en la mayoría de lenguajes) los números se empiezan a contar en 0,
es decir, la casilla 0 también tiene un valor, por lo tanto la cadena seria del 0-19 =
20 valores. El carácter final de nuestra cadena siempre será “\0”, esto indica el final
de nuestra cadena y también ocupa un espacio de memoria, por lo que debe
tomarse en cuenta a la hora de programar la capacidad de memoria que tendrá
nuestra cadena.
Características de un arreglo: Un arreglo se caracteriza por los siguientes puntos:
-Almacena cada valor en memoria contigua
-Se accede a cada elemento del arreglo mediante el subíndice. Por ejemplo, si
queremos acceder a la casilla 4 seria: Arreglo[4].
-Un arreglo tiene un solo nombre y si queremos acceder a todo el arreglo será
mediante el nombre del arreglo, si solo queremos acceder a un valor seria mediante
el subíndice del arreglo.
-Tiene un número finito n de datos que son del mismo tipo.

Cadena de caracteres: Una cadena de caracteres es un tipo de dato que es


compuesto por un conjunto de caracteres como símbolos, letras, números, etc. Es
exactamente lo mismo que un arreglo unidimensional, sin embargo, se maneja y
tiene propósitos distintos. Mientras que un arreglo está hecho para examinar el valor
de cada casilla, una cadena de caracteres está hecha para manejarse como un solo
valor completo. Por ejemplo, una cadena de caracteres podría ser utilizado para
guardar la calle de una dirección, en ese caso solo queremos saber el valor de la
calle, y no analizar cada elemento que compone la cadena como lo haríamos en un
arreglo.
En C no existe un tipo predefinido para manipular cadenas de caracteres (string).
Sin embargo, el estándar de C define algunas funciones de biblioteca para
tratamiento de cadenas. Una cadena en C es un array de caracteres de una
dimensión (vector de caracteres) que termina con el carácter especial ‘\0’ (cero).
El formato para declarar una cadena es:
char nombre[n];
Dónde: n >= 1 y representa a la longitud-1 real de la cadena.
Un ejemplo de declaración de cadena:
char cadena [5];
Debido a que en la representación interna de una cadena de caracteres es
terminada por el símbolo '\0', para un texto de "n" caracteres, debemos reservar
"n+1”. El carácter '\0', Aunque pertenece a la cadena, no aparece al utilizar
funciones como printf.
En el caso especial de los arrays de caracteres, podemos utilizar varias formas de
inicialización:
char cadena[] = "Hola";
char cadena[] = {'H','o','l','a',0};
char cadena[] = {'H','o','l','a','\0'};
sin especificar el tamaño de la cadena, o especificando el tamaño:
3
char cadena[5] = "Hola";
char cadena[5] = {'H','o','l','a',0};
char cadena[5] = {'H','o','l','a','\0'};
Para declarar una cadena de caracteres es igual que un arreglo unidimensional:
char Nombre[Numero]= “Datos a guardar”; //Declarando arreglo tipo Char
(caracteres)

4.2 ARREGLOS BIDIMENSIONALES

Es una colección finita, homogénea y ordenada de datos, en la que se hace


referencia a cada elemento del arreglo por medio de dos índices. El primero se
utiliza para indicar la fila, y el segundo para indicar la columna.

Un array en C puede tener una, dos o más dimensiones. Por ejemplo, un array de
dos dimensiones también denominado matriz, es interpretado como un array
(unidimensional) de dimensión "f" (número de filas), donde cada componente es un
array (unidimensional) de dimensión "c" (número de columnas). Un array de dos
dimensiones, contiene, pues, "f*c" componentes.
El formato para declarar un array multidimensionales:

int nombre[f][c]…;

dónde: f,c...>= 1;

Para acceder a un elemento del array multidimensional:

nombre[i][j];

donde: 0 <= i < f ; 0 <= j < c;

Durante la declaración de un array multidimensional también podemos inicializar


sus componentes indicando la lista de los valores entre llaves. En el interior de la
lista, los componentes de cada línea del array son encerrados nuevamente entre
llaves. Para hacer más cara la visibilidad de los elementos del array, podemos
indicarlos en varias líneas.

int A[3][4] ={{ 0,1,2,3},

{ 1,2,3,4},

{ 2,3,4,5}};

Sin embargo, es mucho más conveniente anidar dos ciclos para inicializar un array
de dos dimensiones:

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

for (j = 0; j< 4; j++)

A[i][j] = i+j;}

Un arreglo bidimensional se define así:

int arreglo[10][10];

float matriz[10][10];
También podemos utilizar constantes para definir la dimensión del arreglo de dos
dimensiones:

const int N = 10;

int arreglo[N][N];

Inicialización

Una matriz o arreglo bidimensional se puede inicializar de este modo:

int matriz[3][3] = {{1,2,3},{4,5,6},{7,8,9}};

Con la anterior asignación se crea en memoria una matriz igual a la de abajo

0 1 2

0 1 2 3

1 4 5 6

2 7 8 9

En un arreglo de dos dimensiones necesitamos también dos índices para acceder


a sus elementos. Si utilizamos: matriz[i][j], entonces i se refiere a la fila y j a la
columna, hacemos:

int nro = matriz[1][1];

En la variable nro se guardara el número 5.

Las matrices o arreglos bidimensionales se suelen utilizar en cálculos matemáticos,


operaciones con matrices, recorridos por matrices, y cualquier uso que nosotros le
podamos dar.
Se pueden definir arreglos de más de 2 dimensiones, pero su manejo se dificultaría
enormemente.

4.3 Apuntadores:

Llamados también punteros. 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. Cada variable que se utiliza en una aplicación ocupa una o varias posiciones
de memoria. Estas posiciones de memoria se accedan por medio de una dirección.

Los apuntadores en C y C++ son una herramienta muy potente de programación


que suele causar mucha confusión en los estudiantes que la están aprendiendo.
Además, cuando los programadores cometen un error en su utilización, puede ser
muy difícil encontrar el error, por lo cual es importante saber utilizarlos muy bien. El
uso de apuntadores en C y C++ es muy importante debido a que permite hacer los
programas más eficientes y más flexibles.

Cuando se declara una variable, el compilador reserva un espacio de memoria para


ella y asocia el nombre de ´está a la dirección de memoria desde donde comienzan
los datos de esa variable. Las direcciones de memoria se suelen describir como
números en hexadecimal. Un apuntador es una variable cuyo valor es la dirección
de memoria de otra variable. Se dice que un apuntador “apunta” a la variable cuyo
valor se almacena a partir de la dirección de memoria que contiene el apuntador.
Por ejemplo, si un apuntador p almacena la dirección de una variable x, se dice que
“p apunta a x”.

La referenciación es la obtención de la dirección de una variable. En C y C++ esto


se hace a través del operador ‘&’, aplicado a la variable a la cual se desea saber su
dirección. Nótese que se trata de un operador unario. Ejemplo:

int x = 25; cout << "La direcci´on de x es: " << &x << endl;

Para declarar un apuntador se especifica el tipo de dato al que apunta, el operador


‘*’, y el nombre del apuntador. La sintaxis es la siguiente:
int *ptr1; // Apuntador a un dato de tipo entero (int) char *cad1, *cad2; // Dos
apuntadores a datos de tipo car´acter (char) float *ptr2; // Apuntador a un dato
de tipo punto-flotante (float)

Indireccion de apuntadores:

El indireccionar un apuntador conlleva obtener el valor al que está apuntando, esto


se logra utilizando el operador de indireccion * [1].

int edad;

int ∗ p edad;

p edad = &edad

p edad = 50;

Si se desea imprimir el valor de edad, se puede hacer de dos formas distintas, una
imprimiendo directamente el valor de edad y la otra des referenciando al apuntador
edad como se muestra a continuación, respectivamente según el caso.

printf(“%d”, edad);

printf(“%d”, ∗p edad);

4.3.2 TIPOS DE APUNTADORES:

Direcciones invalidas y la dirección NULL

Normalmente, un apuntador inicializado adecuadamente apunta a alguna posición


específica de la memoria. Sin embargo, algunas veces es posible que un apuntador
no contenga una dirección valida, en cuyo caso es incorrecto des referenciarlo
(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:
-Cuando un apuntador se declara, al igual que cualquier otra variable, el mismo
posee un valor cualquiera que no se puede conocer con antelación, hasta que se
inicialice con algún valor (dirección). Ejemplo:

C´odigo C y C++

float *p;

cout << "El valor apuntado por p es: " << *p << endl; // Incorrecto

*p = 3.5; // Incorrecto

-Despues 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´o1

. Ejemplo:

Código C y C++

int *p, y;

void func()

int x = 40;

p = &x;

y = *p; // Correcto

*p = 23; // Correcto

void main()

func();
y = *p; // Incorrecto

*p = 25; // Incorrecto

Si se intenta des referenciar 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 sobrescribir datos de otras variables. Existe la posibilidad de que la
dirección este fuera de la zona de memoria utilizada para almacenar datos y más
bien este, por ejemplo, en la zona donde se almacenan las instrucciones del
programa. Al intentar escribir en dicha zona, fácilmente puede 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.

Cuando no se desea que un apuntador apunte a algo, se le suele asignar el valor


NULL, en cuyo caso se dice que el apuntador es nulo (no apunta a nada). NULL es
una macro típicamente definida en archivos de cabecera como stdef.h y stdlib.h.
Normalmente, en C++ se encuentra disponible sin incluir ningún archivo de
cabecera. NULL se suele definir en estas librerías así:

C´odigo C y C++

#define NULL 0

Un apuntador nulo se utiliza para proporcionar a un programa un medio de conocer


cuando un apuntador contiene una dirección valida. Se suele utilizar un test
condicional para saber si un apuntador es nulo o no lo es, y tomar las medidas
necesarias. El valor NULL es muy ´útil para la construcción de estructuras de datos
dinámicas, como las listas enlazadas, matrices esparcidas, etc. Es igualmente
incorrecto des referenciar el valor NULL por las mismas razones presentadas
previamente.

Apuntadores a apuntadores:

Dado que un apuntador es una variable que apunta a otra, fácilmente se puede
deducir que pueden existir apuntadores a apuntadores, y a su vez los segundos
pueden apuntar a apuntadores, y así sucesivamente. Estos apuntadores se
declaran colocando tantos asteriscos (‘*’) como sea necesario. Ejemplo:

char c = ’z’;

char *pc = &c;

char **ppc = &pc;

char ***pppc = &ppc;

***pppc = ’m’; // Cambia el valor de c a ’m’

-Apuntadores constantes y apuntadores a constantes:

Es posible declarar apuntadores constantes. De esta manera, no se permite la


modificación de la dirección almacenada en el apuntador, pero si se permite la
modificación del valor al que apunta.

int x = 5, y = 7;

int *const p = &x; // Declaración e inicialización del apuntador constante

*p = 3; // Esto es valido

p = &y; // Esto no es válido (el compilador genera un error)

También es posible declarar apuntadores a datos constantes. Esto hace que no sea
posible modificar el valor al que apunta el apuntador. Ejemplo:

int x = 5, y = 7;
const int *p = &x; // Declaración e inicialización del apuntador a constante

p = &y; // Esto es valido

*p = 3; // Esto no es valido (el compilador genera un error)

y = 3; // Esto es valido

-Apuntadores, arreglos y aritmética de apuntadores

Los arreglos y apuntadores están fuertemente relacionados. El nombre de un


arreglo es simplemente un apuntador constante al inicio del arreglo. Se pueden
direccionar arreglos como si fueran apuntadores y apuntadores como si fueran
arreglos. Ejemplos:

int lista_arr[5] = {10, 20, 30, 40, 50};

int *lista_ptr;

lista_ptr = lista_arr; // A partir de aqu´ı ambas variables apuntan al mismo sitio

cout << lista_arr[0]; // Imprime 10

cout << lista_ptr[0]; // Instrucci´on equivalente a la anterior

cout << *lista_arr; // Instrucci´on equivalente a la anterior

cout << *lista_ptr; // Instrucci´on equivalente a la anterior

cout << lista_arr[3]; // Imprime 40

cout << lista_ptr[3]; // Instrucci´on equivalente a la anterior

Es posible sumar y restar valores enteros a un apuntador. El resultado de estas


operaciones es el desplazamiento de la dirección de memoria hacia adelante (suma)
o hacia atrás (resta) por bloques de bytes del tamaño del tipo de dato apuntado por
el apuntador. Esto permite recorrer arreglos utilizando apuntadores.

Ejemplos:

int lista[5] = {10, 20, 30, 40, 50};


int *p;

char cad[15];

char *q;

p = &lista[3]; // ’p’ almacena la direcci´on de la posici´on 3 del arreglo

p = lista + 3; // Instrucci´on equivalente a la anterior

cout << lista[2]; // Imprime 30;

cout << *(lista+2); // Instrucci´on equivalente a la anterior

// Las siguientes instrucciones imprimen la palabra "Programando" /* Nota:


Recuérdese que una constante de cadena de caracteres es una secuencia de
caracteres en memoria seguidos del carácter nulo */

strcpy(cad, "Programando");

for (q = cad; *q != ’\0’; q++)

cout << q;

También es posible restar dos apuntadores. El resultado de esta operación es el


número de bloques de bytes que hay entre las dos direcciones del tamaño del tipo
de dato apuntado por los apuntadores. Ejemplo:

double x[5] = {1.1, 2.1, 3.1, 4.1, 5.1};

double *p = &x[1],

*q = &x[4];

int n;

n = q - p; // a ’n’ se le asigna 3

-Apuntadores para paso de parámetros por referencia

El lenguaje C no provee una manera de pasar parámetros por referencia. Sin


embargo, es posible hacerlo a través del uso de apuntadores. A continuación se
muestra un ejemplo del paso de un parámetro por referencia en C++, y luego un
código equivalente en C o C++ utilizando un apuntador:

void suma(int a, int b, int& r)

r = a + b;

void main()

int x;

suma(7, 5, x);

cout << "7 + 5 = " << x;

C´odigo C y C++

void suma(int a, int b, int *r)

*r = a + b;

void main()

int x;

suma(7, 5, &x);

cout << "7 + 5 = " << x;


}

Nótese que en ambos casos se utiliza el operador ‘&’ para cosas distintas. El
operador ‘&’ tiene dos significados como operador unario: señalación de parámetro
por referencia y operador de referenciación.

Operaciones básicas con punteros

Dirección

Operador &

&<id> devuelve la dirección de memoria donde comienza la variable <id>. El


operador & se utiliza para asignar valores a datos de tipo puntero:

int i;

int *ptr;

...

ptr = &i;

Indirección

Operador *

*<ptr> devuelve el contenido del objeto referenciado por el puntero <ptr>.El


operador * se usa para acceder a los objetos a los que apunta un puntero:

char c;

char *ptr;

ptr = &c;

*ptr = ‘A’; // Equivale a escribir: c = ‘A’

Asignación

Operador =
A un puntero se le puede asignar una dirección de memoria concreta, la dirección
de una variable o el contenido de otro puntero.

Una dirección de memoria concreta:

int *ptr;

...

ptr = 0x1F3CE00A;

...

ptr = NULL;

La dirección de una variable del tipo al que apunta el puntero:

char c;

char *ptr;

...

ptr = &c;

Otro puntero del mismo tipo:

char c;

char *ptr1;

char *ptr2;

ptr1 = &c;

ptr2 = ptr1;

Como todas las variables, los punteros también contienen “basura” cuando se
declaran, por lo que es una buena costumbre inicializarlos con NULL.

Ejemplo:
int main ()

int y = 5;

int z = 3;

int *nptr;

int *mptr;

nptr = &y;

z = *nptr;

*nptr = 7;

mptr = nptr;

mptr = *z;

*mptr = *nptr;

y = (*nptr) + 1;

return 0;

}
Bibliografía
(1997), L. Y. (s.f.). “Estructura de Datos con C y C++”. Segunda Edición. Prentice
Hall.

(1999)., J. L. (s.f.). “Estructura de Datos,Libro de Problemas”. En M. Hill.

Bosco, U. d. (s.f.). Arreglos bidimensionales (matrices). En G. d. 9.

Deite, C. p. (s.f.). Programación en C Byron S. Gottfried - McGraw-Hill. Prentice


Hall Hispanoamericana.

Morales, J. I. (Toluca, Méx. Agosto 2015). Tema 1.2 Arreglos: Vectores, matrices,.
UNIVERSIDAD AUTÓNOMA DEL ESTADO DE MÉXICO.

T., P. M. (s.f.). Apuntadores en C y C++. Universidad de Carabobo Facultad


Experimental de Ciencias y Tecnología.

También podría gustarte