Lenguajes de Programaci´n o Ayudant´ 1: Recordando el pasado ıa Versi´n 1 - Semestre 1 o

Camilo Zambrano Lagos <cizambra@alumnos.inf.utfsm.cl> 24 de abril de 2012

1.

Introducci´n o

´ Esta ayudant´ consiste en b´sicamente recordar en forma superficial el uso de ciertos recursos utilizaıa a dos en asignaturas anteriores, como por ejemplo, las bases del lenguaje de programaci´n C, el uso te´rico o o de listas y su implementaci´n en los lenguajes de programaci´n. El objetivo es que el estudiante logre o o incorporar todo lo ense˜ado de forma ´ n ıntegra y permanente a su caja de herramientas de programaci´n, o para enfrentar cualquier problema que requiera el uso de ´sto. En ´ste texto se asumir´ que el estudiante e e a posee conocimientos acerca de los t´rminos t´cnicos que se emplear´n de aqu´ en adelante (son t´rminos e e a ı e ya vistos en otros ramos).

2.

El lenguaje de programaci´n C o

El lenguaje de programaci´n C, es un lenguaje creado el a˜o 1972 por Dennis M. Ritchie en Bell o n Labs., orientado a la implementaci´n de Sistemas Operativos. La filosof´ de ´ste lenguaje consiste en o ıa e buscar la simplicidad en la traducci´n de cada elemento del lenguaje al lenguaje m´quina, de modo de o a disminuir el tiempo de ejecuci´n en las tareas a realizar. Inicialmente, C se desarroll´ por programadores o o para programadores, sin embargo en la actualidad se ha convertido en uno de los lenguajes m´s populares, a volvi´ndose herramienta en contextos muy distintos a los originales. e

2.1.

Caracter´ ısticas
Es un lenguaje compilado. Es un lenguaje de nivel medio-bajo. Es case-sensitive (sensible a may´sculas). u

2.2.
2.2.1.

Componentes
Variables y constantes

Las variables y constantes corresponden a una forma m´s “entendible” de representar de una dia recci´n de memoria en donde se guardar´ un dato o conjunto de datos de un tipo determinado. Toda o a representaci´n (sea variable o constante) posee una direcci´n de memoria, un valor y un ´mbito de acceso o o a del programa donde es v´lido. La diferencia la marca la posibilidad de cambio de valor de la reprea sentaci´n, esto significa, una variable puede cambiar su valor durante la ejecuci´n de la aplicaci´n, al o o o contrario de una constante en donde su valor no cambia durante la ejecuci´n de la misma. Se tienen dos o tipos de variables: Globales: Son declaradas despu´s de las cabeceras pero antes de cualquier funci´n y su ´mbito de e o a acceso corresponde al programa completo. 1

Locales: Son declaradas dentro de una funci´n, por lo que su ´mbito se remite s´lo a nivel interno o a o de la funci´n (local). o La sintaxis para la declaraci´n de variables puede ser de dos formas: o [<modificador>] <tipo_dato> <identificador>; , o bien <tipo_dato> [<modificador>] <identificador>; , donde el tipo de dato puede ser predefinido o propio del lenguaje C, combinado a partir de los definidos, o bien, ser definido por el programador; el modificador corresponde a un elemento que puede indicar el a ´mbito (por defecto es local si se encuentra dentro de una funci´n y global si se encuentra fuera), o bien o indicar si es constante o variable (por defecto es variable), y los identificadores son b´sicamente nombres a que deben cumplir con la norma de identificadores para C. Para asignar variables se usa la siguiente sintaxis: <identificador> = <valor>; Mientras una variable no se inicialice, su valor ser´ indeterminado (¡cuidado con ´sto!). Una variable a e puede ser inicializada durante su declaraci´n: o [<modificador>] <tipo> <identificador> = <valor>; Por su parte, las constantes pueden definirse de dos formas: Mediante la sentencia de preprocesador #define. Declarando una variable con el modificador const, en donde es obligaci´n realizar al mismo tiempo o la asignaci´n. o 2.2.2. Expresiones y operadores

