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 */
double
F
para indicar float
L
para indicar long double
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 */
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 NOMBRECONSTANTE
NOMBRECONSTANTE
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;
const
volatile
unsigned int const year = 2018;
extern
static
(dentro de una función)static
(fuera de una función)register
auto
En 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 int
int b*c
se convierte a float
y se divide entre d
. Se obtiene un float
a
se convierte a float
y se suma a b*c/d
. Se obtiene un float
float
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á expresion
7/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.5
Un 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.h
stdlib.h
string.h
scanf()
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 cadena1
strcpy(cadena1,cadena2);
copia cadena2
en cadena1
strcmp(cadena1,cadena2);
compara cadena1
y cadena2
strlen(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 .h
sizeof()
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 identificador
sizeof
»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.h
0
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.h
void free(void *nombrepuntero);
nombrepuntero
es el identificador del puntero (de cualquier tipo) que apunta al bloque de memoria que se desea liberarstdlib.h
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 */
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