Tabla de Contenidos

Lenguaje C: Datos


Los datos son el objeto de procesamiento en los programas de ordenador, en lenguajes avanzados se habla de objetos, como denominación más genérica.

En lenguaje C las variables y las constantes deben declararse antes de ser utilizadas.

La declaración de un dato requiere expresar:

  modificador tipodato identificador;

Tipos básicos y modificadores

Los tipos de datos establecen la diferencia entre los objetos que se van a procesar, en cuanto a:

La cantidad de memoria necesaria para el almacenamiento de datos, así como el margen de variación (rango) de dichos datos depende:

Las palabras reservadas en lenguaje C para los tipos de datos básicos son:

Los modificadores que se pueden aplicar a los tipos de datos básicos son:

Los datos fundamentales utilizados en lenguaje C se obtienen de las combinaciones permitidas de tipos básicos y modificadores.

Enteros

Los tipos de datos enteros permiten representar cantidades numéricas enteras

La relación entre tamaños que se cumple siempre es:

  shortintlong

Representación interna de números enteros:

Ejemplos:

  char letra;
  int cantidad;
  short edad;
  long memoria;
  enum semana = {lunes, martes, miercoles, jueves, viernes, sabado, domingo};
  enum semana hoy;
  hoy = martes;
Reales

Los tipos de datos reales permiten representar cantidades numéricas en notación científica y de mayor rango.

Se almacenan en memoria en un formato normalizado, ya explicado, en el que se distinguen tres campos:

Tipos:

Otros tipos de datos
  typedef tipodato nuevonombre;
  typedef unsigned long int mitipo;
  /* Se ha creado un nuevo tipo de dato: mitipo */

Constantes

Las constantes son valores fijos que no pueden ser alterados por el programa. Pueden ser de cualquiera de los tipos de datos posibles en lenguaje C:

Enteras

Para su almacenamiento el compilador escoge el tipo de dato más pequeño compatible con esa constante

Pueden expresarse:

* Al escribirlas, se distinguirán los siguientes campos:

* Ejemplos:

  -23L  /* el número -23 almacenado como long */
  010   /* el octal 10 que equivale al 8 en decimal*/
  0xF   /* el 0F hexadecimal que es el 15 decimal */
Reales
  35.78       /* constante real de tipo double */
  1.25E-12    /* constante real de tipo double */
  45F         /* constante real de tipo float */
  33L         /* constante real de tipo long double */
Caracteres
  '6'       /* Carácter 6, código ASCII 0x36 */
  '\12'     /* Código ASCII 12 (Salto de línea) */
  '\0x20'   /* Códgio ASCII 32 (Espacio) */
Simbólicas
  #define NOMBRECONSTANTE Equivalencia
  #define MAXIMO 100               /* MAXIMO toma el valor 100 */
  #define FRASE "Pulsa una tecla"

Variables

Declaración de variables

Todas las variables deben declararse antes de ser utilizadas para que el compilador les asigne la memoria necesaria

  tipodedato nombrevariable;
   char letra;                /* variable tipo carácter */
   int actual, mayor, menor;  /* variables enteras */
   float resultado;           /* variable real */

Según el punto del programa donde se declaran, las variables pueden ser locales, globales o parámetros formales.

  long int Mifuncion(int base, int exponente)
  {
     /* Cuerpo de la función */
  }
Inicialización de variables

La inicialización de variables sirve para asignar el primer valor

  tipodato nombrevariable = valorinicial;
  unsigned int edad = 25;

Otros modificadores de datos

  unsigned int const year = 2018;

Conversión de tipos

En las expresiones, los operandos cambian de tipo automáticamente

  long a
  char b;
  int c, f;
  float d;
  f = a + b * c / d;

Conversión explícita: operador cast

  (tiponuevo)expresion;

Vectores y cadenas

Vectores

Un vector es un tipo de variable especial que permite almacenar un conjunto de datos del mismo tipo

Declaración de vectores

La declaración de un vector supone reservar memoria para sus elementos

  tipodato nombrearray[tamaño];
  num_bytes = tamaño * sizeof(tipodato)
  tipodato nombrearray[tamaño1][tamaño2]...[tamañoN];
  num_bytes = tamaño1 * tamaño2 * ... * tamañoN * sizeof(tipodato)
  tipodato nombrearray[numfilas][numcolumnas];
  int lista[10];       /* Vector de 10 enteros */
  char vocales[5];     /* Vector de 5 letras */
  float matriz[6][4];  /* Tabla de 6 filas y  5 columnas de */
                       /* números reales */
Inicialización de vectores

Cuando se declara un vector sólo se inicializa con cero si se trata de una variable global. En caso contrario su contenido inicial será basura

  tipodato nombrearray[tam1]...[tamN]={listavalores};
  tipodato nombrearray[] = {listavalores};
  tipodato nombrearray[][tam2]..[tamN]={listavalores};
  int digit [10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  int impares[] = {1, 3, 5, 7, 9};
  int matriz[3][4] = { s00, 01, 02, 03,
                       10, 11, 12, 13,
                       20, 21, 22, 23};
  char letras[][5] = { ‘a’, ’b’, ’c’, ’d’, ’e’,
                       ’f’, ’g’, ’h’, ’i’, ’j’,
                       ’k’, ’l’, ’m’, ’n’, ’o’,
                       ’p’, ’q’, ’r’, ’s’, ’t’,
                       ’u’, ’v’, ’x’, ’y’, ’z’};
  int matriz[FIL][COL], f, c; /* Declaraciones */
  for(f=0 ; f<FIL ; f++)
     for(c=0 ; c<COL ; c++) {
         printf("Introduce el dato [%d][%d]", f, c);
         scanf("%d", &matriz[f][c]);
     }

Cadenas

Una cadena de caracteres, string o cadena es un vector unidimensional en el que todos sus elementos son de tipo char y el último elemento es el carácter nulo «('0')»

  char nombrecadena[longcad];

Inicializaciones en la declaración

  char nombrecadena[longcad]= "cadena";
  char nombrecadena[longcad]= {listacaracteres};
  char nombre[6]= "Pedro";
  char apellido[]={'R', 'u', 'a', 'n', 'o', ''};
Funciones que utilizan las cadenas de caracteres
Vectores de cadenas de caracteres

Un vector de cadenas es un vector bidimensional en el que el índice izquierdo señala el número de cadena y el índice derecho la longitud máxima de las cadenas

  char nombrearray[numcad][longcad];
  char frases[3][80]= {"Error de lectura",
                       "Error de escritura",
                       "Error de acceso" };
  /* frases[0] representa la cadena "Error de lectura"
     y puede mostrarse en pantalla escribiendo puts(frases[0]); */
  /* La longitud de 80 caracteres nos asegura que quepan todas las frases,
     aunque en algunas se desaproveche la memoria */

Datos definibles

Estructuras

Una estructura es un tipo de variable especial que permite almacenar datos de tipos diferentes con un identificador común

  struct nombretipoestructura
  {
     tipodato1 nombrelemento1;
     tipodato2 nombrelemento2;
     ...
     tipodatoN nombrelementoN;
  };
  struct nombretipoestructura variablestructura;
  struct nombretipoestructura
  {
     tipodato1 nombrelemento1;
     tipodato2 nombrelemento2;
     ...
     tipodatoN nombrelementoN;
  } listavariablestructura;
  struct Militar    /* Tipo de estructura */
  {
      char nombre[40];
      char apellidos[80];
      unsigned edad;
      float estatura;
      unsigned long telefono;
  } cabo, sargento, teniente;
 
  struct Militar capitan;    /* cabo, sargento, teniente y */
                             /* capitan son variables      */
                             /* estructuras del tipo militar */
  variablestructura.nombrelemento
  &variablestructura.nombrelemento
  /* Inicialización de algunos campos de la variable cabo
     de tipo estructura militar */
  gets(cabo.nombre);
  cabo.telefono = 916830106;
  scanf("%f", &cabo.estatura);

Uniones

Constituyen una porción de memoria compartida por variables de diferentes tipos

  union nombretipounion
  {
    tipodato1 nombrelemento1;
    tipodato2 nombrelemento2;
    ...
    tipodatoN nombrelementoN;
  };
  union nombretipounion variableunion;
  union Talla
  {
    int numero;         /* 38, 40, 42 */
    char letra;         /* P, M, G */
    char letras[4];     /* L, XL, XXL */
  } camiseta, camisa, jersey;
 
  camiseta.numero = 44;
  scanf("%c",&camiseta.letra);
  gets(camiseta.letras);
 
  /* Primero se almacena el entero 44, después se almacena la letra leída con
     scanf() y al final se guarda una cadena de hasta 3 caracteres (más el nulo).
     Los anteriores se pierden */
  /* Podría utilizarse para guardar la talla en un formato diferente
     en cada variable */

Campos de bits

Un campo de bits es un tipo especial de elemento de una estructura en el que se puede definir su tamaño en bits

  tipodato nombrecampo:longitud;
  struct Campobit{
            int entero;
            unsigned sietebits:7;
            char letra
         } trescampos;
  trescampos.sietebits = dato;

Definición de tipos con typedef

La expresión typedef permite dar un nombre particular a cualquier tipo de dato válido

  typedef tipodatovalido nuevonombre;
  typedef struct Militar  {
                          ...
                          } Midato;
  Midato soldado, cabo, sargento;

Punteros

Un puntero es una variable que contiene la dirección de memoria de otra variable

Los punteros constituyen una importante herramienta en lenguaje C

Memoria de datos
Etiqueta Dirección Contenido
dato1 0x00000F00 1234
dato2 0x00000F04 6789
puntdat1 0x00000F08 4321
puntdat2 0x00000F0C 7500

Declaración e inicialización

  tipodato *nombrepuntero;
  tipodato *punt;   /* Declaración del puntero*/
  tipodato var;     /* Declaración de la variable */
  punt = &var;      /* Inicialización del puntero */
  /* La variable var no contiene todavía ningún dato válido */
  float *punt;   /* Puntero */
  float var;     /* Variable */
  /* La variable y el puntero deben ser el mismo tipo de dato */
 
  punt = &var;   /* Inicialización del puntero */
  *punt = 7.98;  /* Inicialización de la variable */
                 /* Equivalente a lo anterior:
  var = 7.98;    */
Operadores de memoria

Los operadores de punteros son «*» y «&», se asocian de derecha a izquierda y tienen mayor precedencia que todas las operaciones aritméticas y lógicas

Operaciones

  int dato, *punt1, *punt2;  /* Declaraciones */
  punt1 = &dato;             /* Inicialización de punt1 */
  punt2 = punt1;             /* asignación del contenido de punt1 a punt2.
                                Ahora punt2 apunta a la variable dato */
Aritmética con punteros
Tipos de punteros
  void *nombrepuntero;
  tipodato *nombrepuntero = NULL;
  tipodato *const nombrepuntero;
  const tipodato *nombrepuntero;
  const tipodato *const nombrepuntero;

Relación entre punteros y vectores

En general, todo lo que puede hacerse con vectores puede hacerse también con punteros, si bien, las versiones con punteros son más rápidas y más utilizadas

  /* Mediante vectores: */
  elementoM = nombrearray[M];
  /* Mediante punteros: */
  elementoM = *(nombrearray+M);
  char *nombrepuntero = "cadena";
  char *mensaje = "Error de lectura";
  puts(mensaje);

Un vector de punteros contiene elementos que son direcciones a elementos un tipo determinado

  tipodato *nombrevariable[tamaño];

  char mens[3][80] = {"Inicial", "Central", "Último"};
  /* Vector de 3 cadenas. Se reserva más memoria de la necesaria para que quepan todos los  mensajes */
  puts(mens[1]);    /* Saca el mensaje "Central" */
  char *mens[3];          /* Vector de 3 punteros a carácter */
  mens[0]= "Inicial";     /* Inicialización de punteros */
  mens[1]= "Central";
  mens[2]= "Último";
  puts(mens[1]);          /* Saca el mensaje "Central" */

Puntero a un puntero

Un puntero a puntero representa una indirección múltiple: el primer puntero contiene la dirección del segundo puntero, el cual apunta al dato

  tipodato **nombrepuntero;

false

Declaraciones complejas

La combinación de:

dan lugar a declaraciones complejas y difíciles de descifrar

Para interpretar correctamente las declaraciones:

  1. Se debe comenzar con el identificador, mirando a su derecha
    • Los paréntesis indicarán que es una función
    • Los corchetes indicarán que es un vector
  2. Mirar si a la izquierda hay un asterisco, lo que indicará que es un puntero
  3. Aplicar las reglas anteriores a cada nivel de paréntesis y de dentro hacia fuera
  int (*lista)[20];       /* lista es puntero a vector de 20 enteros */
  char *datos[20];        /* datos es vector de 20 punteros a carácter */
  void (*busc)();         /* busc es puntero a función que no devuelve nada */
  char (*(*Func())[])();  /* Func es función que devuelve un puntero a vector de
                             punteros a funciones que devuelven un carácter */
  int (*(*tim[5])())[3];  /* tim es vector de 5 punteros a funciones que devuelven
                             cada una un puntero a un vector de 3 enteros */

Asignación dinámica de memoria

En la declaración de variables, el compilador de C reserva memoria para cada variable:

La asignación dinámica de memoria consiste en la reserva de memoria para los datos en tiempo de ejecución, es decir, tomándola de la que el sistema tiene libre en ese momento.

Un programa en ejecución es un proceso activo que tiene a su disposición:

Asignación y liberación

  void *malloc(unsigned tamanio);
  void free(void *nombrepuntero);
  int *dato;  /* Puntero a int */
  dato = (int *)malloc(sizeof(int));
  if (dato==NULL)
     printf("Error en la asignación");
     /* ...
        Operaciones de utilización de la memoria asignada
        ...
     */
  free(dato); /* Liberación de la memoria asignada  dinámicamente */

Vectores dinámicos

Los vectores dinámicos son aquellos cuyo tamaño se determina en un proceso de asignación dinámica de memoria

  void * calloc(numelementos, tamañoelemento);

* Ejemplo de reserva de memoria para un vector de 10 elementos de tipo double:

  punt = (double*)calloc(10, sizeof(double));

  int *lista;    /* Puntero a int */
  lista = (int *)calloc(N, sizeof(int));
  if (lista==NULL)
     printf("Error en la asignación");
     /* ...
        Operaciones de utilización de la memoria asignada
        ...
     */
  free(lista); /* Liberación de la memoria asignada  dinámicamente */
  float **punt;  /* Puntero a puntero a float */
  int i,j;
  punt = (float **)calloc(NFIL , sizeof(float *))
                 /* Vector de NFIL punteros a float */
  for (i=0 ; i<NFIL ; i++;)
     punt[i] = (float *)calloc(NCOL , sizeof(float));
                 /* Se dispone de NFIL vectores de NCOL números reales.
                    Los datos pueden ser referenciados mediante
                    expresiones del tipo punt[i][j] */
  for (i=0 ; i<NFIL ; i++) free(punt[i]);
                 /* Se liberan los vectores unidimensionales
                    (las filas //  de la matriz de números reales) */
  free(punt);    /* Se libera la memoria del vector unidimensional de
                    punteros a float */

Reasignación de bloques de memoria

Es posible reasignar un bloque de memoria previamente asignado dinámicamente (para redimensionar ese bloque)

  void * realloc(void *puntbloc, numbytes);