Los operadores son elementos que indican una operaci´n a realizar para evaluar un valor. Los o operandos son variables y constantes. Una expresi´n corresponde a la combinaci´n entre operadores o o y operandos. Ejemplos de expresiones: operador1 + operador2 operador2 = operador1 Se asume en ´ste texto que el estudiante ha visto los operadores con los que C cuenta, a lo largo de sus e a˜os de estudio en el ´rea de programaci´n. n a o 2.2.3. Punteros

B´sicamente un puntero, es un tipo de dato que representa una direcci´n de memoria en la cual se a o almacena un dato o bien se aloja una funci´n. Para declarar punteros se hace uso de la siguiente sintaxis: o <tipo_dato> * <identificador>; Si <tipo dato> corresponde a void, el puntero puede apuntar a una funci´n (se ver´ m´s adelante en o a a ´ste mismo texto) o bien a cualquier tipo de dato. Para acceder a una direcci´n de memoria se debe e o utilizar el operador &, as´ por ejemplo se puede tener la siguiente declaraci´n y asignaci´n de un puntero: ı, o o data_t value; data_t * value_p; value_p = &value; Para acceder al contenido de un puntero se debe utilizar el operador *, as´ por ejemplo se puede tener ı, la siguiente declaraci´n y asignaci´n de un puntero: o o 2

data_t value; data_t value2; data_t * value_p; value_p = &value; value2 = (*value_p)+2; En uno o m´s punteros, pueden realizarse operaciones aritm´ticas (seg´n el tipo apuntado), de comparaa e u ci´n (comparan direcciones, no contenido) o de asignaci´n (copia la direcci´n, no los contenidos, seg´n o o o u eso podr´ decirse que si se modifica el valor en un puntero, entonces el valor contenido en el puntero ıa reci´n asignado tambi´n cambia ya que la direcci´n de referencia es la misma). e e o Los punteros son elementos en los que hay que tener sumo cuidado al momento de programar, ya que es necesario tener ciertas precauciones al momento de trabajar con ellos, por asuntos de acceso a la memoria. Es importante el tener cuidado de no acceder a zonas de memoria no declaradas por el programa, generalmente ´sto se traduce en errores de violaci´n de segmento y son errores que no se detectan al e o momento de compilar, s´lo al momento de ejecutar. Por lo anterior, es muy importante que se entienda o la diferencia entre los operadores * y & para el caso de los punteros. 2.2.4. Control de Flujo

En el caso de ´ste tipo de sentencias, existe una subclasificaci´n: e o Sentencias condicionales: Son aquellas que ejecutan una vez, una o m´ltiples l´ u ıneas de c´digo, o sujetas a una condici´n. Dentro de ´sta clasificaci´n, se encuentran las sentencias if y switch. o e o Ejemplo de sentencia if: // If condition is equal to expr. if (condition == expr) { // Do something. } else { // Do another thing. } Ejemplo de sentencia switch: switch (condition) { // If condition is equal to cons1. case cons1: // Do something. break; // If condition is equal to cons2. case cons2: // Do another thing. break; // Otherwise. default: // Do default thing. } Sentencias repetitivas: Son aquellas que ejecutan una o m´ltiples veces, una o m´ltiples l´ u u ıneas de c´digo, sujetas a una condici´n. Dentro de ´sta clasificaci´n se encuentran las sentencias while, o o e o do-while, for. Ejemplo de sentencia while: // While cond is different from expr then do something. while (cond != expr) { // Do something. } 3

Ejemplo de sentencia do-while: // Execute at least once, and repeat if cond is different from expr. do { // Do something. } while (cond != expr); Ejemplo de sentencia for: // Count the loops, store them on i, while cond is different to expr. for (int i = 0 ; cond != expr ; i++ ) { // Do something. } 2.2.5. Funciones

