Ads 468x60px

Perfil

lunes, 17 de octubre de 2011

Tipos de datos

En este apartado se verán estructuras de datos más complejas que las descritas hasta ahora. Se hablará de:
  • Cadenas de caracteres ( strings)
  • Tipos estructurados
  • Tipos enumerados
  • Uniones

También se presentarán algunas conceptos sobre el uso de identificadores, como el ámbito y la existencia.

6.1 Cadenas de caracteres


En C no existe un tipo predefinido para manipular cadenas de caracteres ( strings). Sin embargo, el estándar de C define algunas funciones de biblioteca para tratamiento de cadenas.

La forma de declarar una cadena en C es mediante un vector de caracteres:

         char hola [5];


Toda cadena ha de terminar con el carácter especial 0 (cero).
El C no tiene otra manera de detectar el final de una cadena.


6.2 Literales e inicialización de cadenas


Los literales tipo cadena son de la forma

          "texto entre comillas"

Al declarar una vector de caracteres, se le puede inicializar con un literal:

         char texto [4] = "abc";


Pero NO se puede hacer una asignación de ese tipo en una sentencia:

         texto = "xyz";   /* ERROR */

En su lugar, hay que emplear ciertas funciones de biblioteca.


Tres formas equivalentes de inicializar una cadena:

char hola [5] = { 'h', 'o', 'l', 'a', 0 };

char hola [5] = "hola";

main()
{
         char hola [5];
         hola[0] = ‘h’;
         hola[1] = ‘o’;
         hola[2] = ‘l’;
         hola[3] = ‘a’;
         hola[4] = 0;
}


Obsérvese que una cadena de N elementos es capaz de almacenar un texto de N-1 caracteres (el último siempre ha de ser un cero).

No importa que un vector de caracteres contenga una cadena de menos letras, el carácter cero marca el final de la cadena.

Lo que sí es un error peligroso (y además no lo detecta siempre el compilador) es intentar asignarle a un vector de caracteres una cadena de mayor tamaño.
Hay que cuidar mucho que las cadenas queden dentro del espacio reservado para ellas.

6.3 Visualización de cadenas


La función printf admite formato de cadenas, con %s

         char cadena[80];
         ...
         printf ( "El texto es %s\n", cadena );

Para usar printf, debería incluirse la cabecera <stdio.h>

El formato %s admite modificadores. Por ejemplo:
%20s Texto a la derecha, ocupando siempre 20 caracteres
%-15s Texto alineado a la izquierda, ocupando 15 caracteres

Para imprimir sólo la cadena, seguida de un salto de línea, se puede emplear puts (también pertenece a <stdio.h>):

         puts (cadena);



6.4 Biblioteca de manejo de cadenas (string.h)


La biblioteca <string.h> contiene un conjunto de funciones para manipular cadenas: copiar, cambiar caracteres, comparar cadenas, etc.
Las funciones más elementales son:

strcpy ( c1, c2 ); Copia c2 en c1
strcat ( c1, c2 ); Añade c2 al final de c1
int strlen ( cadena ); Devuelve la longitud de la cadena
int strcmp ( c1, c2 ); Devuelve cero si c1 es igual a c2;
no-cero en caso contrario

Para trabajar con estas funciones, al comienzo del programa hay que escribir

         #include <string.h>

Ejemplo:


#include <stdio.h>
#include <string.h>
 
char completo [80];
 
char nombre[32] = "Pedro";
char apellidos [32] = "Medario Arenas";
 
main()
{
  /* Construye el nombre completo */
 
  strcpy ( completo, nombre );     /* completo <- "Pedro" */
  strcat ( completo, " ");                  /* completo <- "Pedro " */
  strcat ( completo, apellidos );  /* completo <- "Pedro 
                                                                Medario Arenas" */
 
  printf ( "El nombre completo es %s\n", completo );
 
}

6.5 Lectura de cadenas


En teoría, podría utilizarse la opción %s de scanf, pero
CASI NUNCA FUNCIONA.

Una mejor alternativa es emplear gets, que también viene en stdio.h

#include <stdio.h>
 
main ()
{
         char nombre [80];
         printf ( "¿Cuál es su nombre? " );
         gets ( nombre );
         printf ( "Parece que su nombre es %s\n", nombre );
}

