Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 1 En los cursos de programación el estudio de los punteros siempre se ha considerado un reto para los alumnos, tanto en el aprendizaje como en su aplicación. En el lenguaje de programación C, los apuntadores son una de las características más poderosas y útiles, en el desarrollo de programas. El manejo de punteros requiere que el alumno modifique su forma de pensar con respecto al uso de los identificadores. Los punteros son variables que almacenan como valor la dirección de memoria que ocupa otro dato (una variable, constante, elementos de arreglos, funciones, entre otras); en otras palabras, los punteros hacen referencia a un tipo de dato almacenado en memoria.
Esta capacidad de los punteros permite que referencias a funciones, puedan ser utilizadas como argumentos en el llamado a otra función.
Los punteros guardan una relación muy estrecha con los arreglos y cadenas, proporcionando una vía alternativa para acceder a los elementos individuales del arreglo. Los punteros son el elemento fundamental en la creación de estructuras de datos dinámicas (listas, pilas, colas, listas enlazadas, árboles y grafos), a través de la asignación dinámica de memoria.
Aquí desarrollaremos los siguientes ítems:
• Definición
• Naturaleza de los Punteros
• Declaración
• Inicialización
• Operaciones de Dirección e Indirección
• Aritmética de Punteros
• Relación entre Arreglos y Punteros
• Punteros y Funciones
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 2 Definición
Un puntero o un apuntador es una variable que almacena como dato la dirección de memoria de otro identificador como puede ser un arreglo, una función u otra variable. Los punteros son variables que almacenan como valor la dirección de memoria que ocupa otro dato (una variable, constante, elementos de arreglos, funciones, entre otras); en otras palabras, los punteros hacen referencia a un tipo de dato almacenado en memoria. Esta capacidad de los punteros permite que referencias a funciones, puedan ser utilizadas como argumentos en el llamado a otra función.
Hasta los momentos habíamos visto variables cuyos datos podían ser valores de tipo numérico, alfanumérico y lógico o booleanos, ahora estamos conociendo otro tipo de dato, como lo son las direcciones de memoria, que son capaces de almacenarlas en una variable especial conocida como apuntador.
Los punteros pueden ser usados para:
• Fundamentalmente son usados para el manejo de memoria en forma dinámica, permitiendo un uso más eficiente de este recurso. • Comunicar información entre una función y sus puntos de llamada. • Permite devolver varios datos desde una función mediante los
argumentos de la función.
• Permiten que referencias a otras funciones puedan ser especificadas como argumentos de otra función. (pasar funciones como argumentos).
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 3 • Representan de una forma más conveniente los arreglos multidimensionales, permitiendo que un arreglo multidimensional sea reemplazado por un array de punteros de menor dimensión.
• Son el soporte de enlace que utilizan estructuras avanzadas de datos en memoria dinámica como las listas, pilas, colas y árboles.
Naturaleza de los Punteros
Entender cómo trabajan los punteros requiere un repaso de cómo se almacenan los datos en memoria. Primero debemos recordar que la memoria está compuesta por celdas de memoria y cada celda posee dos áreas, una que es la dirección (valor fijo permite identificar la posición de dicha celda) y la otra es la información (dato almacenado en la celda). Al momento de declarar una variable, por ejemplo int x, el compilador le asigna a x una dirección de memoria, fija, donde almacenará su valor. Los valores almacenados en memoria ocupan una o más celdas contiguas de memoria (palabras o bytes adyacentes). El número de celdas de memoria requeridas para almacenar un valor depende del tipo de dato. Por ejemplo, un carácter será almacenado en 1 byte (8 bits) de memoria; un entero normalmente necesita 2 bytes continuos. Un valor punto flotante puede necesitar 4 u 8 bytes. Para acceder a un valor de una variable, almacenado en memoria, se hace mención al nombre de la variable.
int x; x = 4;
printf("%d",x);
Hay dos operadores de apuntadores: & y *. & es un operador monario que devuelve la dirección de memoria de su operando. (Recuérdese que un operador monario sólo necesita un operando.)
Celda de Memoria
Dirección x Valor x
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 4 El segundo operador de apuntadores, *, es el complemento de &. Es un operador monario que devuelve el valor que se encuentra en la dirección que le sigue.
Ejemplo:
Supongamos que v es una variable que representa un determinado dato.
El compilador automáticamente asignará celdas de memoria para este dato. Podemos acceder a los datos si conocemos su localización (la dirección) de la primera celda de memoria. La dirección de memoria de v puede se determinada mediante la expresión &v. Ahora vamos asignar la dirección de v a otra variable llamada, pv. Así, pv = &v
Esta nueva variable (pv) es un puntero a v, puesto que <<Apunta>> a la posición de memoria donde se almacena v, pv representa la dirección de v y no su valor, pv es referida como una variable apuntadora, La relación entre v y pv está ilustrada en la figura siguiente
Dirección de v Valor de v
pv v
El dato representado por v (es decir, el dato almacenado en las celdas de memoria de v) puede ser accedido mediante la expresión *pv, donde * es el operador indirección, que opera sólo sobre una variable puntero. Por tanto; *pv y v representa el mismo dato (el contenido de las mismas celdas de memoria).
Declaración
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 5 debe ser igual al tipo de dato de la variable que ocupa la dirección de memoria que el puntero almacena. Para declarar un puntero se realiza igual que el resto de las variables, pero al nombre del puntero le precede el símbolo * (asterisco), que leeremos “puntero a”. Entonces se dice que un puntero apunta a un tipo de datos específico.
Sintaxis:
<Tipo_dato>* <nombre_puntero>;
equivale a
<Tipo_dato> *<nombre_puntero>;
Donde:
Tipo_dato: Es el tipo de dato del puntero.
Nombre_puntero: Es el nombre del puntero
Ejemplo:
int* : es un tipo de dato puntero a enteros.
float *: es un tipo de dato puntero a datos tipo float.
float* ptry; ptry, es un puntero que apunta a tipos de datos float. int *ptrx; ptrx, es un puntero que apunta a tipos de datos enteros.
También se puede decir:
*ptrx, es una variable que almacena direcciones de memoria que
contienen tipos de datos int (enteros).
*ptry, es una variable que almacena direcciones de memoria que
contienen tipos de datos float.
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 6 Declaraciones de Punteros
int *PuntAInt; // Tipo puntero a enteros (int) char *PuntACar; // Tipo puntero a caracteres (char) double *punto; // Tipo puntero a double
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, clase, función, etc.) y después el tipo de datos puntero.
Inicialización
Como pasa con todas las variables en C, cuando se declara un puntero sólo se reserva el espacio de memoria para almacenarlo, pero no se asigna ningún valor inicial, el contenido de la variable puntero permanecerá sin cambios, de modo que el valor inicial del puntero será aleatorio e indeterminado
(denominado comúnmente como basura). Debemos suponer que contiene una
dirección no válida de memoria. Al intentar realizar operaciones con punteros NO INICIALIZADOS, ocurrirá un error no sólo en el programa, sino también en el
sistema operativo, porque se intenta acceder a una dirección de memoria NO EXISTENTE o NO VÁLIDA. Todos los punteros deben ser inicializados antes de ser
utilizados.
A las variables de tipo punteros sólo pueden asignárseles: • el valor cero (0 o la constante NULL ),
• otro puntero con el mismo tipo de dato,
• una dirección de memoria de una variable que posea el mismo tipo de dato que el puntero. Las direcciones de memoria deben ser asignadas sólo con el operador de dirección & (ampersand).
Punteros Nulos:
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 7 float *ptry = NULL;
int *ptrz;
Es posible realizar la asignación de un puntero a otro, si ambos son del mismo tipo de datos.
ptrz = ptrx; //el puntero ptrz recibe la dirección de memoria que tiene almacenada el puntero ptrx.
Operadores de Dirección e Indirección
Operador de Dirección o Referencia. (Operador &). Es un operador monario (sólo requiere un operando) que devuelve la dirección de memoria del operando, es decir, lo que hace es devolver la dirección que en memoria ocupa el objeto sobre el que se aplica. Literalmente significa “la dirección de”.
Un ejemplo de su uso para inicializar un puntero es: int x =10;
int *ptrx = &x;
ptrx, posee como valor la dirección de memoria que ocupa la variable x.
Entonces se dice que ptrx apunta a x
Celda de Memoria ptrx
Dirección Valor
0xF8E 0xF8A
Celda de Memoria x
Dirección Valor
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 8 La representación gráfica
Este operador NO es aplicable a expresiones constantes, pues éstas no se almacenan en ninguna dirección de memoria específica sino que se incrustan en las instrucciones. Por ello, no es válido hacer directamente:
int px = &10; // Error 10 no es una variable con dirección propia
Tampoco es válido aplicar & a variables declaradas con la clase de almacenamiento register, ni campos de sólo lectura (readonly), pues si éstos pudiesen ser apuntados por punteros se correría el riesgo de poderlos modificar ya que a través de un puntero se accede a memoria directamente, sin tenerse en cuenta si en la posición accedida hay algún objeto, por lo que mucho menos se considerará si éste es de sólo lectura.
NOTA: El operador & puede aplicarse sólo a operandos lvalue (algo a lo
que se puede asignar un valor, como una variable).
Operador de Indirección o Desreferencia de memoria. (Operador *): Es un
operador monario que actúa sólo sobre operandos de tipo puntero. Este operador devuelve el valor de la variable a la que apunta el puntero. Es decir, devuelve el valor almacenado en la dirección de memoria apuntada por el puntero. Literalmente significa “el valor de”.
Ejemplo:
int x = 10; int *ptrx = &x; int z ;
z = *ptrx ; // se asigna a la variable z el valor 10.
Puntero Variable
ptrx x
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 9 Con el operador de Indirección (*), también se pueden asignar valores a una dirección de memoria apuntada por un puntero, es decir, se puede alterar el valor de la variable a la que apunta un puntero.
Ejemplo:
int x = 10; int *ptrx = &x;
*ptrx = 30; // se asigna a la variable x el valor 30.
NOTA: No debemos confundir el operador * en la declaración
del puntero (int *p), con el operador * (asterisco) en las instrucciones (*p=10 o z=*p).
No se puede desreferenciar un puntero NULL, produce un error en tiempo de ejecución.
Ejemplo referenciando:
#include<stdio.h>
int main(void) {
int x = 99; int *p1, *p2;
p1 = &x; p2 = p1;
/* imprimir el valor de x dos veces */
printf(“Valores apuntados por p1 y p2: %d %d\n”, *p1, *p2);
/* imprimir la dirección de x dos veces */
printf(“Direcciones en p1 y p2: %p %p\n”, p1, p2);
return 0; }
Tras la secuencia de asignaciones p1 = &x;
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 10
tanto p1 como p2 apuntan a x. Por tanto, tanto p1 como p2 se refieren al mismo objeto. A continuación se muestra un ejemplo de salida del programa, confirmando esto.
Valores apuntados por p1 y p2: 99 99 Direcciones en p1 y p2: 0063FE00 0063FE00
Obsérvese que las direcciones se muestran usando el modificador de formato de printf( ) %p. que hace que printf( ) muestre una dirección en el formato utilizado por la computadora en cuestión.
Ejemplo desreferenciando:
{
int* puntero; int variable;
puntero = &variable;
*puntero = 33; /* mismo efecto que variable=33 */ }
Varios apuntadores pueden apuntar a la misma variable: int* puntero1;
int* puntero2; int var;
puntero1 = &var; puntero2 = &var;
*puntero1 = 50; /* mismo efecto que var=50 */
var = *puntero2 + 13; /* var=50+13 */
Aritmética de Punteros
Los Apuntadores pueden ser utilizados como operandos válidos en los siguientes tipos de expresiones:
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 11
• Relacionales.
Los apuntadores no pueden ser utilizados como operandos válidos con todos los operadores. Los apuntadores sólo pueden realizar:
Operaciones Aritméticas:
o Sumarle o restarle un valor entero: Incremento/Decremento.
Ejemplo:
ptrx++; ptrx--; ++ptrx; --ptrx;
ptrx=ptrx+5 ; ptrx=ptrx-4; ptrx+=3; ptrx-=2;
o Restar un apuntador de otro. Resta de direcciones de
memoria.
Ejemplo:
ptrx = ptrx – ptry;
Operaciones Relacionales: Es posible comparar dos variables de tipo
puntero en una expresión relacional, usando operaciones relacionales de igualdad (= =), desigualdad (!=) y comparación (<, >, =<, >=).
La aritmética de punteros, tiene sentido sólo cuando el puntero posee asignado varias direcciones continuas de memoria, es decir, cuando se ejecuta sobre un arreglo. Es muy poco probable que dos variables de un mismo tipo de datos posean direcciones contiguas de memoria a no ser que sean elementos adyacentes que formen parte de un arreglo.
Ejemplo
# include <stdio.h> void main()
{
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 12
static int a[6]= { 1, 2, 3, 4, 5, 6]; px=&a[0]:
py=&a[5]:
printf (“px=%x py%x”, px, py); printf (“\n\npy- px=%x”, py - px); }
La ejecución del programa produce la siguientes salida:
px=52 py=5c
py – px=5
Relación entre Arreglos y Punteros
Los nombres de los arreglos, son un tipo especial de punteros, que apuntan a la dirección de memoria del primer elemento del arreglo. Recordemos que los arreglos poseen elementos almacenados en direcciones contiguas de memoria. Cada elemento del arreglo posee una dirección de memoria.
Ejemplo:
int v[10] = {0,1,2,3,4,5,6,7,8,9,}; int *ptrv;
ptrv = &v[0]; //se le asigna al puntero ptrv, la dirección de memoria del primer elemento del arreglo v;
//equivale a ptrv = v;
ptrv = v; //se asigna la dirección de v a ptrv.
Como dijimos anteriormente, el nombre del arreglo (v) es un puntero, que apunta, a la primera dirección de memoria del arreglo. Por lo tanto se está realizando una asignación de punteros. Cualquier posición de un elemento en el arreglo se puede obtener con el operador de dirección.
ptrv = &v[4]; //ptrv apunta a la posición 4 del arreglo. ptrv = &v[7]; //ptrv apunta a la posición 7 del arreglo.
Con aritmética de punteros, es posible acceder a la dirección de cada elemento del arreglo.
Ejemplo:
int v[10] = {0,1,2,3,4,5,6,7,8,9,}; int *ptrv;
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 13
//Aritmética
ptrv + 2; //equivale a &v[2] ptrv + 6; //equivale a &v[6]
En ambos casos, ptrv no modifica la dirección de memoria a la que apunta.
En la aritmética de punteros la sumatoria de enteros a memorias (ptrv + n) se realiza en base a la cantidad de bytes del tipo de datos del puntero (ptrv + n* t_bytes).
Tipos Datos Tamaño Byte (máquina 16 bits) Tamaño Byte (máquina 32 bits)
char 1 byte 1 byte
int 2 bytes 4 bytes
long 4 bytes 4 bytes
float 4 bytes 4 bytes
Si el arreglo es de tipo int, cada posición del arreglo ocupa 2 bytes de memoria (máquinas de 16 bits), por lo tanto avanza (ptrv + (2 * 2))
En el siguiente ejemplo se inicia en la primera posición
int *ptrv, v[3] = {15, 18, 20, 46};
ptrv = v;//ptrv apunta a la dirección 2000
printf(“%d”, *ptrv); //valor 15
ptrv = ptrv + 1;//avanza a la posición de dirección 2002 //ptrv = 2000 + (1 * 2) = 2000 + 2 = 2002 printf(“%d”, *ptrv); //valor 18
ptrv+=2; //avanza a la posición de dirección 2006 //ptrv = 2000 + (2 * 2) = 2000 + 4 = 2006 printf(“%d”, *ptrv); //valor 46
1 5 1 8 2 0 4 6
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 14
Resumen de las Operaciones con Punteros
1. A una variable puntero se le puede asignar la dirección de memoria de una variable ordinaria (pv=&p).
2. A una variable puntero se le puede asignar el valor de otra variable puntero (pv=py), siempre y cuando ambos punteros apunten al mismo tipo de datos o que el puntero pv sea un puntero genérico (puntero a void).
3. A una variable puntero se le puede asignar un valor nulo (cero) (pv=NULL, NULL es una constante simbólica con valor cero).
4. Una cantidad entera puede ser sumada o restada a una variable puntero (px+3, pv++).
5. Una variable puntero puede ser restada de otra siempre y cuando ambas apunten a elementos del mismo arreglo.
6. Dos variables punteros pueden ser comparadas siempre que ambas apunten a datos del mismo tipo de datos.
Punteros y Funciones
Un área en la cual desempeñan un papel prominente los apuntadores, en el lenguaje C, es en la transmisión de parámetros a funciones. Por lo común, los parámetros se transmiten por valor a una función en C, es decir, se copian los valores transmitidos en los parámetros de la función llamada en el momento en que se invoca. Si cambia el valor de un parámetro dentro de la función, no cambia su valor en el programa que la llama. Por ejemplo considérese el siguiente fragmento:
... X=5;
printf (“%d\n”, x); funct(x);
printf(“%d\n”, x); ...
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 15
++y;
printf(“%d\n,y); return;
}
La ejecución del programa sería: imprime el número 5 y después llama a funct. El valor de "x", que es 5, se copia en "y" y comienza la ejecución de funct. Después, imprime el número 6 y regresa funct. Sin embargo, cuando se incrementa el valor de "y", el valor de "x" permanece invariable. Así, imprime el número 5, "x" y "y" refieren a 2 variables diferentes.
Si deseamos usar funct para modificar el valor de "x", debemos de transmitir la dirección de "x" de la siguiente manera:
... X=5;
printf (“%d\n”, x); funct(&x); printf(“%d\n”, x); ...
void funct (int *py) {
++(*py);
printf(“%d\n, *py); return;
}
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 16
Precauciones con los Apuntadores
Apuntadores No Inicializados:
Si se altera el valor al que apunta un puntero no inicializado, se estará modificando cualquier posición de la memoria.
main() {
int* puntero;
*puntero = 1200; /* Se machaca una zona cualquiera de la memoria */
}
Confusión De Tipos:
Un puntero a un tipo determinado puede apuntar a una variable de cualquier otro tipo. Aunque el compilador lo puede advertir, no es un error. (Afortunadamente, esto no ocurre en C++).
main() {
int p;
double numero; int* puntero;
p = № /* incorrecto,
pero el compilador no aborta */
*p = 33; /* Un desastre */
}
Apuntadores a variables locales fuera de ámbito:
Si un puntero apunta a una variable local, cuando la variable desaparezca el puntero apuntará a una zona de memoria que se estará usando para otros fines. Si se des-referencia el puntero, los resultados son imprevisibles y a menudo catastróficos.
main() {
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 17
while (...) {
int local;
puntero = &local;
/* ‘puntero’ apunta a una variable local */ ...
*puntero = 33; /* correcto */
} ...
/* ahora ‘puntero’ apunta a una zona de memoria inválida */
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 18
Estructuras
El uso de estructuras como una representación o aproximación de registros de datos, ha sido una de las herramientas más idóneas usada en la Programación Estructurada para poder agrupar varios tipos de datos primitivos en un solo a la final.
Aquí desarrollaremos los siguientes ítems:
• Conceptos Básicos
• Acceso a los componentes de una estructura.
• Arreglos de Estructuras
• Estructuras Anidadas
• Apuntadores a estructuras.
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 19
Conceptos Básicos
Una Estructura es una colección de variables simples, que pueden contener
diferentes tipos de datos. Es un tipo de dato definido por el usuario. Son también conocidas como Registros.
Ayudan a organizar y manejar datos complicados en programas debido a que agrupan diferentes tipos de datos a las que se les trata como una sola unidad en lugar de ser vistas como unidades separadas.
En C se forma una estructura utilizando la palabra reservada struct, seguida por un campo etiqueta opcional, y luego una lista de miembros dentro de la estructura. La etiqueta opcional se utiliza para crear otras variables del tipo particular de la estructura:
struct campo_etiqueta{ tipo_miembro miembro_1; tipo_miembro miembro_2; tipo_miembro miembro_3; :
:
tipo_miembro miembro_n; };
Un punto y coma finaliza la definición de una estructura puesto que ésta es realmente una sentencia C. Algunos de los ejemplos usan la estructura:
struct stbarco{ char tipo[15]; char modelo[15]; char titular[20]; int anno;
long int horas_motor; float precioventa; };
En un programa, podemos asociar una variable con una estructura utilizando una sentencia similar a la siguiente:
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 20
La sentencia define stbarco_usado de tipo struct stbarco. La declaración requiere el uso del campo etiqueta de la estructura. Si esta sentencia está contenida dentro de una función, entonces la estructura, llamada stbarco_usado, tiene un ámbito local a esa función. Si la sentencia está contenida fuera de todas las funciones de programa, la estructura tendrá un ámbito global. Es posible declarar una variable usando esta sintaxis:
struct stbarco{ char tipo[15]; char modelo[15]; char titular[20]; int anno;
long int horas_motor; float precioventa; };stbarco_usado;
Aquí la declaración de variable va antes del punto y coma final. Cuando se asocia sólo una variable con el tipo estructura, el campo etiqueta puede ser eliminado, por lo que sería posible escribir:
struct { char tipo[15]; char modelo[15]; char titular[20]; int anno;
long int horas_motor; float precioventa; };stbarco_usado;
Acceso a los Componentes de una Estructura
Para accesar a los miembros de las estructuras se usa el punto u operador miembro (.). La sintaxis es:
estructuraNombre.miembroNombre
Por ejemplo en:
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 21
Aquí, stbarco_usado es el nombre asociado con la estructura, y modelo es una variable miembro de la estructura, otro ejemplo:
gets(stbarco_usado.tipo);
Esta sentencia leerá la marca del stbarco_usado en el arreglo de caracteres, mientras la próxima sentencia imprimirá el precio de venta de stbarco_usado en la pantalla.
printf(“%f”, stbarco_usado.precioventa);
Ejemplo de estructuras:
#include <stdio.h> #include <conio.h> struct datos {
char num_control[9]; char nombre[30]; int edad;
char sexo;
char domicilio[30]; };
struct datos alumno; void main ( )
{
clrscr( );
printf(“\nIntroduce los datos del alumno:”); printf(“\nNúmero de Control:”);
gets(alumno.num_control); printf(“\nNombre:”); gets(alumno.nombre); printf(“\nEdad:”);
scanf(“%d”,&alumno.edad); printf(“\nSexo:”);
alumno.sexo=getchar( ); printf(“\nDirección:”); gets(alumno.domicilio);
printf(“\nLos datos capturados fueron:”); printf(“\nNúmero de Control:”);
puts(alumno.num_control); printf(“\nNombre:”); puts(alumno.nombre); printf(“\nEdad:”);
printnf(“%d”,alumno.edad); printf(“\nSexo:”);
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 22
printf(“\nDirección:”); puts(alumno.domicilio); getch ( );
}
Arreglos de Estructuras
Pueden ser construidas, es decir, conceptuar a los elementos de un arreglo como estructuras, esto se puede hacer de la siguiente manera:
struct datos {
char num_control[9]; char nombre[30]; int edad;
char sexo;
char domicilio[30]; };
struct datos alumnos[35]; /*Esta es la variable arreglo de estructura*/
Para tener acceso a una determinada estructura, se indexa el nombre de la estructura de la siguiente manera: alumnos[i].nombre; aquí sabemos el nombre del alumno que se encuentra en la posición i del arreglo, y de esta manera para todos los datos.
Estructuras Anidadas
Una estructura puede contener otras estructuras llamadas estructuras anidadas. Las estructuras anidadas ahorran tiempo en la escritura de programas
que utilizan estructuras similares. Se han de definir los miembros comunes solo una vez en su propia estructura y a continuación utilizar es estructura como un miembro de estructura. Consideremos las siguientes dos definiciones de estructuras:
struct empleado {
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 23
long int cod_postal; double salario; };
struct clientes {
char nombre_cliente[30]; char direccion[25]; char ciudad[20]; char provincia[20]; long int cod_postal; double saldo; };
Estas estructuras contienen muchos datos diferentes, aunque hay datos que están solapados. Así, se podría disponer de una estructura, info_dir que contuviera los miembros comunes.
struct info_dir {
char direccion[25]; char ciudad[20]; char provincia[20]; long int cod_postal; };
Esta estructura se puede utilizar como miembro de las otras estructuras, es decir anidarse.
struct empleado {
char nombre_emp[30]; struct info_dir direccion_emp; double salario;
};
struct clientes {
char nombre_cliente[30]; struct info_dir direccion_clien; double saldo;
};
Apuntadores a Estructuras
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 24
cualquier otro objeto y se declara un puntero estructura tal como se declara cualquier otra variable estructura: especificando un puntero en lugar del nombre de la variable estructura.
struct persona {
char nombre[30]; int edad;
int altura; int peso; };
struct persona empleado; struct persona persona *p; p = &empleado;
Cuando se referencia un miembro de la estructura utilizando el nombre de la estructura, se especifica la estructura y el nombre del miembro separado por un punto (.). Para referenciar el nombre de una persona, utilice empleado.nombre. Se referencia una estructura utilizando el puntero estructura. Se utiliza el operador -> para acceder a un miembro de ella.
Paso de Estructuras a las Funciones
C permite pasar estructuras a funciones, bien por valor o bien por referencia utilizando el operador &. Si la estructura es grande, el tiempo necesario para copiar un parámetro struct a la pila puede ser prohibitivo.
En tales casos, se debe considerar el método de pasar la dirección de la estructura. El siguiente programa muestra el pase de la dirección de una estructura a una función para entrada de datos.
La misma variable la pasa por valor a otra función para salida de los campos.
#include <stdio.h>
/*Define el tipo de estructura info_persona*/ struct info_persona
{
Recopilado por: MSc. Leonardo Javier Malavé Quijada
Unidad Curricular: Intensivo 2016.- Algorítmica y Programación – Trayecto I Página 25
char ciudad[25]; char provincia[25]; char codigopostal[6]; };
/*Prototipos de funciones*/
void entrad_pna(struct info_persona *pp); void ver_info(struct info_persona p); void main ( )
{
struct info_persona reg_dat;
entrad_pna(®_dat); /*Pasa por referencia la variable*/ ver_info(reg_dat); /*Pasa por valor*/
clrscr( );
printf(“\nPulsa cualquier carácter para continuar\n”); getchar( );
}
void entrad_pna (struct info_persona *pp) {
clrscr( );
puts(“\nEntrada de datos de la Persona\n”);
/*Para acceder a los campos se utiliza el selector -> */ printf(“\nNombre:”);
gets(pp->nombre); printf(“\nCalle:”); gets(pp->calle); printf(“\nCiudad:”); gets(pp->ciudad); printf(“\nProvincia:”); gets(pp->provincia); printf(“\nCódigo Postal:”); gets(pp->codigopostal); }
void ver_info (struct info_persona p) {
clrscr( );
puts(“\n\t Información relativa a la persona”); puts(p.nombre);