Las funciones son bloques de sentencias que realizan una tarea m´s o menos concreta, ´stos bloques a e pueden ser llamado una o m´s veces y pueden retornar o no, un resultado. Al igual que las funciones a matem´ticas, ´stas funciones pueden hacer uso de par´metros o argumentos. a e a Conociendo ya el concepto de punteros, puede mencionarse que los valores originales que una funci´n o recibe como par´metros pueden ser modificados en tiempo de ejecuci´n; a ´ste tipo de paso de variables, a o e se le conoce como paso por referencia. Hay otras ocasiones, en donde el valor original se mantiene luego de ejecutarse la funci´n, y lo que se hace internamente es copiar el valor del par´metro en una variable o a local, que es la que se usa para realizar las operaciones necesarias; a ´ste tipo de paso de variables, se le e conoce como paso por valor. Es importante comentar, que dentro de un mismo archivo fuente no pueden declararse dos funciones con el mismo identificador (polimorfismo), la unica excepci´n es la funci´n main. ´ o o Dentro de un archivo fuente, se realizan tres acciones distintas relacionadas con funciones: declaraci´n, o definici´n y llamada. La declaraci´n debe hacerse antes de la funci´n main, tanto en forma de firmas (o o o o prototipos), como en forma de archivos de cabecera. La sintaxis para la declaraci´n es la siguiente: o <tipo> <identificador> ([<par´metros>]); a Donde <tipo> corresponde a void si es que no hay valor de retorno, y [<par´metros>] corresponde a a una lista de tipos (que pueden ser acompa˜ados por identificadores, para una mayor comprensi´n), n o separados por comas (,), la lista es opcional, y puede no ser especificada, o bien especificada como void. La definici´n corresponde a la implementaci´n del c´digo de la funci´n, y debe hacerse posterior a la o o o o funci´n main; la sintaxis para la definici´n de funciones es la siguiente: o o <tipo> <identificador> ([<par´metros>]) { a [<declaraci´n de variables locales>] o [<sentencias>] [return <expresion>;] } Donde, a diferencia de la declaraci´n, [<par´metros>] debe llevar siempre un identificador por cada o a tipo en la lista. Es muy importante mencionar, que las definiciones pueden encontrarse dentro del mismo archivo despu´s de main, o bien en archivos *.c aparte, o bien en archivos ya compilados. e Para la llamada a una funci´n se utiliza la siguiente sintaxis: o ...<identificador> ([<par´metros>])... a Donde [<par´metros>] corresponde a los valores reales enviados al momento de la llamada, separados a por comas(,), ´stos valores reales pueden ser variables (o constantes) o bien expresiones. Una funci´n e o puede ser llamada desde main, otra funci´n, o por s´ misma, a ´ste ultimo caso se le llama recursividad. o ı e ´ 4

2.2.6.

Arreglos

Los arreglos o arrays son un tipo de dato que representa un conjunto de datos de igual tipo, ´ almacenados en posiciones contiguas de memoria. Estos arreglos, pueden tener una o varias dimensiones; decimos que un arreglo es unidimensional, cuando es semejante a un vector, bidimensional, cuando es semejante a matrices de dos dimensiones, y n-dimensional o multidimensional cuando es semejante a matrices de n dimensiones. El tama˜o en bytes de un array, var´ seg´n el n´mero m´ximo de elementos y n ıa u u a seg´n el tama˜o del tipo de dato que posea cada elemento del arreglo. A continuaci´n se profundizar´ un u n o a poco m´s en los arreglos unidimensionales y bidimensionales: a Unidimensionales: Para declarar un arreglo, se utilizan diversas formas: // Sin inicializaci´n o <tipo> <identificador> [tama~o]; n , o bien // Con inicializaci´n, donde el tama~o es opcional o n <tipo> <identificador> [] = {val1, val2, ...}; Para acceder a los elementos de un array, se ocupa la siguiente sintaxis: <identificador> [<indice>] , donde [<indice>] corresponde a un valor entre 0 y <tama~o>-1. n Los elementos de un array, puede ser referenciados con notaci´n de puntero, usando la siguiente o sintaxis: array+N ≡ &array[N] Seg´n lo anterior, el nombre del array representa la direcci´n de memoria en la que se encuentra el u o primer elemento de ´ste: e array ≡ &array[0] La caracter´ ıstica reci´n mencionada, es conocida como dualidad array-puntero. e Un arreglo puede ser pasado como par´metro por referencia, al pasar el nombre del arreglo en a la funci´n, se pasa la direcci´n de todo ´ste, en donde los valores del array pueden ser modificao o e dos dentro de la funci´n.Existen dos sintaxis para la declaraci´n de un array unidimensional como o o par´metro por referencia: a // Notaci´n array o <tipo_funcion> <identificador_funcion> (...,<tipo> [],...); // Notaci´n punteros o <tipo_funcion> <identificador_funcion> (...,<tipo> *,...); ¡Cuidado!: No intentes retornar un arreglo como resultado de una funci´n, si es que el arreglo ha o sido declarado en forma local, ´sto debido a que al terminar la ejecuci´n de la funci´n, el contexto e o o de dicha funci´n se destruye, por lo que la zona referenciada por el array deja de ser v´lida. o a Bidimensionales: La disposici´n en la memoria es, primero filas y luego columnas. Para declarar o un arreglo bidimensional se utilizan las siguientes sintaxis:

5

// Sin inicializar <tipo> <identificador> [n_filas][n_columnas]; /* Con inicializaci´n, n_filas es opcional, o por cada fila, debe declararse un arreglo unidimensional que representa una columna de tama~o n_columnas. Si se define el n´mero de filas, n u la cantidad de arrays deben ser igual a dicho n´mero. */ u <tipo> <identificador> [][n_columnas] = {array1, array2, ...}; <tipo> <identificador> [n_filas][n_columnas] = {array1, array2, ...}; Para acceder a los elementos de un array bidimensional, se utiliza la siguiente sintaxis: <identificador>[<fila>][<columna>] Para declarar un arreglo bidimensional como par´metro por referencia, se indica el tama˜o de todas a n las dimensiones, menos la primera: <tipo_funcion> <identificador_funcion>(...,<tipo> [][n_columnas],...); ¡Cuidado!: A pesar de que un arreglo podr´ considerarse como una variable cualquiera, los opıa eradores de comparaci´n y asignaci´n funcionan de forma completamente distinta a la forma cono o vencional entre dos operandos comunes; en el caso de los operadores de comparaci´n (==, !=), se o comparan las direcciones, m´s no el contenido, por lo que se hace necesario usar sentencias repetia tivas (bucles, loops), para comparar los contenidos; en el caso del operador de asignaci´n =, no es o aplicable a arreglos, es decir, no se puede asignar el valor de un arreglo a otro, para poder realizar una “especie de asignaci´n“, se puede copiar elemento a elemento, utilizando bucles, o bien usando o funciones de biblioteca. 2.2.7. Strings

Los strings o cadenas de caracteres son arrays de caracteres (char) que terminan con el caracter ’\0’ (o NULL). Un string puede representarse de dos formas: 1. Entre “”, donde el compilador reserva autom´ticamente un byte m´s para el caracter final. a a 2. Representaci´n en forma de arreglo, en donde el caracter final se ingresa autom´ticamente (de lo o a contrario, solo se considera como un arreglo de caracteres). Como en C no hay un tipo predefinido para los strings, existen dos formas de declararlo: 1. Como arreglo: char <identificador>[] = "<string>";

2. Como puntero: char *<identificador> = "<string>";

Para declarar un string como arreglo de caracteres: // Sin inicializaci´n o char <identificador>[<tama~o>]; n // Con inicializaci´n, en donde la longitud es opcional. o char <identificador>[<tama~o>] = "<string>"; n char <identificador>[<tama~o>] = {<lista_de_caracteres_con_caracter_nulo>}; n char <identificador>[] = "<string>"; char <identificador>[] = {<lista_de_caracteres_con_caracter_nulo>};

6

Cuando el tama˜o del literal del string declarado supera al tama˜o definido en <tama~o>, en caso n n n de estar definido, entonces se almacena ese n´mero determinado de caracteres, independiente de u que no coincida el tama˜o del literal con el definido. n Para declarar el string como un puntero: // Sin inicializaci´n o char *str; // Con inicializaci´n o char *str = "<string>"; Respecto a ´ste tipo de declaraci´n, es necesario comentar que luego de declarar usando la sintaxis e o de puntero, no se puede modificar el contenido luego de asignarlo. Adem´s, la representaci´n de a o arreglo en la sintaxis de puntero, no es v´lida. a Para comparar los caracteres de las cadenas, es necesario emplear la funci´n strcmp. o 2.2.8. Estructuras

Las estructuras son tipos de datos especiales que almacenan un conjunto de datos de distinto tipo bajo un mismo identificador. La sintaxis para declarar estructuras es la siguiente: struct <identificador> { <declaraci´n [e inicializaci´n] campo1>; o o <declaraci´n [e inicializaci´n] campo2>; o o ... <declaraci´n [e inicializaci´n] campoN>; o o }; La declaraci´n de una estructura debe hacerse antes y fuera de la funci´n donde se use. Cualquier o o campo inicializado en la estructura, quedar´ inicializado por defecto para todas las variables cuyo a tipo sea struct <identificador>. Para declarar variables y funciones cuyo valores de retorno sean de tipo struct <identificador>, se usan las siguientes sintaxis: // Declaraci´n de variables de tipo struct o struct <identificador> <variable>; // Declaraci´n de funciones con valores de retorno de tipo struct o struct <identificador> <funcion>(...); Para acceder a un campo de estructura se deben considerar los operadores . y ->: Operador .: Se utiliza para acceder al campo de una variable de tipo estructura. <identificador_variable>.<campo> Operador ->: Se utiliza para acceder al campo de una variable de tipo puntero a estructura. <identificador_puntero>-><campo> Los operadores de comparaci´n entre variables de tipo estructura no son v´lidos, debe como a pararse campo por campo para determinar si una o m´s variables son o no iguales. El operador a de asignaci´n = sin embargo, s´ es v´lido en cuanto a su uso, ya que al copiar una variable o ı a de un mismo tipo de estructura, se copian los campos uno por uno en forma interna, incluso aunque uno de los campos sea un arreglo.

7

2.3.

Estructura de un archivo fuente

Todo archivo fuente con extensi´n *.c debe cumplir con una estructura determinada para poder ser o compilada y posteriormente ejecutada como binario. Dicha estructura es flexible dentro de su rigidez, y a continuaci´n se mostrar´ el porqu´ de lo mencionado anteriormente, adem´s de presentar tal estructura o a e a al estudiante, de modo que recuerde c´mo programar “el esqueleto” de una aplicaci´n en C. o o Un programa escrito en C, debe poseer la siguiente estructura: <Inclusi´n de archivos cabeceras> o <Sentencias de precompilador> // Descriptores <Definiciones de Constantes> <Definiciones de Estructuras> <Definiciones de Tipos de Datos personalizados> <Declaraci´n de Firmas de Funciones personalizadas> o <Declaraci´n y definici´n de Variables Globales> o o // Programa principal (int|void) main ([args]) { <Declaraci´n y definici´n de variables locales> o o <Implementaci´n de la funci´n> o o <Retorno> } // Implementaci´n de Funciones o <Definici´n de funciones personalizadas> o Como es posible observar y comprobar (lo m´s probable es que el estudiante ya lo haya comprobado en a ramos anteriores), la estructura de C a pesar de mostrar cierta rigidez, presenta flexibilidad a la hora de definir y declarar ciertos elementos, esto es, muchas veces las definiciones de estructuras son realizadas antes de las definiciones de los tipos de datos, o bien, las variables globales son definidas antes de la declaraci´n de las funciones propias, sin embargo, se recomienda utilizar la estructura entregada en ´ste o e documento, para evitar discordancia y comenzar a programar en forma m´s “profesional”. a Tambi´n es posible observar, que los argumentos de la funci´n main presentan cierta flexibilidad al moe o mento de la definici´n, ´sto le da un caracter polim´rfico a la funci´n principal, lo que vuelve menos o e o o r´ ıgida la forma en que se programa. Dentro de la estructura de un archivo C, es posible encontrar diversos tipos de l´ ıneas, que cumplen diversas funciones, ´stos tipos de l´ e ıneas son los siguientes: Las sentencias de precompilador #. Comentarios de una l´ ınea //. Comentarios de l´ ıneas m´ltiples /* y */. u Sentencias simples que llevan un ; al final. Bloques de sentencias entre { y }. L´ ıneas vac´ ıas.

2.4.

Estructura b´sica de la cabecera a

Los archivos de cabecera, corresponden a archivos que contienen declaraciones directas funciones, variables u otros identificadores, son tambi´n llamados ficheros de inclusi´n. La existencia de ´stos e o e archivos facilita la programaci´n, ya que se permite la modularizaci´n de los programas, dividi´ndolos en o o e componentes de menor tama˜o, y con ello permitiendo tambi´n detectar errores en forma m´s sencilla. n e a Cada cabecera est´ compuesta en s´ por dos archivos, el archivo de cabecera mismo, cuya extensi´n es a ı o *.h y un archivo con la definici´n de las firmas existentes en el archivo de cabecera *.c, a continuaci´n, o o 8

la estructura b´sica de ambos archivos: a Archivo de cabecera my header.h #ifndef __MY_HEADER_H__ #define __MY_HEADER_H__ // Declaraciones, firmas, etc... #endif /*__MY_HEADER_H__*/ Archivo de definiciones my header.c #include "my_header.h" // Definici´n de las firmas, etc... o

2.5.

Bibliotecas est´ticas a

Muchas veces se observa que el programador desea ocupar frecuentemente un conjunto de funciones, por lo que podr´ desear agruparlas en un conjunto de c´digos fuente en un archivo objeto y luego crear ıa o la biblioteca (library), con los archivos objeto. B´sicamente cumple la misma funci´n que la estructura a o b´sica mencionada en el punto 2.4, pero conllevando un ahorro en tiempo al compilar (que en muchos a casos es muy importante). Suponga se tiene el mismo archivo *.c mencionado anteriormente: my header.c. Suponga que se tiene el mismo archivo *.h mencionado anteriormente: my header.c. Para crear una biblioteca est´tica a partir a de las librer´ mencionadas, se utilizan los siguientes dos comandos: ıas $ gcc -static -c my_header.c -o my_header.o $ ar -rcs libmy_header.a my_header.o ¡Importante!: Notar que el archivo *.a lleva como prefijo la palabra lib, ´sto es necesario para que e GCC lo enlace en el programa a compilar. Suponga ahora, que ha creado un archivo fuente en el que ha sido usada la biblioteca my header, llamado example.c: #include <stdio.h> #include "my_header.h" int main() { // Llamado a alguna funci´n de my_header o } Para hacer que ´ste programa compile es necesario que el header est´ presente, para eso se utiliza el GCC e e flag -l. $ gcc example.c -L. -lmy_header Al momento de utilizar el flag -l, GCC comenzar´ a buscar en el path entregado junto al flag -L, el a archivo libmy header.a.

2.6.

Punteros a funciones

Probablemente, el alumno al saber que un puntero posee direcci´n de memoria, debe estar golpeando o su cabeza contra la mesa o contra la pared, no hay porqu´ asustarse, en estos momentos, y de ahora en e adelante, se asumir´ que las funciones tienen una direcci´n. La direcci´n de una funci´n es la del primer a o o o segmento de c´digo de la funci´n. o o

9

Un puntero a funci´n, es una variable que almacena la direcci´n de comienzo de la funci´n; se puede o o o pensar en ellos como un alias de la funci´n, pudiendo ser usados como argumentos en otras funciones. El o tipo de ´sta variable, corresponde a “puntero-a-funci´n recibiendo P par´metros y devolviendo X como e o a resultado”, donde P corresponde a los par´metros que recibe la funci´n apuntada, y X, el tipo de variable a o devuelta por la funci´n apuntada. o ¡Importante!: El puntero siempre debe ir encerrado entre par´ntesis. e A continuaci´n, una sintaxis general para declarar punteros a funciones: o <valor_retorno> (*<identificador>) ([<lista de tipos par´metros>]); a Por ejemplo, para mayor claridad del estudiante: int (*puntero) (int, int); En el caso de las matrices, tambi´n pueden aplicarse a punteros a funciones: e int (*puntero[15]) (int, int); // 15 punteros a funci´n. o ¡Importante!: T´cnicamente una matriz de punteros a funci´n y una matriz de funciones son e o distintos, sin embargo, en la pr´ctica se consideran como equivalentes, ya que cada puntero referencia al a primer segmento de cada funci´n. o Para la asignaci´n de punteros a funci´n, considere el ejemplo anterior y adem´s, la funci´n int my func(int, o o a o int): int main() { int (*puntero) (int, int); int result; puntero = &my_func; result = puntero(1,2); return 0; } 2.6.1. Reservando memoria

Como se ha visto en el ramo de Estructura de Datos (en algunos casos Estructura de Datos y algoritmos), se asume como sabido que es posible reservar memoria en forma din´mica. A continuaci´n se a o mostrar´n ejemplos en forma breve y a modo de repaso, de como reservar memoria en ciertos casos: a 1. Punteros simples como arreglos unidimensionales : int main() { int *matriz; int num_elementos = 10; // Se crea una matriz de 10 elementos. matriz = (int*)malloc(sizeof(int*)*num_elementos); /* Operaciones con el arreglo */ // Se libera la memoria del puntero. free(matriz) }

2. Punteros dobles como arreglos bidimensionales: int main() { int **matriz; int num_elementos = 10; 10

// Se crea una matriz de 10x10 elementos, partiendo por las filas. matriz = (int**)malloc(sizeof(int*)*num_elementos); // Se crean las columnas. for (int i = 0; i < num_elementos; i++) { matriz[i] = (int*)malloc(sizeof(int*)*num_elementos); } /* Operaciones con el arreglo */ // Se libera la memoria de cada columna. for (int i = 0; i < num_elementos; i++) { free(matriz[i]); } // Se libera la memoria del puntero. free(matriz) }

2.7.

Listas

Una lista es un tipo de dato abstracto, en el que se pueden almacenar diversos elementos de un mismo tipo, un un n´mero indeterminado de veces, en forma ordenada. Una lista est´ compuesta por nodos, que u a representan cada uno de los elementos agregados y est´n conectados a trav´s de punteros. B´sicamente a e a una lista se puede definir a trav´s de una estructura b´sica como la siguiente: e a struct node { <type> data; node *next; }; // Dato // Puntero al nodo siguiente

struct list { unsigned int size; // Tama~o de la lista n node *first; // Nodo cabecera node *current; // Nodo actual }; ´ Esta estructura b´sica tiene diversas variantes, dependiendo del tipo de lista que se quiera construir, a pudiendo agregarse un puntero a un ultimo nodo, a nodos anteriores, etc. ´ Es conveniente implementar funciones para realizar operaciones a nivel de estructura, algunas de estas operaciones son: clean(Lista): Elimina todos los elementos de la lista Lista. empty(Lista): Comprueba que la lista Lista est´ vac´ e ıa. push(Lista, x, n): Inserta un dato x a la lista Lista, despu´s del nodo apuntado por n. e delete(L,x): Elimina al nodo cuyo dato sea x de la lista Lista. node(x): Obtiene el nodo apuntado por x. data(n): Obtiene el dato almacenado por el nodo n. next(n): Obtiene la direcci´n siguiente del nodo apuntado por n, si n no es NULL. o Existen varios tipos de listas, de entre los cuales se encuentran: Listas simplemente enlazadas: Listas cuyos nodos apuntan s´lamente al nodo siguiente. o Listas doblemente enlazadas: Listas cuyos nodos apuntan al nodo siguiente y al nodo anterior. Listas circulares: Listas enlazadas en las que el ultimo nodo apunta al primero. ´ 11

Listas circulares doblemente enlazadas: Listas doblemente enlazadas en las que el ultimo nodo ´ apunta al primero.
*

Referencias
[1] Wikipedia - C (lenguaje de programaci´n) http://es.wikipedia.org/wiki/C_(lenguaje_de_programacion) o [2] Introducci´n al lenguaje C - Jos´ D. Guti´rrez Porset http://www.slideshare.net/danitxu/lenguaje-c-pdf o e e [3] Variables, Constantes y Tipos de Datos en variables-constantes-y-tipos-de-datos-en-c-presentation C http://www.slideshare.net/javi2401/

[4] Wikipedia - Archivo de Cabecera http://es.wikipedia.org/wiki/Archivo_de_cabecera [5] Archivos de cabecera archivos-de-cabecera-en-c/ en C++ http://computaciongrafica.wordpress.com/2008/03/29/

[6] Compilaci´n de un programa en C/C++ http://www.fismat.umich.mx/mn1/manual/node2.html o [7] Crear Bibliotecas Est´ticas a crear-bibliotecas-estaticas-con-gcc/ con GCC http://varrojo.algorithmia.net/2010/05/22/

[8] Puntero a funci´n http://www.zator.com/Cpp/E4_2_4.htm o [9] Listas en lenguaje C http://programandoenc.over-blog.es/article-listas-en-lenguaje-c-58802324.html [10] Memoria din´mica: malloc y free sopa.dis.ulpgc.es/so/cpp/intro_c/introc75.htm a

* Cualquier

duda, sugerencia o correcci´n del documento, hacerla a cizambra@alumnos.inf.utfsm.cl o

12

Sign up to vote on this title
UsefulNot useful