NOTA: gets no comprueba el tamaño de la cadena. Si el texto tecleado tuviera más de 80 caracteres, se destruirían posiciones de memoria incorrectas.
Para leer caracteres hasta un máximo, hay que usar fgets:

         fgets ( nombre, 80, stdin );

(el identificador stdin se refiere a la entrada estándar , normalmente el teclado. Está definido en <stdio.h>)



6.6 Tipos estructurados


Se pueden definir tipos compuestos de varios elementos o campos de tipos más simples.

La sintaxis es:

struct nombre_del_tipo
{
         campo1;
         campo2;
         ...
         campoN;
};


Las variables de ese tipo se declaran así:

struct nombre_de_tipo variable;

y para acceder a un campo de una variable estructurada,
se utiliza esta sintaxis:

variable.campo


6.7 Ejemplo de tipo estructurado

struct Persona
{
         char nombre [40];
         char apellidos [80];
         long telefono;
         long dni;
         char sexo;       
};
 
 
struct Persona usuario;
 
main ()
{
         usuario.dni = 43742809;
         strcpy ( usuario.apellidos, "Santana Melián" );
         strcpy ( usuario.nombre, "Jacinto Lerante" );
         usuario.telefono = 908330033;
         usuario.sexo = 'V';
}

Si hay campos del mismo tipo, se pueden declarar en la misma línea, separándolos por comas.
Se pueden declarar variables de tipo estructurado a la vez que se declara el tipo.

struct T
{
 
   int campo1, campo2, campo3; /* varios campos */
 
} v1, v2;        /* declaración de variables de tipo struct T */
 
...
 
v1.campo1 = 33;
v2.campo3 = 45;


También se permite omitir el tipo de la estructura ( estructura anónima ):

struct {
  char nombre [8];
  char extension [3];
} nombre_fichero;

En este caso, no se puede crear nuevas variables de ese tipo. No se recomienda usar este tipo de declaraciones.

Inicialización de estructuras


Una variable de tipo struct se puede inicializar en su declaración, escribiendo entre llaves los valores de sus campos en el mismo orden en que se declararon estos.

struct Persona
{
         char nombre [32];
         char apellidos [64];
         unsigned edad;
};
 
struct Persona variable = 
{ "Javier",  "Tocerrado", 32 };



6.8 Definición de tipos: typedef


Se puede dar un nombre nuevo a cualquier tipo de datos mediante typedef.
La sintaxis es

                 typedef declaración;

donde declaración tiene la forma de una declaración de variable,
sólo que se está definiendo un tipo de datos.

typedef long pareja [2];

define un tipo pareja que se puede usar en declaraciones de variables:

pareja p;

es equivalente a

long p [2];

Ejemplos de typedef con estructuras

 
 
typedef struct Persona PERSONA;
PERSONA dato;    /* igual que struct Persona dato; */


Un uso típico es la redefinición de tipos estructurados:

typedef struct   /* estructura anónima */
{ 
  char nombre[80]; 
  char sexo;
  int edad;
} Persona;                /* se declara el tipo Persona */
 
...
 
Persona p;
 
...
 
p.edad = 44;

6.9 Tipos enumerados: enum


Con la construcción enum se pueden definir tipos de datos enteros que tengan un rango limitado de valores, y darle un nombre a cada uno de los posibles valores.

enum dia_de_la_semana
{
         lunes, martes, miercoles, jueves, viernes,  sabado, domingo
};

...

enum dia_de_la_semana hoy;

...

hoy = sabado;


Los valores definidos en enum son constantes enteras que se pueden usar en cualquier punto del programa, usando un operador de moldeo (ver ejemplo).
Se empiezan a numerar de cero en adelante (en el ejemplo, lunes vale cero, martes vale uno, etc.)

int dia = (int)sabado;    /* dia = 5 */

6.10 Valores de la lista en enum


Se puede dar un valor inicial a la lista de valores dados en enum:

enum dia
{
         lunes=1, martes, miercoles, jueves, viernes,        sabado, domingo
};

En este caso los valores van del 1 al 7.

También se pueden dar valores individuales:

enum codigo_postal
{
         LasPalmas=35, Madrid=28, Barcelona=8
};

6.11 Uniones


