ESTRUCTURAS DE DATOS
|
|
|
- Rocío Naranjo Fidalgo
- hace 9 años
- Vistas:
Transcripción
1 ESTRUCTURAS DE DATOS Gestión Dinámica de Memoria Funciones New Malloc Realloc delete - free Ing Yamil Armando Cerquera Rojas Especialista en Sistemas Universidad Nacional Docente Universidad Surcolombiana Neiva - Huila Contenido Introducción 2 El tipo de datos Puntero 2 DEFINICIÓN: 3 Declaración de variables puntero 4 Asignación errónea: "Cannot assign..." 5 Operaciones con Punteros 6 Operadores específicos de punteros 7 Diferencia entre punteros y variables: 8 Asignación de Punteros 9 Comparación de Punteros 10 Asignación Dinámica de Memoria 10 Funciones de reserva de memoria: new 11 Función de liberación de memoria dinámica: delete 12 Ejemplo de asignación dinámica de memoria 13 Punteros a Estructuras 13 Listas encadenadas o enlazadas 15 Operaciones básicas sobre listas enlazadas 16 Otras clases de listas enlazadas 23 Ejercicios 27 Otras funciones de reserva de memoria de forma dinámica. 30 Función de liberación de memoria dinámica: free(). 33 Creación Dinámica de Arrays y Aritmética de Punteros. 34 Universidad Surcolombiana Pag 1 de 40
2 Introducción En este tema se estudiarán las posibilidades que ofrece el Lenguaje C a la hora de trabajar dinámicamente con la memoria dentro de los programas, esto es, reservar y liberar bloques de memoria al momento de ejecutar un programa. Además en este tema se introducirá el concepto de tipo abstracto de dato y la forma de dividir un gran programa en otros más pequeños. Los tipos de datos vistos hasta ahora, tanto los simples (Predefinidos por el lenguaje) como los estructurados (Definidos por el programador), sirven para describir datos o estructuras de datos cuyos tamaños y formas se conocen de antemano. Cuando se declara una variable se reserva la memoria suficiente para contener la información que debe almacenar. Esta memoria permanece asignada a la variable hasta que termine la ejecución del programa (fin de la función main). Sin embargo, hay programas cuyas estructuras de datos pueden variar de forma y tamaño durante la existencia del mismo (En modo ejecución). Las variables de todos los tipos de datos vistos son variables estáticas, en el sentido de que se declaran en el programa, se designan por medio del identificador declarado (variable), y se reserva para ellas un espacio en memoria en tiempo de compilación. El contenido de la variable podrá cambiar durante la ejecución del programa, pero no el tamaño de memoria reservado para determinada variable. En ocasiones el tamaño de los objetos no se conoce hasta el momento de la compilación. Por ejemplo, la longitud de una cadena de caracteres que introducirá el usuario no se conoce hasta el tiempo de ejecución. El tamaño de un arreglo puede depender de un parámetro cuyo valor se desconoce previo al momento de ejecución. Ciertas estructuras de datos como listas enlazadas, pilas y colas utilizan memoria dinámica. C++, ofrece la posibilidad de crear o destruir variables en tiempo de ejecución del programa, a medida que van siendo necesitadas durante la ejecución del mismo. Puesto que estas variables no son declaradas en el programa, no tienen nombre y se denominan variables anónimas. Si un lenguaje permite la creación de variables anónimas, debe también proporcionar una forma de referirse a estas variables, de modo que se les pueda asignar valores. Del mismo modo, debe proporcionar una forma de acceder a estas. Para ello C++ proporciona el tipo Puntero. El tipo de datos Puntero El tipo puntero y las variables declaradas de tipo puntero se comportan de forma diferente a las variables que se han estudiado en temas anteriores. Hasta ahora cuando se declaraba una variable de un determinado tipo, dicha variable podía contener directamente un valor de dicho tipo. Con el tipo puntero esto no es así. Universidad Surcolombiana Pag 2 de 40
3 Los punteros proporcionan la mayor parte de la potencia al C y C++, y marcan la principal diferencia con otros lenguajes de programación. Una buena comprensión y un buen dominio de los punteros pondrán en sus manos una herramienta de gran potencia. Un conocimiento mediocre o incompleto te impedirá desarrollar programas eficaces. Por eso se le dedicará especial atención y mucho espacio a los punteros. Es muy importante comprender bien cómo funcionan y cómo se usan. Para entender qué es un puntero se da un repaso primero cómo se almacenan los datos en un ordenador. La memoria de un ordenador está compuesta por unidades básicas llamadas bits. Cada bit sólo puede tomar dos valores, normalmente denominados alto y bajo, ó 1 y 0 (Estados lógicos). Pero trabajar con bits no es práctico, y por eso se agrupan. Cada grupo de 8 bits forma un byte u octeto. En realidad el microprocesador, y por lo tanto el programa, sólo puede manejar directamente bytes o grupos de dos o cuatro bytes. Para acceder a los bits hay que acceder antes a los bytes. Y aquí se llega al asunto, cada byte tiene una dirección, llamada normalmente dirección de memoria (compuesta por un segmento y un desplazamiento). La unidad de información básica es la palabra, dependiendo del tipo de microprocesador una palabra puede estar compuesta por dos, cuatro, ocho o dieciséis bytes. Se hablará en estos casos de plataformas de 16, 32, 64 ó 128 bits. Se habla indistintamente de direcciones de memoria, aunque las palabras sean de distinta longitud. Cada dirección de memoria contiene siempre un byte (Una dirección de memoria corresponde únicamente a un byte). Lo que sucederá cuando las palabras sean de 32 bits es que se accede a posiciones de memoria que serán múltiplos de 4. Todo esto sucede en el interior de la máquina, e imteresa más bien poco. Se puede saber qué tipo de plataforma se usa averiguando el tamaño del tipo int, y para ello hay que usar el operador "sizeof()", por ejemplo: cout << "Plataforma de " << 8*sizeof(int) << " bits"; DEFINICIÓN: Un puntero es un tipo especial de variable que contiene, ni más ni menos que, una dirección de memoria. Por supuesto, a partir de esa dirección de memoria puede haber cualquier tipo de objeto (o dato): un char, un int, un float, un array, una estructura, una función u otro puntero. El programador será el responsables de decidir ese contenido. Universidad Surcolombiana Pag 3 de 40
4 En síntesis un puntero es una variable cuyo valor es la dirección de memoria de otra variable. Esto quiere decir que un puntero se refiere indirectamente a un valor. Por tanto, no hay que confundir una dirección de memoria con el contenido de esa dirección de memoria: Se hace una distinción entre la variable referencia (puntero) y la variable referenciada por un puntero (anónima o apuntada). Variable Referencia (Puntero): Es una variable estática, es decir se crea en tiempo de compilación. Variable Referenciada (Anónima): Es una variable dinámica creada en tiempo de ejecución, que únicamente puede ser accedida a través de un puntero. Una variable puntero no puede apuntar a cualquier variable anónima; debe apuntar a variables anónimas de un determinado tipo. El tipo de la variable anónima vendrá determinado por el tipo de la variable que la apunta. Este tipo debe ser incluido en la especificación del tipo puntero. Declaración de variables puntero Un puntero, como cualquier variable u objeto, además de ser declarado (para comenzar a existir) necesita ser inicializado (darle un valor de modo controlado), lo cual se realiza mediante el operador de asignación ('='). Desde que el puntero es declarado almacena un valor, el problema es que se trata de un valor aleatorio, intentar operar con un puntero sin haberlo inicializado es una frecuente causa de problemas. Los punteros se declaran precediendo el identificador con el operador de indirección, (*), que se leerá como "puntero a". La forma general de declarar un tipo de datos puntero es la siguiente: typedef <tipo> *<identificador>; Donde tipo es el tipo base del puntero, que puede ser cualquier tipo válido e identificador es el nombre del tipo de datos. Ejemplos de declaración de tipos de datos punteros son los siguientes: Universidad Surcolombiana Pag 4 de 40
5 Ejemplos: int *entero; char *carácter; struct stpunto *punto; typedef int *PunAInt;// Tipo puntero a enteros (int) typedef char *PunACar;// Tipo puntero a caracteres (char) Asignación errónea: "Cannot assign..." En primer lugar veremos un caso simple de asignación errónea para comprender el mensaje enviado por el compilador. void main() { int a; int* b; b = a; //Error El programa no compila, y recibimos el mensaje de error: "Cannnot assign 'int' to 'int near*' en function main(); Los punteros siempre apuntan a un objeto de un tipo determinado, en el ejemplo anterior, "entero" siempre apuntará a un objeto de tipo "int". Tras definir los tipos de datos, es posible definir una variable de estos tipos del modo siguiente: PunAInt contador; //contador es una variable de tipo PunAInt PunACar carácter; // carácter es una variable de tipo PunACar La variable contador no contendrá un valor entero (tipo int), sino la dirección de memoria donde esté almacenado un valor de tipo int. El valor almacenado en la variable anónima de tipo int apuntada por contador será accesible a través de este puntero. La forma: <tipo>* <identificador>; con el (*) junto al tipo, en lugar de junto al identificador de variable, también está permitida. Vea algunos matices. Retomando el ejemplo anterior: int *entero; equivale a: int* entero; Universidad Surcolombiana Pag 5 de 40
6 Debe tener muy claro que "entero" es una variable del tipo "puntero a int", que "*entero" NO es una variable de tipo "int". Un puntero, puede apuntar a cualquier tipo de dato definido por el usuario, no sólo a tipos simples. Es muy importante tener en cuenta que primero debe declararse el tipo de datos al que apunta (un array, registro,...) y después el tipo de datos puntero. Ejemplo: struct Complejo { float parte_real; float parte_imag; ; typedef Complejo *TPComplejo; //Tipo puntero a un valor de tipo Complejo Después definir el tipo TPComplejo, una variable de este tipo se definiría de la siguiente forma: TPComplejo ptr1; //ptr1 es una variable de tipo TPComplejo ptr1, es un puntero, por tanto contendrá una dirección de memoria; y en esa posición de memoria habrá una variable de tipo Complejo. Por otro lado, también es posible definir variables de tipos puntero sin asociarles un nombre de tipo (declaración anónima): <tipo> *<ident>; Donde tipo es el tipo base del puntero, y puede ser cualquier tipo válido. Algunos ejemplos de declaración de variables puntero son los siguientes: int *contador; // puntero a una variable de tipo entero (int) char *caracter; // puntero a una variable de tipo caracter (char) Complejo *ptr1; // ptr1 es un puntero a una variable de tipo Complejo Siempre que sea posible evite las declaraciones anónimas, y asocie un nombre de tipo a las variables que declare. NOTA: Al declarar un puntero hay que asegurarse de que su tipo base sea compatible con el tipo de objeto al que se quiera que apunte. NOTA: Aunque no es una imposición de C, nosotros siempre declararemos el tipo de datos puntero inmediatamente después del tipo de datos al que apunta. Operaciones con Punteros Las operaciones que se pueden llevar a cabo con punteros son: Universidad Surcolombiana Pag 6 de 40
7 1) Operadores específicos de punteros 2) Asignaciones de punteros 3) Comparación de punteros Operadores específicos de punteros Al trabajar con punteros se emplean dos operadores específicos: Operador de dirección: & Es un operador monario (sólo requiere un operando) que devuelve la dirección de memoria del operando. Por ejemplo, typedef int *PunAInt; int q,m; // q y m son variables de tipo entero (int) PunAInt contador; // contador es un puntero a un entero (int) q = 100; m = 200; contador = &m; // contador contiene la dirección de m La línea de código contador = &m, coloca en contador la dirección de memoria de la variable m (es decir el valor 1506). No tiene nada que ver con el valor de la variable m. Esta instrucción de asignación significa contador recibe la dirección de m. Para entender mejor cómo funciona el operador de dirección &, la siguiente figura muestra cómo se almacenan en memoria las variables q, m y contador. Por supuesto, los tipos tienen que ser "compatibles", no se puede almacenar la dirección de una variable de tipo "char" en un puntero de tipo "int". Por ejemplo: int A; int *pa; pa = &A; Según este ejemplo, pa es un puntero a int que apunta a la dirección donde se almacena el valor del entero identificado con A. Universidad Surcolombiana Pag 7 de 40
8 Operador de contenido o indirección: * Este operador es el complementario de &. Es un operador monario que devuelve el valor de la variable ubicada en la dirección que se especifica (contenido de la variable anónima apuntada por el puntero). Continuando con el ejemplo anterior, si contador contiene la dirección de memoria de la variable m (posición 1506), *contador devolverá el valor 200 (el valor de la variable anónima apuntada por contador). Entonces al ejecutar la siguiente asignación: q = *contador; La línea de código anterior colocará el valor 200 en la variable q. NOTA: No se debe confundir el operador * en la declaración del puntero (int *contador;) con el operador * en las instrucciones (*contador = 200;) Diferencia entre punteros y variables: Declarar un puntero no creará un objeto. Por ejemplo: int *entero; no crea un objeto de tipo "int" en memoria, sólo crea una variable que puede contener una dirección de memoria. Se puede decir que existe físicamente la variable "entero", y también que esta variable puede contener la dirección de un objeto de tipo "int". Observe el siguiente ejemplo: int A,B; int *entero;... B = 213; /* B vale 213 */ entero = &A; /* entero apunta a la dirección de la variable A */ *entero = 103; /* equivale a la línea A = 103; */ B = *entero; /* equivale a B = A; */... En este ejemplo se nota que "entero" puede apuntar a cualquier variable de tipo "int", y que puede hacer referencia al contenido de dichas variables usando el operador de indirección (*). Universidad Surcolombiana Pag 8 de 40
9 Como todas las variables, los punteros también contienen "basura" cuando son declaradas. Es costumbre dar valores iniciales nulos a los punteros que no apuntan a ningún sitio concreto: entero = NULL; caracter = NULL; NULL es una constante, que está definida como cero en varios ficheros de cabecera, como "cstdio" o "iostream", y normalmente vale 0L. Asignación de Punteros Después de declarar un puntero, pero antes de asignarle un valor, el puntero contiene un valor desconocido. Si se intenta utilizar el puntero antes de darle valor, se producirá un fallo en el programa. Un puntero que no apunte a ninguna posición válida de la memoria ha de tener asignado el valor nulo, que es un cero. Se usa el valor nulo porque C++ garantiza que no existe ningún objeto en la dirección nula. Así, cualquier puntero que sea nulo indica que no apunta a nada y no debe ser usado. Una forma de dar a un puntero el valor nulo es asignándole el valor cero. Por ejemplo, lo siguiente inicializa el puntero p a nulo: char *p = 0; Además, la mayoría de los archivos de cabecera de C++, como <cstdio>, definen NULL como el valor de puntero nulo. Por eso también es posible ver la inicialización de un puntero a nulo usando la constante NULL (aunque realmente con esto le estamos asignando un 0). char *p = NULL; NOTA: El uso del valor nulo es simplemente un convenio que siguen los programadores C++. No es una regla impuesta por el lenguaje C++. Tener mucho cuidado porque la siguiente secuencia no provocará ningún error de compilación, pero hará que el programa falle: int *p = 0; *p = 10; // Incorrecto Asignación en la posición 0 Es posible asignar el valor de una variable puntero a otra variable puntero, siempre que ambas sean del mismo tipo. int *ptr1,*ptr2; ptr2 = ptr1; Universidad Surcolombiana Pag 9 de 40
10 La variable ptr2, apuntará a donde apunte ptr1. A la variable ptr2 se le asigna la dirección de memoria de ptr1. Es muy importante tener en cuenta que si ptr2, anteriormente estaba apuntando a una variable anónima (tenía un valor distinto del valor nulo), ésta será inaccesible, puesto que la forma de acceder a ella era a través del puntero que la apuntaba ptr2, y este ahora apunta a otra variable anónima (la apuntada por ptr1). Comparación de Punteros Es posible comparar dos variables de tipo puntero en una expresión relacional, usando operaciones relacionales de igualdad (==), desigualdad (!=) y comparación (<, >, =<, >=). Dos variables puntero son iguales, sólo si ambas apuntan a la misma variable anónima (misma dirección de memoria) o si ambas están inicializados al valor nulo (valor 0). Los punteros a los que se apliquen estas operaciones relacionales han de ser del mismo tipo. Sin embargo, es posible comparar punteros de cualquier tipo (igualdad o desigualdad) con el valor nulo. if (ptr1 < ptr2) { cout << p apunta a una dirección de memoria menor que q << endl; Asignación Dinámica de Memoria Hasta este momento solamente se han realizado asignaciones estáticas del programa, y más concretamente estas asignaciones estáticas no eran otras que las declaraciones de variables en el programa. Cuando se declara una variable se reserva la memoria suficiente para contener la información que debe almacenar. Esta memoria permanece asignada a la variable hasta que termine la ejecución del programa (fin de la función main). Realmente las variables locales de las funciones se crean cuando éstas son llamadas pero no se tiene control sobre esa memoria, el compilador genera el código para esta operación automáticamente. En este sentido las variables locales están asociadas a asignaciones de memoria dinámicas, puesto que se crean y destruyen durante la ejecución del programa Permite crear variables y arreglos de manera dinámica. Dinámica quiere decir que el espacio de memoria es asignado durante la ejecución o corrida del programa. En ocasiones el tamaño de los objetos no se sabe hasta el momento de la ejecución. Por ejemplo, la longitud de una cadena de caracteres que introducirá el usuario no se sabe hasta el momento en que se ejecute el programa. El tamaño de un arreglo puede depender de un parámetro cuyo valor se desconoce previo al momento de ejecución. Ciertas estructuras de datos como listas enlazadas, pilas y colas utilizan memoria dinámica. Universidad Surcolombiana Pag 10 de 40
11 Cuando se habla de asignación dinámica de memoria, se refiere al hecho de crear variables anónimas, es decir reservar espacio en memoria para estas variables en tiempo de ejecución, y también a liberar el espacio reservado para una variable anónima en tiempo de ejecución, en caso de que dicha variable ya no sea necesaria. La zona de la memoria donde se reservan espacios para asignarlos a variables dinámicas se denomina HEAP o montón. Puesto que esta zona tiene un tamaño limitado, puede llegar a agotarse si únicamente asignamos memoria a variables anónimas y no liberamos memoria cuando ya no sea necesaria, de ahí la necesidad de un mecanismo para liberar memoria. El núcleo del sistema de asignación dinámica de C++ está compuesto por la función new para la asignación de memoria, y la función delete para la liberación de memoria. Estas funciones trabajan juntas usando la región de memoria libre. Esto es, cada vez que se hace una petición de memoria con new, se asigna una parte de la memoria libre restante. Cada vez que se libera memoria con delete, se devuelve la memoria al sistema. El subsistema de asignación dinámica de C++ se usa para dar soporte a una gran variedad de estructuras de programación, tales como listas enlazadas que se verán más adelante en este tema. Otra importante aplicación de la asignación dinámica es la asignación dinámica de arrays. Funciones de reserva de memoria: new La función new es simple de usar y será la función que nosotros siempre utilizaremos. El prototipo de la función new es: void *new <nombre_tipo>; Aquí, nombre_tipo es el tipo para el cual se quiere reservar memoria. La función new reservará la cantidad apropiada de memoria para almacenar un dato de dicho tipo. La función new devuelve un puntero de tipo void *, lo que significa que se puede asignar a cualquier tipo de puntero. Convierte automáticamente el puntero devuelto al tipo adecuado, por lo que el programador no tiene que preocuparse de hacer la conversión de forma explícita. char *p; p = new char; // reserva espacio para un carácter La función new también se encarga de averiguar cual es el tamaño en bytes del tipo de datos para el cual se desea reservar memoria (recordemos que la cardinalidad de un tipo nos permite saber la memoria necesaria para su almacenamiento). Observar en el ejemplo anterior que se indica el que se quiere reservar memoria para un dato de tipo char y no cual es el tamaño en bytes que se quiere reservar. Universidad Surcolombiana Pag 11 de 40
12 Tras una llamada con éxito, new devuelve un puntero al primer byte de la región de memoria dispuesta del montón. Si no hay suficiente memoria libre para satisfacer la petición, se produce un fallo de asignación y new devuelve un NULL. NOTA: Si se define un tipo puntero con typedef, cuando se reserva memoria para una variable de ese tipo se indica el tipo de datos de la variable anónima (a la que apunta el puntero) y no el nombre del tipo puntero. typedef char *TPChar; TPChar p; p = new char; //Correcto p = new TPChar; //INCORRECTO NOTA: Como el montón no es infinito, siempre que se reserve memoria con malloc() o new debe comprobarse, antes de usar el puntero, el valor devuelto para asegurarse que no es nulo. char *p; p = new char; if (p == NULL) { // No se ha podido reservar la memoria deseada // Tratar error Función de liberación de memoria dinámica: delete La función delete es la complementaria de new. Una vez que la memoria ha sido liberada, puede ser reutilizada en una posterior llamada a new. El prototipo de la función delete es: void delete <variable_puntero>; Aquí, p es un puntero a memoria que ha sido previamente asignado con new. Por ejemplo: char *p; p = new char; // reserva espacio para un carácter delete p; //libera la memoria previamente reservada NOTA: Es muy importante no llamar NUNCA a delete con un argumento no válido; se dañará el sistema de asignación. NOTA: Las función delete p NO asignan el valor nulo al puntero p después de liberar la memoria a la que apuntaba. Universidad Surcolombiana Pag 12 de 40
13 Ejemplo de asignación dinámica de memoria Para ver mediante un ejemplo, como se asigna/libera memoria dinámica, recordemos la estructura Complejo definida anteriormente. Para crear una variable anónima de tipo Complejo hacemos una llamada a new que creará una variable anónima de tipo Complejo apuntada por el puntero ptr1, donde ptr1 es un puntero del tipo definido anteriormente TPComplejo (Tipo puntero al tipo registro Complejo) TPComplejo ptr1; ptr1 = new Complejo; Si queremos liberar el espacio reservado antes, haremos una llamada a delete que liberará el espacio de memoria reservado para la variable de tipo Complejo apuntada por ptr1. delete ptr1; Gráficamente, teniendo en cuenta que Complejo es un registro con dos campos, las operaciones de asignación/liberación de memoria dinámica anteriores pueden verse de la siguiente forma: Punteros a Estructuras C++ permite punteros a estructuras igual que permite punteros a cualquier otro tipo de variables. Sin embargo, hay algunos aspectos especiales que afectan a los punteros a estructuras que vamos a describir a continuación. La declaración de un puntero a una estructura se realiza poniendo * delante del nombre de la variable de estructura. Por ejemplo, si tenemos la estructura DatosPersonales la siguiente línea declara un tipo de datos puntero a un dato de este tipo: struct DatosPersonales { char nombre[80]; unsigned int edad; ; typedef DatosPersonales *TPunt_dpersonales; Universidad Surcolombiana Pag 13 de 40
14 Ó declarando primero el puntero (indicando que es a una estructura) y luego se declara la estructura. typedef struct DatosPersonales *TPunt_dpersonales; struct DatosPersonales { char nombre[80]; unsigned int edad; ; Los punteros a estructuras se usan mucho con el fin de crear listas enlazadas y otras estructuras de datos dinámicas utilizando el sistema de asignación dinámica de memoria. En el siguiente punto de este tema describiremos en profundidad las listas enlazadas. Para acceder a una variable anónima de tipo registro apuntada por un puntero, se usa, como ya vimos en el punto 8.3 el operador de indirección *. A continuación mostramos un ejemplo: Tpunt_dpersonales p;// Puntero a un registro de tipo DatosPersonales p = new DatosPersonales; //reserva memoria para una variable de tipo DatosPersonales y pone el //puntero p apuntando a dicha variable anónima (*p).edad = 15; //accede al campo edad del registro y le asigna el valor 15 Como se puede observar en la última sentencia para acceder a campo de una variable anónima de tipo registro apuntado por un puntero, se pone el operador de indirección entre paréntesis (para evitar problemas de precedencia pues el operador * tiene mas precedencia que el.) seguido de un. y el nombre del campo al que se desea acceder. Debido a que esta notación resulta algo incomoda de escribir es posible usar en su lugar el operador ->. A -> se le denomina operador flecha y consiste en el signo menos seguido de un signo de mayor. Cuando se accede a un miembro de una estructura a través de un puntero a la estructura, se usa el operador flecha en lugar del operador punto. Según esto la última sentencia del ejemplo anterior se suele escribir de la forma siguiente: P -> edad = 15; // accede al campo edad del registro. NOTA: Si p es un puntero a struct DatosPersonales, p.edad ES INCORRECTO Para pedir memoria para mas de un elemento la sintaxis de new y delete cambia. Nueva sintaxis: p-var=new type[tamaño]; delete [tamaño]p-var; ó delete []p-var; // para un compilador reciente. Ejemplo: Universidad Surcolombiana Pag 14 de 40
15 main() { int *v; int n; cout << "Cuantas posiciones\n" cin << n; v=new int[n];.. delete [n]v; Listas encadenadas o enlazadas Una lista enlazada está formada por una colección de elementos (denominados nodos) tales que cada uno de ellos almacena dos valores: (1) un valor de la lista y (2) un apuntador que indica la posición del nodo que contiene el siguiente valor de la lista. Un arreglo almacena todos sus elementos agrupados en un bloque de memoria. Mientras que en una lista enlazada la memoria se va tomando según se necesita. Se van agregando nodos. Cuando queremos añadir un nuevo elemento ( nodo ) reservamos memoria para él y lo añadimos a la lista. La lista va tomando su estructura usando apuntadores que conectan todos sus nodos como los eslabones de una cadena, o las cuentas de un collar. Cuando queremos eliminar el elemento simplemente lo sacamos de la lista y liberamos la memoria usada. Lo que se desea es una nueva forma de asignar memoria, para nuevos nodos de la lista, solo cuando se requiere y desasignar esa memoria cuando ya no se necesita. Además, la memoria recién asignada debe combinarse lógicamente en una sola entidad (la representación de la lista). Una lista encadenada proporciona estas capacidades. Retomando, una lista encadenada comprende un conjunto de nodos, cuyo formato incluye dos campos macro: uno que contiene los datos (información) deseados; y el otro que contiene la dirección del nodo al cual apunta en la lista (Enlace). struct nodo { int información; struct node* Enlace; ; Figura Formato de un nodo para una lista encadenada En la anterior definición del tipod e dato nodo, se supone que como dato solo podrá almacenar un entero. Universidad Surcolombiana Pag 15 de 40
16 Ventajas del uso de listas encadenadas Hay una ganancia implícita de memoria, porque se pueden usar partes de memoria de las que no pueden disponer las listas secuenciales. Los algoritmos de inserción y borrado son más sencillos. Es más sencillo unir dos listas, o dividir una lista. Permite abstracción con polimorfismo. Este esquema permite la creación de estructuras mucho más complejas. Pero la ventaja más importante, es la de poder realizar la abstracción de las diferentes estructuras de datos, definidas dinámicamente. Desventajas Encontrar un nodo arbitrario en una lista encadenada requiere comenzar la búsqueda por el inicio de la lista y, usar la información de cada elemento hasta encontrar el nodo deseado. Los apuntadores requieren memoria adicional. Para entrar a una lista encadenada se requiere información sobre el primer nodo. El uso de listas encadenadas implica la existencia de mecanismos para la asignación dinámica de la memoria requerida, que depende del tipo de herramienta de programación. Este es ambiente ideal del lenguaje C y C++ Operaciones básicas sobre listas enlazadas Los punteros y la asignación dinámica de memoria permiten la construcción de estructuras enlazadas. Una estructura enlazada es una colección de nodos, cada uno de los cuales contiene uno o más punteros a otros nodos. Cada nodo es un registro en el que uno o más campos son punteros. La estructura enlazada más sencilla es la lista enlazada. Una lista enlazada consiste de un número de nodos, cada uno de los cuales contiene uno o varios datos, más un puntero; el puntero permite que los nodos formen una lista. Una variable puntero (puntero externo) apunta al primer nodo en la lista, el primer nodo apunta al segundo, y así todos los demás. El último nodo contiene el valor nulo en su campo de tipo puntero. Para ilustrar las listas enlazadas, mostraremos como definir, crear y manipular una lista enlazada cuyos nodos contienen un único carácter. Los nodos de la lista se Universidad Surcolombiana Pag 16 de 40
17 implementan con registros, ya que cada nodo debe disponer de un enlace al siguiente elemento además de los datos de la lista propiamente dichos. En nuestro ejemplo, declaramos el tipo TLista que define un puntero al registro Nodo que representa un nodo de la lista. Un valor del tipo Nodo será un registro con dos campos: c (el carácter almacenado en el nodo) y sig (un puntero al siguiente nodo en la lista). struct TNodo typedef TNodo *TLista; { char c; typedef struct TNodo *TLista; TNodo *sig; struct TNodo ; { char c; TLista sig; ; Tabla 1. Las 2 Posibilidades (equivalentes) de Definir una Lista Enlazada Podemos dibujar una variable de tipo Nodo como una caja con dos campos. Una lista enlazada creada con nodos de tipo Nodo tendría la forma siguiente, donde primero es una variable de tipo TLista (puntero externo) que apunta al primer nodo de la lista: TLista primero; Con esta estructura, para almacenar una cadena de caracteres en memoria iremos leyendo carácter a carácter desde teclado, y creando variables anónimas de forma dinámica, almacenando en cada una de ellas un carácter y enlazándolas mediante el campo puntero. Procederemos así hasta que nos introduzcan un retorno de carro, que nos indicará el final de la cadena de caracteres. Las operaciones básicas sobre listas enlazadas son: 1) Creación de una la lista enlazada 2) Insertar un nodo en la lista enlazada 3) Borrar un nodo de la lista enlazada Creación de una lista enlazada El procedimiento de creación de una lista enlazada es muy simple e inicializa un puntero del tipo TLista a NULL. TLista crearlistaenlazada() { return NULL; Universidad Surcolombiana Pag 17 de 40
18 Insertar un nodo en la lista enlazada La tarea de insertar un nodo en una lista enlazada podemos separarla en dos casos: 1) Insertar un nodo al comienzo de una lista 2) Insertar un nodo después de un determinado nodo existente (por ejemplo, para mantener una lista ordenada). 1) Insertar un nodo al comienzo de una lista Para insertar un nuevo nodo al comienzo de una lista, debemos seguir los pasos siguientes: 1. Crear un nodo para una variable anónima de tipo TNodo. punt = new TNodo; 2. Una vez que se ha creado un nodo apuntado por punt, se le asigna al campo c el carácter que queremos insertar (ej: d ). punt->c = d ; 3. Hacemos que el campo sig del nuevo nodo apuntado por punt, apunte al primer nodo de a lista (esto es, que apunte donde apunta primero). punt->sig = primero; 4. Por último, hemos de actualizar primero, puesto que este puntero siempre ha de apuntar al primer elemento de la lista, y en este caso este es el nuevo nodo insertado. primero = punt; void insertarfrente(tlista &primero 1, char car) { // Esta función inserta un nodo al principio de la lista TLista punt; punt = new TNodo; if (punt == NULL) { cout << No hay memoria suficiente << endl; else { punt->c = car; punt->sig = primero; primero = punt; 1 Para modificar la lista enlazada debemos pasar la variable primero por referencia. Recordar del Tema 4 que para pasar un valor por referencia a una función se hacía anteponiendo el operador & al nombre de la variable Universidad Surcolombiana Pag 18 de 40
19 2) Insertar un nodo en una posición determinada de una lista Cuando queremos insertar un elemento de forma ordenada en una lista enlazada ordenada (por ejemplo, ordenada alfabéticamente): 1. Se ha de crear un nodo nuevo, y en su campo c almacenar el valor del carácter que quiera insertar. Para ello creamos un nodo apuntado por la variable puntero nuevonodo, y se asigna al campo c el valor del carácter leído (por ejemplo, c ). nuevonodo = new TNodo; if (nuevonodo == NULL) { cout << No hay memoria suficiente << endl; else { nuevonodo->c = c ; nuevonodo->sig = NULL; 2. Se necesita una variable puntero auxiliar, que llamaremos ptr, que recorra cada uno de los nodos de la lista, hasta que encuentre el lugar exacto donde insertar el nuevo nodo; pero ptr siempre ha de estar apuntando al nodo anterior, con respecto al nodo que establece la comparación, para que una vez que ha localizado el lugar exacto pueda enlazar el campo siguiente del nodo anterior (apuntado por ptr) al nodo a insertar (nuevonodo), y pueda también enlazar el campo siguiente de nuevonodo, haciendo que apunte al nodo apuntado por el campo siguiente de ptr. while (ptr->sig!= NULL) && (nuevonodo->c > ptr->sig->c) { ptr = ptr->sig; nuevonodo->sig = ptr->sig; ptr->sig = nuevonodo; Gráficamente, quedaría del modo siguiente: void insertarordenado(tlista &primero, char c) { // Esta función inserta un nodo en la lista de forma ordenada // declaración variables TLista nuevonodo, ptr; nuevonodo = new TNodo; if (nuevonodo == NULL) { cout << No hay memoria suficiente << endl; Universidad Surcolombiana Pag 19 de 40
20 else { nuevonodo->c = c; nuevonodo->sig = NULL; if (primero == NULL) { /* La lista estaba vacía y nuevonodo se convierte en el primer y único elemento de ésta */ primero = nuevonodo; else { if (nuevonodo->c <= primero->c) { /* El elemento a insertar es menor que el primer elemento De la lista*/ nuevonodo->sig = primero; primero = nuevonodo; else { ptr = primero; while ((ptr->sig = NULL) && (nuevonodo->c > ptr->sig->c)) { ptr = ptr->sig; nuevonodo->sig = ptr->sig; ptr->sig = nuevonodo; Borrar un nodo de la lista enlazada En cuanto a la tarea de borrar un nodo en una lista enlazada, también hay dos posibilidades: 1) Borrar el primer nodo de la lista enlazada 2) Borrar un determinado nodo de la lista enlazada (por ejemplo, para mantener la lista ordenada). 1) Borrar el primer nodo de la lista enlazada Cuando queremos borrar el primer nodo de la lista enlazada: 1. Necesitamos un puntero auxiliar punt que me apunte al nodo a borrar, que en este caso será el primero. punt = primero; 2. Actualizar el valor de la variable puntero primero, de modo que apunte al siguiente elemento de la lista, pues el primero será eliminado. Universidad Surcolombiana Pag 20 de 40
21 primero = primero->sig; 3. Liberar la memoria ocupada por la variable anónima a la que apunta la variable puntero punt, que es el nodo que queremos eliminar. delete punt; Gráficamente, quedaría del modo siguiente: void borrarfrente(tlista &primero) { // Este procedimiento borra el primer nodo de la lista // declaración variables TLista punt; if (primero = NULL) { punt = primero; primero = primero->sig; delete punt; 2) Borrar un nodo de una posición determinada de una lista Para borrar un determinado nodo de una lista enlazada, necesitamos dos punteros auxiliares, uno que me apunte al nodo a borrar, puntero que llamaremos act; y otro que me apunte al nodo anterior al que deseamos borrar, de modo que podamos enlazar este con el nodo siguiente al borrado, puntero al que llamaremos ant. Si quisiéramos borrar el segundo nodo (nodo que contiene b en la figura) de una lista ordenada cuyo primer elemento viene apuntado por primero, tendríamos lo siguiente: void borrarordenado(tlista &primero, char c) Universidad Surcolombiana Pag 21 de 40
22 { /* Este procedimiento borra un determinado elemento de la lista*/ // declaración variables TLista act,ant; if (primero!= NULL) { ant = NULL; act = primero; while (act->c = c) { ant = act; act = act->sig; if (ant == NULL) { primero = primero->sig; else { ant->sig = act->sig; delete act; Escribir un programa para el mantenimiento de notas usando listas simplemente enlazadas. struct nodo { int nota; struct nodo *siguiente; cabeza; int ingreso (int n) { struct nodo *nuevo; if (!(nuevo=new struct nodo)) return 0; nuevo->nota=n; if (cabeza) nuevo->siguiente=cabeza; else nuevo->siguiente=null; cabeza->nuevo; return 1; void desplegar (); { struct nodo *actual; actual=cabeza; while (actual) { cout << actual->nota << \n ; actual=actual->siguiente; void desplegar_rec (struct nodo *p) { if (p)cout << p->nota << '\n' desplegar_rec (p->siguiente); void eliminar (struct nodo *p) { if (p) Universidad Surcolombiana Pag 22 de 40
23 { eliminar (p->siguiente); delete p; Otras clases de listas enlazadas Otras clases de listas enlazadas son las listas doblemente enlazadas. Las listas doblemente enlazadas consisten en datos, más un enlace al elemento siguiente y otro enlace al elemento anterior. Figura (a)formato del nodo de listas doblemente encadenadas EI, Enlace izquierdo; ED, Enlace derecho; INFO, la información que almacena el nodo. (b)cola doblemente encadenada abierta. Disponer de dos enlaces en lugar de sólo uno tiene varias ventajas: O La lista puede recorrerse en cualquier dirección. Esto simplifica la gestión de la lista, facilitando las inserciones y las eliminaciones. O Más tolerante a fallos. Se puede recorrer la lista tanto con los enlaces hacia delante como con los enlaces hacia atrás, así si se invalida algún enlace se puede reconstruir la lista utilizando el otro enlace. La forma de construir una lista doblemente enlazada es similar a la de una lista simplemente enlazada excepto en que hay que mantener dos enlaces. Por tanto, el registro tiene que contener esos dos enlaces. Usando el ejemplo anterior modificamos el registro y añadimos el nuevo enlace. struct Nodo { char c; Nodo* sig; Nodo* ant; ; typedef Nodo *TListaDobleEnlazada; Las operaciones básicas son las mismas que en las listas simplemente enlazadas: 1) Creación una la lista doblemente enlazada 2) Insertar un nodo en la lista doblemente enlazada Universidad Surcolombiana Pag 23 de 40
24 3) Borrar un nodo de la lista doblemente enlazada Creación de una lista doblemente enlazada Una lista doblemente enlazada se crea de la misma forma que una lista enlazada, inicializando un puntero del tipo TListaDobleEnlazada a NULL. TListaDobleEnlazada crearlistadobleenlazada() { return NULL; Insertar un nodo en la lista doblemente enlazada La tarea de insertar un nodo en una lista doblemente enlazada podemos separarla en dos casos: 1) Insertar un nodo al comienzo de la lista 2) Insertar un nodo después de un determinado nodo existente (por ejemplo, para mantener una lista ordenada). 1) Insertar un nodo al comienzo de la lista doblemente enlazada El procedimiento para insertar un nodo al comienzo de una lista doblemente enlazada (suponemos que anteriormente hemos creado una lista, cuyo primer nodo está apuntado por el puntero primero), se muestra a continuación: void insertarfrentedobleenlazada(tlistadobleenlazada &primero, char c) { // Esta función inserta un nodo al principio de la lista // declaración variables TListaDobleEnlazada punt; punt = new Nodo; if (punt == NULL) { cout << No hay memoria suficiente << endl; else { punt->c = c; punt->sig = NULL; punt->ant = NULL; if (primero!= NULL) { punt->sig = primero; primero->ant = punt; primero = punt; 2) Insertar un nodo en una posición determinada de una lista doblemente enlazada Universidad Surcolombiana Pag 24 de 40
25 Para insertar un nodo en una lista enlazada ordenada tenemos que buscar primero la posición donde hay que insertar el elemento. Un procedimiento para insertar un nodo en una posición determinada de una lista doblemente enlazada es el siguiente: void insertarordenadodobleenlazada(tlistadobleenlazada &primero, char c) { // Inserta un nodo en la lista doblemente enlazada ordenada TListaDobleEnlazada nuevonodo, ptr, ant; nuevonodo = new Nodo; if (nuevonodo == NULL) { cout << No hay memoria suficiente << endl; else { nuevonodo->c = c; nuevonodo->sig = NULL; nuevonodo->ant = NULL; if (primero == NULL) { // La lista estaba vacía primero = nuevonodo; else { if (nuevonodo->c <= primero->c) { /* c es menor que el primer elemento de la lista */ nuevonodo->sig = primero; primero->ant = nuevonodo; primero = nuevonodo; else { ant = primero; ptr = primero->sig; while ((ptr = NULL) && (nuevonodo->c > ptr->c)) { ant = ptr; ptr = ptr->sig; if (ptr == NULL) { /* c se inserta al final de la lista */ nuevonodo->ant = ant; ant->sig = nuevonodo; else { /* c se inserta en medio de la lista */ nuevonodo->sig = ptr; nuevonodo->ant = ant; ant->sig = nuevonodo; ptr->ant = nuevonodo; Borrar un nodo de la lista doblemente enlazada Universidad Surcolombiana Pag 25 de 40
26 En cuanto a la tarea de borrar un nodo en una lista enlazada, también hay dos posibilidades: 1) Borrar el primer nodo de la lista enlazada 2) Borrar un determinado nodo de la lista enlazada (por ejemplo, para mantener la lista ordenada). 1) Borrar el primer nodo de la lista doblemente enlazada void borrarfrentedobleenlazada(tlistadobleenlazada &primero) { // Este procedimiento borra el primer nodo de la lista // declaración variables TListaDobleEnlazada punt; if (primero = NULL) { punt = primero; primero = primero->sig; delete punt; 2) Borrar un nodo de una posición determinada de una lista doblemente enlazada Para borrar un determinado nodo de una lista enlazada, necesitamos dos punteros auxiliares, uno que me apunte al nodo a borrar, puntero que llamaremos act; y otro que me apunte al nodo anterior al que deseamos borrar, de modo que podamos enlazar este con el nodo siguiente al borrado, puntero al que llamaremos ant. void borrarordenadodobleenlazada(tlistadobleenlazada &primero,char c) { /* Este procedimiento borra un determinado elemento de la lista*/ // declaración variables TListaDobleEnlazada act,ant; ant = NULL; act = primero; while ((ptr!= NULL) && (ptr->c!= c)) { ant = ptr; ptr = ptr->sig; if (ptr!= NULL) { /* Se ha encontrado el elemento */ if (ant == NULL) { /* El elemento a borrar es el primero de la lista */ primero = primero->sig; if (primero!= NULL) { primero->ant = NULL; else if (ptr->sig == NULL) { /* El elemento a borrar es el ultimo de la lista */ ant->sig = NULL; else { /*El elemento a borrar esta en el medio de la lista */ Universidad Surcolombiana Pag 26 de 40
27 ant->sig = ptr->sig; ptr->sig->ant = ant; delete ptr; Ejercicios 1.- Dadas las siguientes declaraciones: struct Node { int info; Node *next; ; typedef Node *TPNode; TPNode p, q; Muestra cual es la salida de los siguientes segmentos de código: (a) q = new Node; p = new Node; P->info = 5; Q->info = 6; p = q; P->info = 1; cout << p->info; cout << q->info; (b) p = new Node; P->info = 3; q = new Node; q->info = 2; p = new Node; p->info = q->info; q->info = 0; cout << p->info; cout << q->info; (c) p = new Node; q = new Node; p->info = 0; p->next = q; q->next = NULL; q->info = 5; p->next->next = p; q->info = q->next->info; p = q; p->next->info = 2; cout << p->info; cout << q->info; 2.- Muestra el efecto de las siguientes sentencias sobre la siguiente lista enlazada. Asume que cada sentencia es ejecutada de forma aislada. Universidad Surcolombiana Pag 27 de 40
28 Donde: struct rec { int data; rec *next; ; typedef rec *Tprec; TPrec A, B, C; (a) A = A->next; (b) A = A->next->next; (c) C->next = A; (d) C->next = A->next; (e) A->data = 0; (f) B->data = A->next->data (g) B->data = A->next->next->data 3. Haz un programa que implemente los siguientes procedimientos y funciones para el manejo de Listas Enlazadas. Para probarlas el programa principal tendrá un menú donde cada una de las operaciones sea una opción del menú. TipoLista Crear(); (* crea una lista enlazada vacía*) bool Lista_Vacia(TipoLista lista); (* devuelve TRUE si la lista enlazada que se la pasas como parámetro está vacía, en caso contrario devuelve FALSE*) void Imprimir(TipoLista lista); (*Imprime el contenido de la lista que se la pasa como parámetro*) void Insertar_Frente(TipoLista &lista, TipoElem elem); (*Inserta en la cabeza de la lista el elemento que se le pasa como parámetro*); void Eliminar_Frente(TipoLista &lista, TipoElem elem); (* Elimina el primer elemento de la lista y devuelve su contenido en el parámetro elem*) void Insertar_ordenado(TipoLista &lista, TipoElem elem); (*Inserta en la lista (ordenada) el elemento elem de forma ordenada*) void Eliminar_Ordenado(TipoLista &lista, TipoElem elem); (*Elimina el elemento elem de la lista, si esta en ella, suponiendo que dicha lista esta ordenada*) Donde: typedef unsigned int TipoElem; struct TipoLista { TipoElem c; TipoLista *p; Universidad Surcolombiana Pag 28 de 40
29 ; typedef TipoLista *TPTipoLista; 4.- Diseña un Algoritmo que tenga como entrada dos matrices cuadradas y devuelva la suma de ambas. Estas matrices tienen la peculiaridad de que algunas de sus filas tienen todos sus componentes a 0; por esta razón estas matrices se podrían representar de una forma más eficiente, que como las habíamos venido representando hasta el momento (arrays bidimensionales), como un array de punteros a arrays de cardinales: Ejemplo, la matriz cuadrada siguiente (5X5): Vendrá representada como: Diseña el programa utilizando la representación anterior. 5.- Se conoce que en C++ (32 bits) el tipo de dato entero unsigned int, sólo toma valores enteros positivos, con un valor máximo de 4.294` (32 bits unos). El objetivo de este ejercicio es ser capaces de manipular números cardinales incluso mayores que el anterior. Con este fin se diseña un programa que permita manejar enteros largos. Estos enteros largos serán implementados mediante una lista enlazada con los dígitos que forman el número. De modo, que 42 se almacenaría como: Universidad Surcolombiana Pag 29 de 40
30 Este programa debe contener un menú que me permita llevar a cabo las siguientes operaciones: suma, resta y multiplicación de dos enteros largos, además de las de entrada/salida (lectura de un entero largo y escritura de un entero largo). 6.- Diseña un algoritmo para un sistema de reserva de vuelos de un aeropuerto. El algoritmo ha de ejecutar un menú que permita llevar a cabo las operaciones siguientes: Añadir un vuelo (dado un número de vuelo y un número de asientos libres) al sistema. Eliminar un vuelo del sistema. Hallar cuantos asientos libres hay para un vuelo dado. Añadir una persona a un vuelo dado. Eliminar a una persona de un vuelo dado. Listar por pantalla los datos de las personas (nombre, edad, DNI) que tienen reservas para un vuelo dado. El algoritmo debería representar el sistema de reservas mediante una lista enlazada de vuelos. El número de vuelo (ej. BA5941), el número de asientos disponibles y una lista, también enlazada, de pasajeros deberían ser almacenados para cada vuelo. Cada nodo de la lista enlazada de pasajeros contendrá toda la información relacionada con una persona: nombre, edad y DNI. Apéndice. Manejo Tradicional de Memoria en C. Además de los mostrado en el tema, existen otras funciones y otras posibilidades de manejo de memoria dinámica y de uso de punteros que si bien no son pedagógicamente las más adecuadas, se muestran en este apéndice para que sirva como material de consulta para el alumno en caso de necesidad o de que desea ampliar sus conocimientos sobre el lenguaje. Otras funciones de reserva de memoria de forma dinámica. Otra posibilidad existente a la hora de reservar memoria dinámicamente es el uso de la función malloc ó calloc. Antes de indicar como deben utilizarse las funciones se tiene que comentar el operador sizeof. Este operador es imprescindible a la hora de realizar programas Universidad Surcolombiana Pag 30 de 40
31 portables, es decir, programas que puedan ejecutarse en cualquier máquina que disponga de un compilador de C. El operador sizeof(tipo_de_dato), devuelve el tamaño que ocupa en memoria un cierto tipo de dato, de esta manera, se puede escribir programas independientes del tamaño de los datos y de la longitud de palabra de la máquina. En resumen si no se utiliza este operador en conjunción con las conversiones de tipo cast probablemente nuestro programa sólo funcione en el ordenador sobre el que lo ha programado. Los prototipos de estas funciones son: void *malloc(size_t número_de_bytes); void *calloc(size_t número_de_elementos, size_t número_de_bytes); Aquí, número_de_bytes es el número de bytes de memoria que se quieren asignar. El tipo size_t está definido en <cstdlib> como un tipo entero sin signo. void *calloc(size_tnobj, size_tsize) calloc obtiene (reserva) espacio en memoria para alojar un vector (una colección) de nobj objetos, cada uno de ellos de tamaño size. Si no hay memoria disponible se devuelve NULL. El espacio reservado se inicializa a bytes de ceros. Obsérvese que calloc devuelve un (void *) y que para asignar la memoria que devuelve a un tipo Tipo_t hay que utilizar un operador de ahormado: (Tipo_T*) Ejemplo: char *c; c=(char *) calloc (40, sizeof(char)); void *malloc(size_tsize) Es necesario saber el tamaño exacto de las posiciones de memoria solicitadas. El ejemplo anterior se puede reescribir: char *c; c=(char *) malloc (40*sizeof(char)); char *cpt;... if ((cpt = (char *) malloc(25)) == NULL) { printf("error on malloc\n"); Universidad Surcolombiana Pag 31 de 40
32 En resumen la función malloc ó calloc() devuelve un puntero de tipo void *, lo que significa que se puede asignar a cualquier tipo de puntero. Se tiene que convertir de forma explícita el resultado al tipo de puntero para el cual se está reservando memoria. En la línea de código siguiente se indica con (char *)que el puntero p es un puntero a char. Tras una llamada con éxito a, malloc ó calloc() devuelve un puntero al primer byte de la región de memoria dispuesta del montón. Si no hay suficiente memoria libre para satisfacer la petición de malloc/calloc(), se produce un fallo de asignación y malloc ó calloc() devuelve un NULL. char *p; p = (char *) malloc(1000); /*reserva bytes */ Para terminar un breve comentario sobre las funciones anteriormente descritas. Básicamente da lo mismo utilizar malloc y calloc para reservar memoria es equivalente: mat = (float *)calloc (20,sizeof(float)); mat = (float *)malloc (20*sizeof(float)); La diferencia fundamental es que, a la hora de definir matrices dinámicas calloc es mucho más claro y además inicializa todos los elementos de la matriz a cero. Nótese también que puesto que las matrices se referencian como un puntero la asignación dinámica de una matriz nos permite acceder a sus elementos con instrucciones de la forma: NOTA: En realidad existen algunas diferencias al trabajar sobre máquinas con alineamiento de palabras. void *realloc(void *p, size_tsize) realloc cambia el tamaño del objeto al que apunta p y lo hace de tamaño size. El contenido de la memoria no cambiará en las posiciones ya ocupadas. Si el nuevo tamaño es mayor ue el antiguo, no se inicializan a ningún valor las nuevas posiciones. En el caso en que no hubiese suficiente memoria para realojar al nuevo puntero, se devuelve NULL y p no varía. El puntero que se pasa como argumento ha de ser NULL o bien un puntero devuelto por malloc(), calloc() o realloc(). Para la reserva de memoria dinámica en C, es muy útil la utilización de la función sizeof(). int *p; /* reserva espacio para un valor de tipo entero (int)*/ p = (int *) malloc(sizeof(int)); Universidad Surcolombiana Pag 32 de 40
33 NOTA: Como el montón (heap) no es infinito, siempre que se reserve memoria con malloc() o calloc debe comprobarse, antes de usar el puntero, el valor devuelto para asegurarse que no es NULL. char *p; p = malloc(sizeof(char)); if (p == NULL) { // No se ha podido reservar la memoria deseada // Tratar error Función de liberación de memoria dinámica: free(). Cuando se usa memoria de manera dinámica, es necesario que el programador libere la memoria luego de utilizarla, esto se hace usando la función de librería stándard free: free() libera el espacio de memoria al que apunta p. Si p es NULL no hace nada. Además p tiene que haber sido alojado previamente mediante malloc(), calloc() o realloc(). La función free() es la complementaria de malloc ó calloc(). Una vez que la memoria ha sido liberada, puede ser reutilizada en una posterior llamada por malloc ó calloc(). El prototipo de la función free() es: void free(void *p); Aquí, p es un puntero a memoria que ha sido previamente asignado con malloó calloc() o new. Por ejemplo: char *p; p = (char *) malloc(sizeof(char)); free(p); //reserva memoria para un carácter //libera la memoria previamente reservada NOTA: Es muy importante no llamar NUNCA a free() con un argumento no válido; se dañará el sistema de asignación. NOTA: La función free(p) NO asignan el valor nulo al puntero p después de liberar la memoria a la que apuntaba. NOTA: Usar siempre free() para liberar la memoria si se usó malloc ó calloc() para reservarla. Y, usar siempre delete para liberar la memoria si se usó new para reservarla. Universidad Surcolombiana Pag 33 de 40
34 Creación Dinámica de Arrays y Aritmética de Punteros. En C/C++ los punteros y los arrays están muy relacionados. En realidad, un nombre de array es un puntero al primer elemento del array, ya que la forma de implementar los arrays es reservar un bloque de memoria de tamaño: <dimensión>*sizeof(<tipo_base>). Tras lo visto con anterioridad en el tema no hay ningún problema en que esa reserva de memoria se haga en tiempo de compilación (caso habitual de la declaración estática de un array) o de forma dinámica (declarando sólo el puntero a la primera posición y reservando la memoria en tiempo de ejecución). Esto nos permitiría declarar un array de tamaño decidible en tiempo de ejecución pero nos obliga a gestionar la reserva/liberación de esa memoria. Por ejemplo, el siguiente fragmento de código es válido en C++: char cad[80]; // Declaración de un array de 80 caracteres char *p; // Declaración de un puntero a char p = cad; // El puntero p apunta a la primera posición del array Aquí a p se le asigna la dirección del primer elemento del array cad, con lo que se puede decir que el puntero p apunta a un array de 80 caracteres. Esta característica de los arrays hace que el paso de arrays como argumentos de una función sea una excepción al convenio de llamada por valor estándar 2. Cuando se usa un array como argumento de un procedimiento o función, sólo se pasa a la función la dirección del array, indicando su nombre. Por ejemplo, la función imprimir_mayúsculas(), que imprime su argumento de tipo cadena en mayúsculas se declara como: typedef char *TPCadena; void imprimir_mayúsculas(tpcadena cadena); Y se invocaría de la siguiente forma: imprimir_mayúsculas(c); Donde c es el nombre de un array declarado de forma estática: char c[80]; En este caso, el código que hay dentro del procedimiento o función opera sobre el contenido real del arreglo (array), pudiendo alterarlo. El procedimiento imprimir_mayúsculas() modifica el contenido del array cadena, de forma que después 2 El paso de argumentos por valor y por referencia está explicado en el Tema4. Procedimientos y Funciones Universidad Surcolombiana Pag 34 de 40
35 de hacer una llamada a este procedimiento, la cadena estará también en mayúsculas en el programa principal. #include <iostream> #include <cstdlib> using namespace std; const char FINCAD = char(0); const char MAXCAD = 80; const int ENTER = '\n'; typedef char *TPCadena; typedef char TCadena[MAXCAD+1]; void imprimir_mayusculas(tpcadena cadena); void main() { TCadena c; cout << Introduce una cadena: << endl; cin.getline(c, MAXCAD, ENTER); imprimir_mayusculas(c); //El valor de c se ha modificado cout << endl << c esta ahora en mayusculas: << c << endl; system("pause"); return 0; void imprimir_mayusculas(tpcadena cadena) { //Imprime una cadena en mayúsculas int i; i=0; while (cadena[i]!= FINCAD) { cadena[i] = toupper(cadena[i]); //Modifica el valor de cadena cout << cadena[i]; i++; En este ejemplo, si no quisiera que el contenido de la cadena se cambiara a mayúsculas en el programa principal tendría que implementar el procedimiento imprimir_mayúsculas() de la siguiente forma, donde el contenido del array no se modifica dentro del procedimiento: void imprimir_mayusculas(tpcadena cadena) { //Imprime una cadena en mayúsculas int i; i=0; while (cadena[i]!= FINCAD) { cout << toupper(cadena[i]); i++; En la situación anterior el array se declara de forma estática asignándole una longitud fija, aún cuando consideremos el nombre del array como un puntero a dicho array. Sin embargo, hay ocasiones en las que se quiere trabajar con un array, pero no sabemos cual será el tamaño del array que vamos a necesitar. En este caso es posible usar la función new para crear un array de forma dinámica. Universidad Surcolombiana Pag 35 de 40
36 Para asignar un array de forma dinámica, primero hay que declarar un puntero que su tipo sea el tipo de los elementos que queremos almacenar en el array. <nombre_tipo> *<var_puntero>; //declaramos un puntero a nombre_tipo Posteriormente tenemos que usar la función de asignación dinámica new para reservar el espacio de memoria necesario indicando el número de datos de dicho tipo que quiere almacenar en el array. <var_puntero> = new <nombre_tipo>[<tamaño>]; Para liberar la memoria asignada a un array de forma dinámica, no es necesario especificar el tamaño del array, sólo los corchetes []. delete []<var_puntero>; Por ejemplo, si se quiere crear un array de 80 char: char *c; // puntero a char c = new char[80]; // reserva memoria para 80 caracteres El siguiente programa muestra cómo se puede usar un array asignado dinámicamente para mantener un array unidimensional; una cadena en este caso. #include <iostream> #include <cstdlib> using namespace std; const char FINCAD = char(0); const char MAXCAD = 80; const int ENTER = '\n'; /*Se define el TPCadena como puntero a un número variable de caracteres*/ typedef char *TPCadena; int LongitudCadena(TPCadena cadena); int main() { char *c; int i; // Reserva memoria para almacenar una cadena de caracteres c = new char[maxcad+1]; if (c == NULL) { cout << No hay memoria suficiente << endl; else { // Lee desde teclado la cadena de caracteres cin.getline(c, MAXCAD, ENTER); // Imprime la cadena al revés for (i=longitudcadena(c)-1; i>=0; i--) { cout << c[i]; delete []c; system ("PAUSE"); Universidad Surcolombiana Pag 36 de 40
37 return 0; int LongitudCadena(TPCadena cadena) { int i; i=0; while ((i<maxcad)3 3 && (cadena[i]!=fincad) ) { ++i; return i; Aun más, si piensa en la implementación de los arrays en C/C++ y en que un puntero no es más que una dirección de memoria. Qué ocurre si se incrementa o decrementa un puntero? La respuesta es sencilla, se va a la siguiente o anterior posición de memoria. Qué ocurre si se hace eso en un array? Pues apuntaremos al elemento siguienteo anterior. Los programadotes clásicos de C usan/usaban estas características para desplazarse por la memoria de un programa intentando aumentar su eficiencia temporal (rapidez) pero quedando códigos de tan poco aconsejable aspecto como el siguiente: void imprimir_mayusculas(tpcadena cadena) { while (cadena= NULL) { cout << toupper(*cadena); ++cadena; Suponga ahora que se desea programar una serie de funciones para trabajar con matrices. Una primera solución sería definir una estructura de datos matriz, compuesta por una matriz y sus dimensiones puesto que interesa que las funciones trabajen con matrices de cualquier tamaño. Por tanto la matriz dentro de la estructura tendrá el tamaño máximo de todas las matrices con las que se desea trabajar y como se tiene almacenadas las dimensiones se trabaja con una matriz de cualquier tamaño pero tendrá reservada memoria para una matriz de tamaño máximo. Se desperdicia memoria. Una definición de este tipo sería: typedef struct { float mat[1000][1000]; int ancho,alto; MATRIZ; En principio esta es la única forma de definir el tipo de datos. Con esta definición una matriz 3x3 ocupará 1000x1000 floats al igual que una matriz 50x50. 3 Si bien esta comparación no es necesaria, es conveniente su uso para evitar errores producidos por datos "corruptos". Universidad Surcolombiana Pag 37 de 40
38 Sin embargo se puede asignar memoria dinámicamente a la matriz y reservar sólo el tamaño que se requiera. La estructura sería ésta. struct mat { float *datos; int ancho,alto; ; typedef struct mat *MATRIZ; El tipo MATRIZ ahora debe ser un puntero puesto que se tiene que reservar memoria para la estructura que contiene la matriz en sí y las dimensiones. Una función que pueda crear una matriz sería así: MATRIZ inic_matriz (int x,int y) { MATRIZ temp; temp = (MATRIZ) malloc (sizeof(struct mat)); temp->ancho = x; temp->alto = y; temp->datos = (float *) malloc (sizeof(float)*x*y); return temp; En esta función se ha obviado el código que comprueba si la asignación ha sido correcta. Observe como se organizan los datos con estas estructuras. temp >datos >x*x elementos ancho,alto Esta estructura puede parecer en principio demasiado compleja, pero verá que es muy útil en el encapsulado de los datos. En el programa principal, para utilizar la matriz declararía algo así: main() { MATRIZ a; a = inic_matriz (3,3);... borrar_matriz(a); Dónde borrar_matriz(a) libera la memoria reservada en inic_matriz, teniendo en cuenta que se realizaron dos asignaciones, una para la estructura mat y otra para la matriz en sí. Universidad Surcolombiana Pag 38 de 40
39 Otra definición posible del problema podría ser así. typedef struct mat MATRIZ; void inic_matriz (MATRIZ *p,int x,int y) { p->ancho = x; p->alto = y; p->datos = (float *)malloc(sizeof(float)*x*y); Con este esquema el programa principal sería algo como esto: main() { MATRIZ a; inic_matriz (&a,3,3);... borrar_matriz (&a); Con este esquema el acceso a la matriz sería *(a.datos+x+y*a.ancho), idéntico al anterior sustituyendo los puntos por flechas ->. En el siguiente capítulo se justificará la utilización de la primera forma de implementación frente a esta segunda. En realidad se trata de la misma implementación salvo que en la primera el tipo MATRIZ es un puntero a una estructura, por tanto es necesario primero reservar memoria para poder utilizar la estructura, mientras que en la segunda implementación, el tipo MATRIZ es ya en si la estructura, con lo cual el compilador ya reserva la memoria necesaria. En el primer ejemplo MATRIZ a; define a como un puntero a una estructura struct mat, mientras que en la segunda MATRIZ a; define a como una variable cuyo tipo es struct mat. Resumen int *p; int *p[10]; int (*p)[10]; int *p(void); int p(char *a); int *p(char *a); int (*p)(char *a); p es un puntero a un entero p es un array de 10 punteros a enteros p es un puntero a un array de 10 enteros p es una función que devuelve un puntero a entero p es una función que acepta un argumento que es un puntero a carácter, devuelve un entero p es una función que acepta un argumento que es un puntero a carácter, devuelve un puntero a entero p es un puntero a función que acepta un argumento que es un puntero a carácter, devuelve un puntero a entero Universidad Surcolombiana Pag 39 de 40
40 int (*p(char *a))[10]; int p(char (*a)[]); int p(char *a[]); int *p(char a[]); int *p(char (*a)[]); int *p(char *a[]); int (*p)(char (*a)[]); int *(*p)(char (*a)[]); int *(*p)(char *a[]); int (*p[10])(void); int (*p[10])(char *a); int *(*p[10])(char a); char *(*p[10])(char *a); p es una función que acepta un argumento que es un puntero a carácter, devuelve un puntero a un array de 10 enteros p es un puntero a función que acepta un argumento que es un puntero a un array de caracteres, devuelve un puntero a entero p es un puntero a función que acepta un argumento que es un array de punteros a caracteres, devuelve un puntero a entero p es una función que acepta un argumento que es un array de caracteres, devuelve un puntero a entero p es una función que acepta un argumento que es un puntero a un array de caracteres, devuelve un puntero a entero p es una función que acepta un argumento que es un puntero a un array de punteros a caracteres, devuelve un puntero a entero p es una función que acepta un argumento que es un puntero a un array de caracteres, devuelve un puntero a entero p es un puntero a una función que acepta un argumento que es un puntero a un array de punteros a caracteres, devuelve un puntero a entero p es un puntero a una función que acepta un argumento que es un array de punteros a caracteres, devuelve un puntero a entero p es una array de 10 punteros a función, cada función devuelve un entero p es una array de 10 punteros a función; cada función acepta un argumento que es un puntero a carácter y devuelve un entero. p es una array de 10 punteros a función; cada función acepta un argumento que es un carácter, y devuelve un puntero a entero. p es una array de 10 punteros a función; cada función acepta un argumento que es un carácter, y devuelve un puntero a caracter. Universidad Surcolombiana Pag 40 de 40
UNIVERSIDAD DE LOS ANDES NUCLEO UNIVERSITARIO RAFAEL RANGEL (NURR) DEPARTAMENTO DE FISICA Y MATEMATICA AREA COMPUTACION TRUJILLO EDO.
UNIVERSIDAD DE LOS ANDES NUCLEO UNIVERSITARIO RAFAEL RANGEL (NURR) DEPARTAMENTO DE FISICA Y MATEMATICA AREA COMPUTACION TRUJILLO EDO. TRUJILLO 1.- Resumen de Prog1 Comentarios es C++ Declaraciones de variables.
Laboratorio de Arquitectura de Redes. Punteros en lenguaje C
Laboratorio de Arquitectura de Redes Punteros en lenguaje C Punteros en lenguaje C Definición Declaración e inicialización de punteros Operadores de punteros: «*» y «&» Operaciones con punteros Operaciones
Apuntadores en C y C++
Apuntadores en C y C++ Universidad de Carabobo Facultad Experimental de Ciencias y Tecnología Prof. Marcos A. Gil T. 8 de diciembre de 2004 1. Introducción Los apuntadores en C y C++ son una herramienta
Elementos de un programa en C
Elementos de un programa en C Un programa en C consta de uno o más archivos. Un archivo es traducido en diferentes fases. La primera fase es el preprocesado, que realiza la inclusión de archivos y la sustitución
Memoria Dinámica. Jornadas de Marzo 2010 Grupo de Usuarios de Linux Tania Pérez
Jornadas de Marzo 2010 Grupo de Usuarios de Linux Tania Pérez 1. PUNTEROS 2. MEMORIA DINÁMICA 2 1. PUNTEROS 3 Qué es un puntero? Un tipo de variable cuyo valor es la dirección de memoria de otra variable.
Todo programa en 'C' consta de una o más funciones, una de las cuales se llama main.
LENGUAJE C CARACTERISTICAS DEL LENGUAJE 'C' El lenguaje 'C' se conoce como un lenguaje compilado. Existen dos tipos de lenguaje: interpretados y compilados. Los interpretados son aquellos que necesitan
TEMA 7: Ficheros. TEMA 7: Ficheros. 7.1.-Concepto de fichero
TEMA 7: Ficheros 7.1.-Concepto de fichero Todas las estructuras de datos que hemos visto hasta ahora utilizan memoria principal. Esto tiene dos limitaciones importantes: 1. Los datos desaparecen cuando
Desde los programas más simples escritos en un lenguaje de programación suelen realizar tres tareas en forma secuencial.
Tipos de Datos Desde los programas más simples escritos en un lenguaje de programación suelen realizar tres tareas en forma secuencial. Entrada de datos Procesamientos de datos Salida de resultados Los
Tema 6. Gestión dinámica de memoria
Tema 6. Gestión dinámica de memoria http://aulavirtual.uji.es José M. Badía, Begoña Martínez, Antonio Morales y José M. Sanchiz {badia, bmartine, morales, [email protected] Estructuras de datos y de la
Tema 2. El lenguaje JAVA
Tema 2. El lenguaje JAVA Nomenclatura habitual Variables Tipos de variables Tipos primitivos Referencias Arrays Operadores Operadores de Java Precedencia de operadores Sentencias de control Sentencias
2.2 Nombres, Ligado y Ámbito
2.2 Nombres, Ligado y Ámbito Ligado estático y dinámico, reglas de ámbito y prueba de tipos. Conceptos Nombres e Identificadores Variables Tipos Ámbito Constantes Nombres Identificador que designa en el
Apuntadores (Punteros)
Apuntadores (Punteros) x9ff10 X int 209 SESION 7 *ptr Definición 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
Relación de prácticas de la asignatura METODOLOGÍA DE LA PROGRAMACIÓN Segundo Cuatrimestre Curso º Grado en Informática
Relación de prácticas de la asignatura METODOLOGÍA DE LA PROGRAMACIÓN Segundo Cuatrimestre Curso 2013-2014. 1º Grado en Informática Práctica 2: Memoria dinámica y Bibliotecas Objetivos Practicar conceptos
6.1.- Introducción a las estructuras de datos Tipos de datos Arrays unidimensionales: los vectores Operaciones con vectores.
TEMA 6: ESTRUCTURAS DE DATOS (Arrays). CONTENIDO: 6.1.- Introducción a las estructuras de datos. 6.1.1.- Tipos de datos. 6.2.- Arrays unidimensionales: los vectores. 6.3.- Operaciones con vectores. 6.4.-
INTRODUCCIóN A LA PROGRAMACIóN APUNTES DE JAVA APUNTES DE JAVA
APUNTES DE JAVA FUNCIONAMIENTO DE UN PROGRAMA Assembler Ensamblador Ejecuta Programador Programa fuente BASIC Interprete Ejecuta C, C++, Pascal Compilador Compila Ejecuta Programa fuente Programa Objeto
Práctica 3. Paso de parámetros entre subrutinas. 3. Consideraciones sobre el paso de parámetros
Práctica 3. Paso de parámetros entre subrutinas 1. Objetivo de la práctica El objetivo de esta práctica es que el estudiante se familiarice con la programación en ensamblador y el convenio de paso de parámetros
Tema 2. Memoria Dinámica. 2.1 Datos estáticos y dinámicos
Tema 2 Memoria Dinámica 2.1 Datos estáticos y dinámicos Datos estáticos: su tamaño y forma es constante durante la ejecución de un programa y por tanto se determinan en tiempo de compilación. El ejemplo
Curso de Programación en C. Licenciatura, FCQeI. APUNTADORES.
APUNTADORES. La memoria de una máquina esta ordenada en forma de celdas numeradas consecutivamente y que se pueden manipular individualmente o en grupos contiguos. La siguiente figura muestra una representación
Instituto Tecnológico de Celaya
CADENAS DE CARACTERES Hasta ahora se han manejado variables de tipo caracter cuyo valor es un sólo caracter. En algunos casos, sin embargo, es necesario usar variables cuyos valores sean un conjunto (cadena)
Estructura de Datos: Archivos
Estructura de Datos: Archivos Registros (record) Un registro es una estructura que consiste de un número fijo de componentes llamados campos. Los campos pueden ser de diferentes tipos y deben tener un
Objetivos de la sesión. Aplicación de consola 7/30/11. Código con que se inicia un programa en Visual C# (aplicación de consola)
Objetivos de la sesión Entender el tipo de programas que se pueden realizar con aplicaciones de consola. Conocer el concepto de variable, la forma en que se declaran y su utilización. Conocer la forma
Principios de Computadoras II
Departamento de Ingeniería Electrónica y Computadoras Ing. Ricardo Coppo [email protected] Qué es un Objeto? Un objeto es una instancia de una clase Las clases actuán como modelos que permiten la creación
Algoritmos. Medios de expresión de un algoritmo. Diagrama de flujo
Algoritmos En general, no hay una definición formal de algoritmo. Muchos autores los señalan como listas de instrucciones para resolver un problema abstracto, es decir, que un número finito de pasos convierten
Programación en java. Estructuras algorítmicas
Programación en java Estructuras algorítmicas Estructuras algoritmicas 1. Conceptos basicos 1. Dato 2. Tipos de datos 3. Operadores 2. dsd Conceptos Basicos DATO: se considera una representación simbólica
Tabla de Símbolos. Programación II Margarita Álvarez
Programación II Margarita Álvarez La tabla de símbolos es una estructura global utilizada por distintos módulos del compilador. Es el principal atributo heredado. Almacena todos los nombres declarados
media = ( temp0 + temp1 + temp2 + temp3 + temp temp23 ) / 24; printf( "\nla temperatura media es %f\n", media );
Arrays en el lenguaje C Introducción Una posible definición de array sería: Un conjunto de datos del mismo tipo, identificados por el mismo nombre, y que se pueden distinguir mediante un número de índice.
Clases e instancias. Algoritmos y Estructuras de Datos I. Clases e instancias. memoria dinámica.
Algoritmos Estructuras de Datos I Primer cuatrimestre de 2014 Departamento de Computación - FCEN - UBA Programación imperativa - clase 10 Memoria dinámica listas enlazadas Clases e instancias El paquete
4. Operadores Operador asignación
Programación orientada a objetos con Java 43 4. Operadores Objetivos: a) Describir los operadores (aritméticos, incrementales, de relación, lógicos y de asignación) y los tipos de dato primitivos sobre
Operadores y Expresiones
Operadores y Expresiones Los programas Java constan de datos, sentencias de programas y expresiones. Una expresión es normalmente, una ecuación matemática, tal como 3 * x + 5 * z. En esta expresión, los
ESCUELA POLITÉCNICA SUPERIOR PRÁCTICA 2: EXPRESIONES, PRINTF Y SCANF
ESCUELA POLITÉCNICA SUPERIOR GRADO EN DISEÑO IND. INFORMÁTICA CURSO 2012-13 PRÁCTICA 2: EXPRESIONES, PRINTF Y SCANF HASTA AHORA... En prácticas anteriores se ha aprendido: La estructura principal de un
Curso de Programación Avanzada en C
Curso de Programación Avanzada en C Copyright, 1996 Universidad Sim on Bol ivar 1 Prof. Mariela J. Curiel Contenido del Curso Conceptos BásicosB Estructuras de Control Arreglos Otros tipos de datos derivados
El lenguaje C. 1. Identificadores, constantes y variables
Principios de Programación El lenguaje C 1. Identificadores, constantes y variables 1.1. Conceptos de memoria Los nombres de variable como x, y, suma corresponden a localizaciones o posiciones en la memoria
Operadores. Javier Fernández Rivera -
Programación en C Operadores Javier Fernández Rivera - www.aurea.es Los operadores Son los elementos o caracteres gráficos encargados de manipular los datos, que pueden ser dados por números, caracteres,
Carlos Montenegro. Programación Orientada a Objetos Proyecto Curricular de Ingeniería de Sistemas
2 - Introducción al lenguaje Java, identificadores y comentarios. Carlos Montenegro Programación Orientada a Objetos Proyecto Curricular de Ingeniería de Sistemas 1. Introducción: Java tiene como todos
Java Avanzado. Guía 1. Java Avanzado Facultad de Ingeniería. Escuela de computación.
Java Avanzado. Guía 1 Java Avanzado Facultad de Ingeniería. Escuela de computación. Java Avanzado. Guía 2 Introducción Este manual ha sido elaborado para orientar al estudiante de Java Avanzado en el desarrollo
Programación en Visual Basic Ricardo Rodríguez García
Manual Básico de Programación en Visual Basic 1.- Estructura de un proyecto Visual Basic Los programas o aplicaciones desarrolladas en Visual Basic van a constituir un único paquete que denominaremos proyecto.
Procesadores de lenguaje Tema 6 La tabla de símbolos
Procesadores de lenguaje Tema 6 La tabla de símbolos Departamento de Ciencias de la Computación Universidad de Alcalá Resumen La tabla de símbolos. Requerimientos de información. Diseño de la tabla de
Agradecimientos. Nota de los autores. 1 Problemas, algoritmos y programas 1
Prologo Agradecimientos Nota de los autores Índice general I III V VII 1 Problemas, algoritmos y programas 1 1.1 Programas y la actividad de la programación.................... 4 1.2 Lenguajes y modelos
2. EXPRESIONES 3. OPERADORES Y OPERANDOS 4. INDENTIFICADORES COMO LOCALIDADES DE MEMORIA
CONTENIDOS: 1. TIPOS DE DATOS 2. EXPRESIONES 3. OPERADORES Y OPERANDOS 4. INDENTIICADORES COMO LOCALIDADES DE MEMORIA OBJETIO EDUCACIONAL: El alumno conocerá las reglas para cambiar fórmulas matemáticas
Ficha de Aprendizaje N 13
Ficha de Aprendizaje N 13 Curso: Lógica y lenguaje de programación Tema: Fundamentos de programación Duración: 2 horas pedagógicas Logros de aprendizaje Identifica los diferentes tipos de operadores que
Vamos a profundizar un poco sobre los distintos tipos de datos que podemos introducir en las celdas de una hoja de cálculo
Tipos de datos. Vamos a profundizar un poco sobre los distintos tipos de datos que podemos introducir en las celdas de una hoja de cálculo Valores Constantes: Es un dato que se introduce directamente en
Algoritmos y Programación I
Algoritmos y Programación I ARREGLOS Y ESTRUCTURAS EN C Arreglos Un arreglo o vector es un conjunto de datos del mismo tipo, almacenados de forma contigua (es decir uno al lado del otro) en memoria principal.
Expresiones Aritméticas. Programación digital I Escuela de Sistemas Facultad de Ingeniería Gilberto Diaz
Expresiones Aritméticas Programación digital I Escuela de Sistemas Facultad de Ingeniería Gilberto Diaz Expresiones Aritméticas El computador puede realizar cálculos además de mostrar datos por pantalla.
Las plantillas permiten definir funciones genéricas.
Introducción (Genericidad). Plantillas de funciones o funciones genéricas. Sintaxis de plantillas. Ejemplos de declaraciones. A tener en cuenta Un ejemplo de plantilla de funciones: Máximo de un vector.
Funciones como Subprogramas en C++
FUNCIONES Cuando es necesario escribir programas complicados para resolver problemas complejos, una práctica común entre los programadores es descomponer el algoritmo (el diagrama de flujo) en varias partes.
Definición de Memoria
Arquitectura de Ordenadores Representación de Datos en Memoria Abelardo Pardo [email protected] Universidad Carlos III de Madrid Departamento de Ingeniería Telemática Definición de Memoria DRM-1 La memoria
Anexo. Control de errores
Anexo. Control de errores Tipos de errores Los errores en un programa o algoritmo se pueden clasificar de la siguiente manera Errores de compilación Los errores de compilación no permiten la ejecución
Capítulo. Listas, pilas y colas en C. Contenido. Introducción
Capítulo 32 Listas, pilas y colas en C Contenido Listas enlazadas Clasificación de listas enlazadas Operaciones en listas enlazadas Inserción de un elemento en una lista Búsqueda de un elemento de una
Es toda la información que utiliza el computador. Según sea la información que guardemos en los datos, se clasifican en los siguientes tipos:
Tipos de datos. Dato: Es toda la información que utiliza el computador. Según sea la información que guardemos en los datos, se clasifican en los siguientes tipos: I Numéricos: Almacenan números y con
Sesión No. 10. Contextualización INFORMÁTICA 1. Nombre: Gestor de Base de Datos (Access)
INFORMÁTICA INFORMÁTICA 1 Sesión No. 10 Nombre: Gestor de Base de Datos (Access) Contextualización Microsoft Access es un sistema de gestión de bases de datos, creado para uso personal y de pequeñas organizaciones,
Guía práctica de estudio 05: Diagramas de flujo
Guía práctica de estudio 05: Diagramas de flujo Elaborado por: M.C. Edgar E. García Cano Ing. Jorge A. Solano Gálvez Revisado por: Ing. Laura Sandoval Montaño Guía práctica de estudio 05: Diagramas de
Relación de prácticas de la asignatura METODOLOGÍA DE LA PROGRAMACIÓN Segundo Cuatrimestre Curso º Grado en Informática
Relación de prácticas de la asignatura METODOLOGÍA DE LA PROGRAMACIÓN Segundo Cuatrimestre Curso 2013-2014. 1º Grado en Informática Práctica 1: Punteros Objetivos Se hará hincapié en la aritmética de punteros
FUNCIONES PHP: DECLARACIÓN Y LLAMADAS. PARÁMETROS, RETURN. EJERCICIOS EJEMPLOS RESUELTOS. (CU00827B)
APRENDERAPROGRAMARCOM FUNCIONES PHP: DECLARACIÓN Y LLAMADAS PARÁMETROS, RETURN EJERCICIOS EJEMPLOS RESUELTOS (CU00827B) Sección: Cursos Categoría: Tutorial básico del programador web: PHP desde cero Fecha
PROGRAMACION ORIENTADA A OBJETOS EN C++
PROGRAMACION ORIENTADA A OBJETOS EN C++ 1- INTRODUCCIÓN El lenguaje C++ representa el resultado de los esfuerzos realizados para proporcionar las ventajas de la programación Orientada a Objetos a un lenguaje
Informática I para Bachillerato
CIMAT C++ C/C++ de Datos CIMAT Sesión 15 Una estructura es un grupo de variables las cuales pueden ser de diferentes tipos sostenidas o mantenidas juntas en una sola unidad, a diferencia de los arreglos
Java para programadores
Java para programadores Java y Servicios Web I Master en Ingeniería Matemática Manuel Montenegro Dpto. Sistemas Informáticos y Computación Desp. 467 (Mat) [email protected] Contenidos Variables. Tipos
Unidad Didáctica 2. Elementos básicos del lenguaje Java Tipos, declaraciones, expresiones y asignaciones
Unidad Didáctica 2 Elementos básicos del lenguaje Java Tipos, declaraciones, expresiones y asignaciones Fundamentos de Programación Departamento de Lenguajes y Sistemas Informáticos Versión 1.0.3 Índice
Estatutos de Control C# Estatutos de Decisión (Selección)
SELECCIÓN Estatutos de Control C# Estatutos de Decisión (Selección) IF Condición THEN Estatuto1 ELSE Estatuto2 Estatuto1 Statement Condición... Antes de ver esta presentación: Lee el Capítulo correspondiente
Tema 8 Gestión de la memoria en tiempo de ejecución.
Traductores, Compiladores e Intérpretes 1 Tema 8 Gestión de la memoria en tiempo de ejecución. S Organización de la memoria en tiempo de ejecución. Cuando un programa se ejecuta sobre un sistema operativo
Arrays dinámicos y arrays estáticos con Visual Basic y.net. Redim, Redim Preserve, Erase. Ejemplos (CU00342A)
aprenderaprogramar.com Arrays dinámicos y arrays estáticos con Visual Basic y.net. Redim, Redim Preserve, Erase. Ejemplos (CU00342A) Sección: Cursos Categoría: Curso Visual Basic Nivel I Fecha revisión:
INDICE Parte 1. Visual Basic Capitulo 1. Qué es Visual Basic? Capitulo 22. Mi Primera Aplicación Capitulo 3. Elementos del lenguaje
INDICE Prólogo XV Parte 1. Visual Basic 1 Capitulo 1. Qué es Visual Basic? 3 Introducción 3 Como crear una aplicación 5 Otras facilidades de Visual Basic 6 Un lenguaje de alto nivel 9 Menús 10 Colores
Expresiones y sentencias
Expresiones y sentencias Expresión Construcción (combinación de tokens) que se evalúa para devolver un valor. Sentencia Representación de una acción o una secuencia de acciones. En Java, todas las sentencias
Tema 4. Operadores y Expresiones
Tema 4 Operadores y Expresiones Contenidos 1. Conceptos Básicos. 2. Operadores Aritméticos. 3. Operadores de Relación, de Igualdad y Lógicos. 4. Operadores de Incremento y Decremento. 5. Operadores y Expresiones
Algoritmos y programas. Algoritmos y Estructuras de Datos I
Algoritmos y programas Algoritmos y Estructuras de Datos I Primer cuatrimestre de 2012 Departamento de Computación - FCEyN - UBA Programación funcional - clase 1 Funciones Simples - Recursión - Tipos de
Computación II. Introducción a Visual Basic
Computación II Introducción a Visual Basic Introducción a Visual Basic Microsoft Visual Basic es un conjunto de herramientas que posibilitan el desarrollo de aplicaciones para Windows de una manera rápida
Variables. Una variable no es más que un nombre simbólico que identifica una dirección de memoria: vs.
Variables Una variable no es más que un nombre simbólico que identifica una dirección de memoria: Suma el contenido de la posición 3001 y la 3002 y lo almacenas en la posición 3003 vs. total = cantidad1
Programación estructurada (Introducción a lenguaje C)
Programación estructurada (Introducción a lenguaje C) M. en C. Sergio Luis Pérez Pérez UAM CUAJIMALPA, MÉXICO, D. F. Trimestre 15-I Sergio Luis Pérez (UAM CUAJIMALPA) Curso de programación estructurada
El lenguaje C. 1. Estructuras. Principios de Programación. 1.1. Definicion de estructuras
Principios de Programación El lenguaje C 1. Estructuras Las estructuras son colecciones de variables relacionadas bajo un nombre. Las estructuras pueden contener variables de muchos tipos diferentes de
Tema: Punteros a Objetos. Puntero this.
Programación II. Guía 6 1 Facultad: Ingeniería Escuela: Computación Asignatura: Programación II Tema: Punteros a Objetos. Puntero this. Objetivos Manejar objetos por medio de punteros. Utilizar el puntero
Principios de Computadoras II
Departamento de Ingeniería Electrónica y Computadoras Operadores y Expresiones [email protected] Primer programa en Java 2 Comentarios en Java Comentario tradicional (multi-línea) Comentario de línea Comentario
Programación Estructurada
Programación Estructurada PROGRAMACIÓN ESTRUCTURADA 1 Sesión No. 2 Nombre: El lenguaje de programación C Contextualización Una constante en todos los lenguajes de programación (viejos y nuevos) es la implementación
Sistemas Operativos Practica 1: procesos y concurrencia.
Sistemas Operativos Practica 1: procesos y concurrencia. Objetivos: Introducir al alumno a los conceptos de programa, concurrencia, paralelismo y proceso o tarea. Manejo del concepto de concurrencia haciendo
Lenguaje C [Apuntadores y arreglos]
Lenguaje C [Apuntadores y arreglos] M. en C. Sergio Luis Pérez Pérez UAM CUAJIMALPA, MÉXICO, D. F. Trimestre 14-O Sergio Luis Pérez (UAM CUAJIMALPA) Curso de Lenguaje C 1 / 20 Apuntadores y direcciones
Introducción a C++ y Code::Blocks
Introducción a C++ y Práctica Imperativo Clase 1 Luis Agustín Nieto Departamento de Computación, FCEyN,Universidad de Buenos Aires. 28 de mayo de 2010 Menu de esta Tarde Funcional Vs. Imperativo (Intérprete
Java Avanzado Facultad de Ingeniería. Escuela de computación.
2 Java Avanzado Facultad de Ingeniería. Escuela de computación. Java Avanzado. Guía 5 3 Introducción Este manual ha sido elaborado para orientar al estudiante de Java Avanzado en el desarrollo de sus prácticas
Métodos para escribir algoritmos: Diagramas de Flujo y pseudocódigo
TEMA 2: CONCEPTOS BÁSICOS DE ALGORÍTMICA 1. Definición de Algoritmo 1.1. Propiedades de los Algoritmos 2. Qué es un Programa? 2.1. Cómo se construye un Programa 3. Definición y uso de herramientas para
Arrays unidimensionales. Dim.Option Base. Erase. Ejemplos en Visual Basic (CU00311A)
aprenderaprogramar.com Arrays unidimensionales. Dim.Option Base. Erase. Ejemplos en Visual Basic (CU00311A) Sección: Cursos Categoría: Curso Visual Basic Nivel I Fecha revisión: 2029 Autor: Mario R. Rancel
Programación Orientada a Objetos Sentencias Java Parte I Ing. Julio Ernesto Carreño Vargas MsC.
Sentencias Java Parte I Ing. Julio Ernesto Carreño Vargas MsC. Variables Conceptos de Memoria Una variable es un espacio en la memoria del PC Cada variable tiene un nombre, un tipo, un tamaño y un valor
DEFINICION. Ing. M.Sc. Fulbia Torres Asignatura: Estructuras de Datos Barquisimeto 2006
ARBOLES ESTRUCTURAS DE DATOS 2006 DEFINICION Un árbol (tree) es un conjunto finito de nodos. Es una estructura jerárquica aplicable sobre una colección de elementos u objetos llamados nodos; uno de los
Programación de Computadores 4 Iteraciones y Decisiones. Prof. Javier Cañas. Universidad Técnica Federico Santa María Departamento de Informática
Programación de Computadores 4 Iteraciones y Decisiones Prof. Javier Cañas Universidad Técnica Federico Santa María Departamento de Informática Contenido 1 Introducción 2 Operadores Relacionales y Lógicos
UNIDAD 4. MODIFICAR TABLAS DE DATOS
UNIDAD 4. MODIFICAR TABLAS DE DATOS Aquí veremos las técnicas de edición de registros para modificar tanto la definición de una tabla como los datos introducidos en ella. Esta unidad está dedicada, principalmente,
Caracteres y Cadenas Conversión de Datos Funciones y procedimientos Archivos cabecera. Fundamentos de programación
1 Caracteres y Cadenas Conversión de Datos Funciones y procedimientos Archivos cabecera Fundamentos de programación Agenda Caracteres y Cadenas Conversión de Datos Introducción a las funciones y procedimientos
LENGUAJE. Tema 2 Elementos de un programa
LENGUAJE Tema 2 Elementos de un programa ELEMENTOS DE UN PROGRAMA Comentarios. Identificadores. Constantes. Variables. Operadores. Sentencias o instrucciones. COMENTARIOS Los comentarios en C pueden ocupar
PILAS Fundamentos
Alg. y Estr. Datos-I / Fund. Progr.-II (R. Ferrís, J. Albert) 11 PILAS 11.1 Fundamentos En este tema y en el siguiente se analizarán las estructuras de datos lineales pilas y colas. Las pilas y las colas
1
Curso: BORLAN DELPHI 5 Curso de desarrollo de aplicaciones Windows utilizando el entorno de programación Borland Delphi 5. Se estudian las particularidades del lenguaje Delphi, así como su sintaxis; sus
Sistemas operativos. Tema 10: Sistemas de ficheros
Sistemas operativos Tema 10: Sistemas de ficheros Sistemas de ficheros El sistema de ficheros suele ser el aspecto más m visible de un SO para los usuarios. Objetivos: Ofrecer una visión n uniforme del
Tema 3 Constantes, Variables y Tipos
Tema 3 Constantes, Variables y Tipos Contenidos 1. Definiciones. 1.1 Variables y Constantes. 1.2 Identificadores. 2. Declaración de Variables en un Programa en C. 3. Tipos de Datos. 3.1 Clasificación.
Unidad II. Fundamentos de programación en Java. Ing. José Luis Llamas Cárdenas
Unidad II Fundamentos de programación en Java Ing. José Luis Llamas Cárdenas En java para poder escribir se emplea el objeto System.out, t pero para leer del teclado es necesario emplear System.in Et Este
Operadores de comparación
Operadores de comparación Los operadores de comparación en C son: Igual (==) Distinto (!=) Mayor (>) y Mayor o igual (>=) Menor (
Estructuras en LabVIEW.
Estructuras en LabVIEW. Sumario: 1. Ejecución según el flujo de datos. 2. Estructuras básicas disponibles en LabVIEW. a) Estructura Sequence. b) Estructura Case. c) Estructura For Loop. d) Estructura While
Las clases como tipos de datos definidos por el usuario
. La clase Fraccional. Representación en UML de los niveles de acceso 3. Categorías de los objetos que aparecen en los métodos 4. El puntero this. Métodos operadores La clase Fraccional Vamos a diseñar
Tema: Tipos Abstractos de Datos (TAD s) en C++.
Programación II. Guía 12 1 Facultad: Ingeniería Escuela: Computación Asignatura: Programación II Tema: Tipos Abstractos de Datos (TAD s) en C++. Objetivos Específicos Explicar el concepto "Tipo Abstracto
Programación n Orientada a Objetos Sentencias Java Parte I. Ing. Julio Ernesto Carreño o Vargas MsC.
Programación n Orientada a Objetos Sentencias Java Parte I Ing. Julio Ernesto Carreño o Vargas MsC. Variables y expresiones aritméticas Variables Conceptos de Memoria Una variable es un espacio en la memoria
Funciones básicas del depurador
Funciones básicas del depurador Con frecuencia, los alumnos piensan que cuando su programa no tiene errores de compilación (está correctamente escrito) entonces ya es correcto. Muchas veces esto no es
Un identificador le da nombre único a un elemento en un programa (Variables, procedimientos, etc.). No puede contener operadores como + - * /
undamentos de sintaxis en algunas instrucciones de C#.Net Dr. Ramón Roque Hernández Identificadores Un identificador le da nombre único a un elemento en un programa (ariables, procedimientos, etc.). No
Representación de números enteros: el convenio exceso Z
Representación de números enteros: el convenio exceso Z Apellidos, nombre Martí Campoy, Antonio ([email protected]) Departamento Centro Informàtica de Sistemes i Computadors Escola Tècnica Superior d
Diagramas de secuencia
Facultad de Ingeniería Departamento de Ingeniería de Sistemas y Computación Diagramas de secuencia Fragmentos Combinados: caminos alternativos Departamento de Ingeniería de Sistemas y Computación - Universidad
ESCUELA DE INFORMÁTICA
TÉCNICO EN SISTEMAS LABORAL SUBMODULO TEMA 1 (Visual Basic for Application) Microsoft VBA (Visual Basic for Applications) es el lenguaje de macros de Microsoft Visual Basic que se utiliza para programar
