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;
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:
char Carácterint Número enterofloat Número realdouble Número real de doble precisiónvoid Tipo que no existeenum Tipo enumeración, lista de valores enterosLos modificadores que se pueden aplicar a los tipos de datos básicos son:
signed Con signounsigned Sin signolong Largo, de mayor tamaño de almacenamientoshort Corto, de menor tamaño de almacenamientoLos datos fundamentales utilizados en lenguaje C se obtienen de las combinaciones permitidas de tipos básicos y modificadores.
Los tipos de datos enteros permiten representar cantidades numéricas enteras
char (signed char) Tipo carácterint (signed int) Tipo entero con signoshort (signed short int) Tipo entero cortolong (signed long int) Tipo entero en formato largoenum Tipo enumeradoLa relación entre tamaños que se cumple siempre es:
short ≤ int ≤ long
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;
1».6» (lunes equivale a «0»)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:
float Tipo real de simple precisióndouble Tipo real de doble precisiónlong double Tipo real de doble precisión con formato largovoid indica un dato de tipo indefinidotypedef tipodato nuevonombre;
typedef unsigned long int mitipo; /* Se ha creado un nuevo tipo de dato: mitipo */
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:
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:
U para indicar unsignedL para indicar longUL para indicar unsigned long* 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 */
doubleF para indicar floatL para indicar long double35.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 */
char y se expresan poniendo el carácter entre comillas simples: 'A''\código''\ddd''\0oo''\0xhh''6' /* Carácter 6, código ASCII 0x36 */ '\12' /* Código ASCII 12 (Salto de línea) */ '\0x20' /* Códgio ASCII 32 (Espacio) */
"Esto es una cadena de caracteres"'\0'» #define NOMBRECONSTANTE Equivalencia
NOMBRECONSTANTE es el identificador de la constante simbólica (recomendado en mayúsculas)Equivalencia representa los símbolos que va a representar NOMBRECONSTANTENOMBRECONSTANTE será sustituido antes de compilar por Equivalencia#define MAXIMO 100 /* MAXIMO toma el valor 100 */ #define FRASE "Pulsa una tecla"
Todas las variables deben declararse antes de ser utilizadas para que el compilador les asigne la memoria necesaria
tipodedato nombrevariable;
tipodedato representa la palabra o palabras que definen el tipo de datonombrevariable es el identificador de la variablechar 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.
auto)extern en el fichero en que se utilicenlong int Mifuncion(int base, int exponente) { /* Cuerpo de la función */ }
La inicialización de variables sirve para asignar el primer valor
tipodato nombrevariable = valorinicial;
unsigned int edad = 25;
constvolatileunsigned int const year = 2018;
externstatic (dentro de una función)static (fuera de una función)registerautoEn las expresiones, los operandos cambian de tipo automáticamente
double por omisiónchar y short se convierten a int si el int puede representar todos los valores del tipo original, o a unsigned int en caso contrariolong a char b; int c, f; float d; f = a + b * c / d;
b se convierte al tipo de c (int) y se realiza b*c.Se obtiene un intint b*c se convierte a float y se divide entre d. Se obtiene un floata se convierte a float y se suma a b*c/d. Se obtiene un floatfloat resultante de a+b*c/d se convierte a int (eliminando la parte fraccionaria) y se guarda en la variable entera f
Conversión explícita: operador cast
(tiponuevo)expresion;
tiponuevo es el tipo de dato al que se convertirá expresion7/2 da como resultado 3, sin embargo la expresión (float)7/2 convierte el 7 en real y el resultado será un número real: 3.5Un vector es un tipo de variable especial que permite almacenar un conjunto de datos del mismo tipo
La declaración de un vector supone reservar memoria para sus elementos
tipodato nombrearray[tamaño];
tipodato representa el tipo de datos de los elementos del vector (cualquiera excepto void)nombrearray es el identificador del vector y de sus elementostamaño representa un valor entero y constante que indica el número de elementos del vectornombrearray[0] y el último es nombrearray[n-1]num_bytes = tamaño * sizeof(tipodato)
tipodato nombrearray[tamaño1][tamaño2]...[tamañoN];
tamaño1, tamaño2, tamañoN son expresiones constantes enterasnum_bytes = tamaño1 * tamaño2 * ... * tamañoN * sizeof(tipodato)
tipodato nombrearray[numfilas][numcolumnas];
numfilas indica el número de filasnumcolumnas indica el número de columnasint 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 */
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};
listavalores es una relación de constantes del tipo declarado para el vector entre llaves «{}» y separadas por comastam1):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’};
FILxCOL enteros el código necesario será similar la siguiente: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]); }
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];
nombrecadena es un identificador válido para la cadena completalongcad es una constante entera que indica el número de elementos de la cadena incluido el nulo final.Inicializaciones en la declaración
char nombrecadena[longcad]= "cadena"; char nombrecadena[longcad]= {listacaracteres};
nombrecadena es el identificador de la cadenalongcad es una constante entera que determina el número de caracteres de la cadena incluido el carácter nulo (si es menor producirá error y si es mayor se rellenarán con “nulos”)listacaracteres representa un conjunto de constantes de carácter (entre comillas simples «' '»y separados por comas) que deberá incluir el nulo '0' al finalchar nombre[6]= "Pedro"; char apellido[]={'R', 'u', 'a', 'n', 'o', ''};
stdio.hstdlib.hstring.hscanf() permite leer una cadena de caracteres desde el teclado, con las siguientes condiciones:scanf(“%[^n]s”,cadena); nos permite leer una cadena completa hasta pulsar return ('n')cadena, sin corchetes y sin que vaya precedido por el operador «&», es el identificador de la cadenafflush(stdin) previamente para limpiar el buffer de entrada para eliminar los caracteres no recogidosprintf() permite, utilizando el especificador de formato %s, imprimir cadenas de caracteresgets() lee una cadena completa y sustituye el return por el caracter nulo al almacenarlaputs() escribe una cadena completa, sustituyendo previamente el caracter nulo por el returnstrcat(cadena1,cadena2) concatena cadena2 al final de cadena1strcpy(cadena1,cadena2); copia cadena2 en cadena1strcmp(cadena1,cadena2); compara cadena1 y cadena2strlen(cadena); devuelve la longitud de la cadenastrlwr(cadena); convierte los caracteres de cadena a minúsculasstrupr(cadena); convierte los caracteres de cadena a mayúsculasatof(cadena); convierte la cadena a un decimal en doble precisión equivalente al representado por los dígitos que cadena contieneatoi(cadena); convierte la cadena a un enteroatol(cadena); convierte la cadena a un entero largofcvt(); convierte un número real a una cadena 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 */
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;
nombretipoestructura es el identificador del tipo de estructura que se está definiendotipodatoX representa al diferente tipo de dato de cada elementonombrelementoX son los identificadores propios de los elementos de la estructuralistavariablestructura representa al identificador (o una lista de estos) de la variable de estructura que se va a crearmain() en los archivos cabecera .hsizeof()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 es el nombre de la variable de estructura en la que se encuentra el elemento que se quiere referenciarnombrelemento es el nombre del elemento de la estructura.» une los dos identificadores en uno solo&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);
Constituyen una porción de memoria compartida por variables de diferentes tipos
.», del mismo modo que en las estructurasunion 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 */
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;
tipodato es el tipo del dato, que sólo puede ser entero (int, signed, unsigned, char, short, long, …)nombrecampo representa el nombre del elemento que va a ser un campo de bitslongitud representa a un entero positivo que indica el número de bits de ese campostruct Campobit{ int entero; unsigned sietebits:7; char letra } trescampos; trescampos.sietebits = dato;
La expresión typedef permite dar un nombre particular a cualquier tipo de dato válido
typedef tipodatovalido nuevonombre;
tipodatovalido representa un tipo de dato válidonuevonombre es el nuevo identificador para ese tipo de datotypedef struct Militar { ... } Midato; Midato soldado, cabo, sargento;
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 |
tipodato *nombrepuntero;
tipodato es el tipo de dato de la variable a la que apuntará el puntero.nombrepuntero es la etiqueta de la zona de memoria que contendrá la dirección de una variable*nombrepuntero hace referencia al contenido de la variable apuntada por el punterotipodato *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; */
int *punt=25; hace que el puntero apunte a la dirección 25 (y no sabemos qué contiene)
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
&»*»*identificador hace referencia al dato contenido por la dirección identificadorsizeof»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 */
void *nombrepuntero;
NULL (= 0)tipodato *nombrepuntero = NULL;
NULL es una constante definida en stdio.h0 nunca es válidatipodato *const nombrepuntero;
const tipodato *nombrepuntero;
const tipodato *const nombrepuntero;
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";
nombrepuntero es un puntero a carácter: contiene la dirección del primer elemento de la cadenacadena es una cadena constante que se almacena en una tabla de constantes. Finaliza con el carácter nulo '0'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];
tipodato es el tipo de datos de los elementos que serán apuntadosnombrevariable es el nombre del vector de punterostamaño indica el número de punteros que contendrá el vectorchar 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" */
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;
nombrepuntero contiene la dirección de *nombrepuntero que, a su vez, contiene la dirección de nombrepuntero * nombrepuntero es el identificador del datodatos[M][N] es un vector bidimensional, el elemento datos[i][j] puede accederse también mediante la aritmética de punteros: *(*(datos+i)+j)false
La combinación de:
*»[]»()» que agrupan operaciones o los indicadores de funcióndan lugar a declaraciones complejas y difíciles de descifrar
Para interpretar correctamente las declaraciones:
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 */
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:
void *malloc(unsigned tamanio);
tamanio es un entero sin signo que indica el número de bytes que se solicitan al sistema operativoNULL) *void que puede apuntar a cualquier tipo válidostdlib.hvoid free(void *nombrepuntero);
nombrepuntero es el identificador del puntero (de cualquier tipo) que apunta al bloque de memoria que se desea liberarstdlib.hint *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 */
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);
numelementos es un entero sin signo que representa el número de elementos del vectortamañoelemento es un entero sin signo que representa el tamaño de un elemento del vectorstdlib.h
* 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 */
NFIL filas y NCOL columnas) y su liberación posterior: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 */
Es posible reasignar un bloque de memoria previamente asignado dinámicamente (para redimensionar ese bloque)
void * realloc(void *puntbloc, numbytes);
puntbloc es un puntero que apunta al bloque de memoria que se quiere reasignarnumbytes es un número entero sin signo que indica el nuevo tamaño en bytes que deberá tener el bloque de memoriaNULL)stdlib.h