El tipo estructurado union es similar al struct, salvo que en las mismas posiciones de memoria almacena todos sus campos.


struct rectangulo
{

   int ancho;
   int largo;
};
 
 
union todo_en_uno


{
   char cadena [8];
   int entero;
   double real;
};


En el caso del struct, cada campo ocupa unas posiciones de memoria diferentes. En la union los tres campos comparten las mismas posiciones de memoria.
Eso significa que si se altera un campo de una union se afecta a todos los demás campos.
La union sólo tiene sentido si se sabe que sólo se va a emplear un campo en cada ocasión.


6.12 Combinaciones de tipos


Los tipos estructurados y los vectores pueden combinarse entre sí: dentro de un struct se pueden declarar campos que sean struct, vectores, enum, etc.; se puede declarar un vector de struct, etc.

struct Producto
{
   unsigned identificador;
   char nombre [40];
   long disponible;
};
 
 
struct Producto catalogo [1000];
...
 
catalogo[133].disponible = 2467;
 
 
/* Ejemplo de inicialización */

struct Producto dos_productos [2] =
{
   { 12, "Brutopía", 28000 },
   { 33, "Papas el Canario", 35000 }
};


6.13 Ámbitos y existencia de variables y tipos


Las variables y los tipos de datos se pueden declarar en dos puntos:

  • Al principio de un bloque ( después del "abre llave" { )
  • Fuera de bloques

Las variables declaradas fuera de bloques se llaman globales.
Las variables en el interior de un bloque son locales a ese bloque.

Ámbito


Una variable global se puede utilizar en todo el programa, desde el punto en que se declara.

Una variable local se puede utilizar desde el punto en que se declara hasta el final del bloque donde se declaró.

Si se declara una variable local con el mismo nombre de otra variable accesible en el bloque actual, la nueva variable oculta a la otra variable.


Existencia


Una variable global existe permanentemente mientras el programa se ejecuta.

Una variable local sólo existe mientras se está ejecutando código del bloque donde se ha declarado.
Así, una variable local a una función se crea cuando se llama a la función y desaparece cuando la función retorna.

Ejemplo de ámbitos


int global;
 
double area ( double base, double altura )
{
         double temp = base*altura;
         return temp;
}
 
main()
{
         int local;
         ...
         while (1)
         {
            int mas_local;
            ...
            {
                 int mas_local_todavia;
                 int local;
                 ...
            }
         }
}

Ámbitos y existencia de tipos de datos


Los ámbitos y existencia de los tipos son similares a los de las variables.
Los tipos declarados fuera de bloques son globales al programa.
Los tipos declarados dentro de bloques son locales a ese bloque.

struct pepe { int a,b,c; }; /* ámbito global */

int funcion (void)
{
struct local { int x1,x2; }; /* ámbito local */
struct pepe una;
struct local otra;
}
 
struct pepe variable;              /* correcto */
struct local otra;        /* incorrecto: no estamos
 en el ámbito de “local” */

6.14 Variables static


Se pueden definir variables que tengan ámbito local pero existencia permanente.
Para declararlas se utiliza la palabra static.

static declaración;

Por ejemplo, si se declara una variable static dentro de una función, aunque la función retorne la variable permanece con el último valor que se asignó:

int contador (void)
{
         static int cuenta = 0;
         return cuenta++;
}

Esta función devuelve un número que se va incrementando cada vez que se la llama. La variable cuenta es local a la función contador, pero no desaparece con la salida de la función.
NOTA: la inicialización de una variable static se realiza una sola vez, al comienzo de la ejecución del programa. Por eso el ejemplo anterior funciona ( cuenta se inicializa a cero una sola vez).

6.15 Declaraciones de funciones


Las funciones son siempre globales, esto es, no se permite declarar una función dentro de otra.
Las funciones son visibles sólo después de que se han declarado.

Se pueden declarar funciones, especificando sólo su formato, pero no su cuerpo:

         int suma ( int a, int b );

lo anterior es una declaración de la función suma, que queda disponible para su uso, a pesar de no haber sido definido su cuerpo.

La declaración de una función de esta forma se llama prototipo.

Es buena práctica declarar al comienzo del programa los prototipos de las funciones que vamos a definir, incluyendo comentarios sobre su finalidad.


0 comentarios: