char
. Este tipo ocupa un byte (ocho bits) de extensión, y puede interpretarse con y sin signo. La tabla de tipos de datos de carácter en C es como sigue:
Nombre | Extensión | Alcance |
---|---|---|
char
|
8 bits | Cualquier código ASCII |
signed char
|
8 bits | desde -128 hasta +127 |
unsigned char
|
8 bits | desde 0 hasta 255 |
#include<stdio.h> char letra; signed char letraconsigno; unsigned char letrasinsigno; int main(int argc, char * argv[]) { letra = 'A'; printf("La letra es %c y su valor decimal es %d.\n\n", letra,letra); letraconsigno = -65; printf("Letraconsigno es %d y su valor decimal es %d.\n\n", letraconsigno,letraconsigno); letrasinsigno = 165; printf("Letrasinsigno es %u y su valor decimal es %d.\n\n", letrasinsigno,letrasinsigno); return 0; }
La letra es A y su valor decimal es 65. Letraconsigno es -65 y su valor decimal es -65. Letrasinsigno es 165 y su valor decimal es 165.Como puede apreciarse, el descriptor empleado para la lectura y escritur de variables de tipo char es
%c
.
#include<stdio.h> /* Este programa muestra los peligros que acechan al incauto lector de caracteres */ char unaletra, otraletra; void main(void) { printf("Escriba una letra y pulse intro, por favor: "); scanf("%c", &unaletra); printf("La letra que ha pulsado era %c y su valor decimal es %d\n", unaletra, unaletra); printf("Ahora escriba otra letra, por favor: "); scanf("%c", &otraletra); printf("La letra que ha pulsado era %c y su valor decimal es %d\n", otraletra, otraletra); printf("\n\nTerminación normal del programa.\n\n"); } /* El resultado de la ejecución de este programa es: Escriba una letra y pulse intro, por favor: a La letra que ha pulsado era a y su valor decimal es 97 Ahora escriba otra letra, por favor: La letra que ha pulsado era y su valor decimal es 10 Terminación normal del programa. ¿Qué ha pasado? */
%c%*c
en lugar de %c.
También se puede recurrir a las funciones gets() y fgets(), que ofrece stdio.h, y que sirven para leer cadenas. La primera, gets() lee del teclado hasta que encuentra el retorno de carro; ése retorno no se incluye en la cadena proporcionada y gets() podría servir muy bien para vaciar el búfer si no fuera peligrosa: es responsabilidad del usuario reservar espacio suficiente para la cadena leída. Dado que no es posible anticipar con seguridad lo que hará el usuario, resulta más prudente emplear fgets(). Esta función si permite determinar el número máximo de caracteres leídos, y devuelve un puntero nulo cuando el búfer está vacío. Una tercera solución, finalmente, consiste en invocar a la función fpurge(stdin)
inmediatamente después de leer el carácter. De este modo se descarta directamente el contenido restante en el búfer de teclado. Sólo hay un problema: es propia de Unix, y los entornos que no están basados en este estándar suelen carecer de ella.
'\0'
, que se emplea como marcador de fin de cadena. Obsérvese que se ha encerrado la expresión \0 entre comillas sencillas y no dobles. Esto denota que se trata de un carácter; si se hubiera encerrado entre comillas dobles se trataría de la cadena formada por este carácter... y tendría dos bytes de longitud, siendo el último, precisamente, '\0'
. La adición de este carácter de finalización es imprescindible, toda vez que será empleado por todas las funciones de tratamiento de cadenas.
Las cadenas de C se declaran como listas de caracteres. El carácter de terminación también cuenta, y ocupa un byte igual que cualquier otro. Por tanto, una cadena para la cual se reserven 80 caracteres podrá contener un máximo de 79 caracteres del usuario, más el carácter de terminación. Esto no implica, desde luego, que sea obligatorio emplear siempre todos los caracteres reservados... con el consiguiente desperdicio de espacio en la mayoría de las ocasiones. La función strlen()
,que pertenece a string.h
, permite determinar el número exacto de caracteres de la cadena utilizados realmente. El operador sizeof()
proporciona el número total de caracteres reservados para la cadena. Consúltense las páginas man
.
Las cadenas se declaran mediante expresiones de la forma siguiente: char cadena[numero_total_de_bytes_reservados];o bien
char cadena[] = "Algún valor";
o bien, incluso,
char cadena[] = { 'U', 'n', 'o', 's', ' ',
'c', 'u', 'a', 'n', 't', 'o', 's', ' ',
'c', 'a', 'r', 'c', 't', 'e', 'r', 'e', 's', '\0'};
Obsérvese que el compilador reserva, en los dos últimos casos, el espacio necesario para almacenar la cadena deseada. El compilador, en el caso de dar valor inicial a la cadena, incluyen el marcador de fin de cadena '\0'
aún cuando éste no se indique explícitamente. No sucede lo mismo cuando se omite el carácter '\0'
en la última declaración. Véase el siguiente ejemplo:
#include<stdio.h>
int main(int arcg, char* argv[]) {
char c1[8];
char c2[] = "Hola";
char c3[] = { 'U', 'n', 'o', 's', ' ',
'c', 'u', 'a', 'n', 't', 'o', 's', ' ',
'c', 'a', 'r', 'a', 'c', 't', 'e', 'r', 'e', 's', '\0'};
strcpy(c1,"hola");
printf("Esto es c1 %s\n", c1);
printf("Esto es c2 %s\n", c2);
printf("Esto es c3 %s\n", c3);
return 0;
}
Obsérvese la necesidad de emplear la función strcpy()
para dar valor a c1
, que inicialmente no había recibido valor alguno. El compilador no admite el operador de asignación como método para dar valor a esta cadena, porque intentaría interpretar esa expresión empleando la lógica de punteros. En efecto: intente cambiar la expresión strcpy(c1, "Hola")
por c1 = "Hola"
y compile el programa.
'\0'
) al declarar c3
. Esto se debe a que printf()
sigue imprimiendo caracteres en pantalla hasta llegar (casualmente) a un byte de valor 0
.
scanf()
y printf()
respectivamente. Es preciso tener en cuenta que scanf()
detiene la lectura de caracteres en cuanto encuentra un espacio en blanco o tabulador. Véase un ejemplo
Ejemplo #include<stdio.h> /* Este programa muestra la utilización de cadenas en C */ char cadena[8], resto[80]; void main(void) { printf("Escriba una cadena: "); scanf("%s", cadena); /* Las cadenas NO llevan & */ printf("\n\nLa cadena leída era %s\n\n", cadena); printf("Y el resto era"); gets(resto); puts(resto); printf("Terminación normal del programa.\n\n");}
gets()
captura todo lo restante, y después se imprime mediante puts()
. Hay que ser precavidos a la hora de "dejar" restos en el tampón. Lo mejor es eliminar posibles restos antes de pasar a la lectura siguiente, empleando fflush(stdin)
o bien fpurge(stdin)
.
string.h
contiene numerosas funciones relacionadas con el tratamiento de caracteres. Véase a continuación una tabla de funciones útiles:
/* * Funciones de concatenación */
Encabezado | Descripción |
---|---|
char *strcat (char *s1, const char *s2);
|
Esta función añade s2 a s1 y pone un '\0' al final del resultado; se supone que s1 contiene espacio suficiente para el total de caracteres (los de s1 , los de s2 y el '\0' ). Proporciona como resultado un puntero de s1
|
char *strncat (char *s1, const char *s2, size_t n);
|
Análoga a la anterior, pero añade a s1 un máximo de n caracteres de s2 . Proporciona como resultado un puntero de s1
|
/* * Funciones de comparación */
Encabezado | Descripción |
---|---|
int memcmp (const void *s1, const void *s2, size_t n);
|
Compara byte por byte las dos cadenas cuya dirección se proporciona, suponiendo que ambas tuvieran n bytes de longitud. Si ambas series de bytes son iguales, la función proporciona el valor 0. Si no son iguales, la función devuelve la diferencia entre los dos primeros bytes distintos. La comparación es numérica, sin tener en cuenta el orden alfabético internacional, lo cual produce resultados extraños cuando las cadenas contienen signos diacríticos.
|
int strcmp (const char *s1, const char *s2);
|
Esta función compara byte por byte las cadenas de caracteres señaladas por s1 y s2 . El resultado es:
|
int strcoll (const char *s1, const char *s2);
|
Esta función sirve para comparar dos cadenas, y hace uso de la secuencia lexicográfica (locale ) definida en el momento de su ejecución. Produce valores numéricos análogos a los de strcmp()
|
int strncmp (const char *s1, const char *s2, size_t n);
|
Esta función es una variante de strcmp() que compara únicamente los n primeros caracteres de ambas cadenas.
|
size_t strxfrm (char *s1, const char *s2, size_t n);
|
Almacena en s1 la traducción de la cadena s2 a la configuración de locale definida en el momento de la ejecución. Esta función puede no estar presente en todas las implementaciones de la biblioteca de C; consúltense las páginas man de la máquina utilizada.
|
/* * Funciones de búsqueda */
Encabezado | Descripción |
---|---|
void *memchr (const void *s, int c, size_t n);
|
Determina la posición de la primera aparición de c (que se interpreta como carácter sin signo) dentro de los n primeros bytes de la cadena s . Si c forma parte de s , se proporciona como resultado un puntero de c . Si c no forma parte de s , se proporciona el puntero nulo.
|
char *strchr (const char *s, int c);
|
Análoga a la anterior, salvo que se explora toda la cadena, incluyendo el marcador final '\0' . Por tanto, si se busca '\0' , se encontrará el marcador de fin de cadena.
|
size_t strcspn (const char *s1, const char *s2);
|
Esta función calcula la longitud del segmento máximo de s1 que está formado por caracteres que no están presentes en s2 . Dicho en pocas palabras, sirve para calcular la longitud de la subcadena complementaria.
|
char * strpbrk (const char *s1, const char *s2);
|
Esta función busca en s1 la primera aparición de cualquier carácter de los contenidos en s2 y proporciona su dirección (mediante un puntero de carácter). Si s1 no contiene ningún carácter de s2 , se proporciona el puntero nulo.
|
char *strrchr (const char *s, int c);
|
Esta función busca un carácter c dentro de la cadena s , empezando por el final. Si lo encuentra, proporciona su dirección mediante un puntero. Si no lo encuentra, proporciona el puntero nulo. Dicho de otro modo, strrchr() busca la última aparición de c en s .
|
size_t strspn (const char *s1, const char *s2);
|
Esta función calcula la longitud del máximo segmento inicial de s1 que está formado completamente por caracteres presentes en s2. |
char *strstr (const char *s1, const char *s2);
|
Esta función busca la primera aparición de s2 dentro de s1 , sin tener en cuenta el carácter de fin de cadena. Si s2 se encuentra en s1 , la función proporciona un un puntero del primer byte de s2 dentro de s1 . Si no se encuentra s2 dentro de s1 , se proporciona el puntero nulo.
|
char *strtok (char *s1, const char *s2);
|
Esta función extrae subcadenas (tokens) de s1 . Los separadores de subcadenas son los caracteres que contenga s2 . La primera subcadena se proporciona (mediante un puntero) cuando se llama a strtok() empleando s1 y s2 como parámetros. La segunda subcadena (y siguientes) se obtienen mediante llamadas a strtok() con el puntero nulo como primer argumento, y con la cadena de separadores como segundo argumento (ésta se puede modificar en las distintas llamadas). Cuando ya no quedan más subcadenas, la función proporciona el puntero nulo. Obsérvese la notable utilidad de esta función para la lectura de archivos de texto con formato delimitado. Obsérvese también que, una vez invocada la cadena por primera vez, se admiten cambios en el delimitador especificado (¡que es una cadena! Se pueden emplear delimitadores "complejos", formados por múltiples caracteres.)
|
/* * Varios */
Encabezado | Descripción |
---|---|
void *memset (void *s, int c, size_t n); | Sirve para rellenar la cadena señalada por s , copiando en sus bytes el valor de c , interpretado como carácter sin signo. Se escribe un máximo de n bytes. |
char *strerror (int errnum);
| Esta función recibe como argumento un número de error y proporciona como resultado el mensaje de error correspondiente. Los números y mensajes de error dependen por completo del compilador (y de la plataforma). |
size_t strlen (const char *s); | Proporciona la longitud de la cadena señalada por s . |
funciones.h | /* * funciones.h * cadenas * * Created by coti on 28/08/09. * Copyright 2009 Tests by Coti. All rights reserved. * */ #include <stdio.h> /* aquí vive strlen() */ #include <string.h> /* CONSTANTES */ #define NUM_BYTES 128 #define LONGITUD_1 64 #define LONGITUD_2 64 void leer(char * p1, char * p2); void mostrar(char * p1, char * p2); void recorrer(char * p1, char * p2); void comparar(char * p1, char * p2); void concatenar(char * p1, char * p2, char * pc); void buscar(char * p1, char * p2, char * pc); |
funciones.c | /* * funciones.c * cadenas * * Created by coti on 28/08/09. * Copyright 2009 Tests by Coti. All rights reserved. * */ #include "funciones.h" void leer(char * p1, char * p2) { printf("\nEscriba la primera palabra: "); fgets(p1, LONGITUD_1, stdin); p1[strlen(p1)-1] = '\0'; printf("\nEscriba la segunda palabra: "); fgets(p2, LONGITUD_2, stdin); p2[strlen(p2)-1] = '\0'; printf("\n"); } void mostrar(char * p1, char * p2) { printf("\nLa primera palabra es %s y la segunda es %s\n\n", p1, p2); } void recorrer(char * p1, char * p2) { int i; printf("\nLos caracteres de la palabra %s son:\n\n",p1); for (i=0; i<strlen(p1); i++) { printf("%3d %c\n", (unsigned char)p1[i], p1[i]); } printf("\nLos caracteres de la palabra %s son:\n\n",p2); for (i=0; i<strlen(p2); i++) { printf("%3d %c\n", (unsigned char)p2[i], p2[i]); } printf("\n"); } void comparar(char * p1, char * p2) { int resultado = strcmp(p1, p2); /* convertimos el resultado en un valor q es 1, 0 o -1 */ resultado = resultado > 0 ? 1 : (resultado < 0 ? -1 : 0); switch (resultado) { case -1: printf("\n%s es menor que %s\n",p1,p2); break; case 0: printf("\n%s es igual que %s\n",p1,p2); break; case 1: printf("\n%s es mayor que %s\n",p1,p2); break; default: break; } printf("\n"); } void concatenar(char * p1, char * p2, char * pc) { strcpy(pc, ""); strcat(pc, p1); strcat(pc, " "); strcat(pc, p2); printf("\nLa concatenación de %s y %s es %s\n\n",p1, p2, pc); } void buscar(char * p1, char * p2, char * pc) { char * resultado = NULL; printf("\nEscriba la palabra que busca: "); fgets(pc, NUM_BYTES, stdin); pc[strlen(pc)-1] = '\0'; resultado = strstr(p1, pc); if (NULL == resultado) printf("\n%s no contiene a %s\n",p1,pc); else { printf("\n%s contiene a %s\n",p1,pc); } resultado = strstr(p2, pc); if (NULL == resultado) printf("\n%s no contiene a %s\n",p2,pc); else { printf("\n%s contiene a %s\n",p2,pc); } printf("\n"); } |
main.c | #include <stdio.h> /* malloc() vive aquí */ #include <stdlib.h> /* toupper() vive aquí */ #include <ctype.h> /* Las funciones viven aquí */ #include "funciones.h" /* Este programa muestra ejemplos de las operaciones fundamentales con cadenas: - declaración (estática) - lectura - escritura - recorrido - comparación - concatenación - búsqueda de fragmentos */ int main (int argc, const char * argv[]) { /* Las variables palabra_1 y palabra_2 son cadenas de LONGITUD_1 y LONGITUD_2 bytes respectivamente, y se declaran estáticamente. */ char palabra_1[LONGITUD_1], palabra_2[LONGITUD_2]; /* La variable pcadena señala un bloque de memoria de dimensiones iguales a la suma de dimensiones de palabra_1 y palabra_2. */ char * pcadena = malloc((LONGITUD_1+LONGITUD_2)*sizeof(char)); /* Este es el menú q muestra la aplicación */ char menu[] = "1)Leer 2)Mostrar 3)Recorrer 4)Comparar\n5)\ Concatenar 6)Buscar fragmento Q) Salir"; /* Esta es la opción q escribe el usuario */ char opcion; /* Esta es la condición de finalización del programa */ int terminar = 0; do { printf("%s ", menu); scanf("%c%*c",&opcion); opcion = toupper(opcion); switch (opcion) { case '1': printf("\nLectura de cadenas\n"); leer(palabra_1, palabra_2); break; case '2': printf("\nEscritura de cadenas\n"); mostrar(palabra_1, palabra_2); break; case '3': printf("\nRecorrido de cadenas\n"); recorrer(palabra_1, palabra_2); break; case '4': printf("\nComparación de cadenas\n"); comparar(palabra_1, palabra_2); break; case '5': printf("\nConcatenación de cadenas\n"); concatenar(palabra_1, palabra_2, pcadena); break; case '6': printf("\nBuscar un fragmento\n"); buscar(palabra_1, palabra_2, pcadena); break; case 'Q': printf("\nTerminación del programa\n"); terminar = 1; break; default: break; } } while (!terminar); return 0; } |
mancorrespondientes a las distintas funciones.
Por favor, dígame el nombre : José
Por favor, dígame el apellido : García
Titular: José García.
#include<stdio.h> #define DIM_NOMBRE 12 #define DIM_APELLIDOS 10 int main(int argc, char *argv[]) { char nombre[DIM_NOMBRE], apellido[DIM_APELLIDOS]; printf("\n\nPor favor, dígame el nombre : "); fgets(nombre,sizeof(nombre),stdin); /* fgets() retiene el retorno de carro, lo descartamos */ nombre[strlen(nombre)-1] = '\0'; /* Descartamos el resto si lo hay */ fpurge(stdin); printf("\n\nPor favor,dígame el apellido : "); fgets(apellido,sizeof(apellido), stdin); /* fgets() retiene el retorno de carro, lo descartamos */ apellido[strlen(apellido)-1] = '\0'; /* Si cabe en nombre el nombre, un espacio y el apellido, lo almacenamos */ if (strlen(nombre) + strlen(" ") + strlen(apellido) < sizeof(nombre) -1) { strcat(nombre, " "); strcat(nombre, apellido); } else /* Si no cabe, intentamos añadir el blanco y lo que quepa de apellido. Hay que tener en cuenta que en nombre sobran sólamente sizeof(nombre) - strlen(nombre) - 1 caracteres. Por tanto, limitamos a éste número los caracteres añadidos (mediante strncat). */ { strncat(nombre, " ", sizeof(nombre) - strlen(nombre)-1); strncat(nombre, apellido, sizeof(nombre) - strlen(nombre)-1); printf("Resultado demasiado largo, se ha truncado a %d caracteres.\n", sizeof(nombre)-1); }; printf("\n\nTitular: %s\n", nombre); printf("\n\nFin del programa.\n\n"); return 0; }
#include<stdio.h> int main(int argc, char * argv[]) { int i; char * p; for(i=0;i<sys_nerr;i++) printf("%d.-%s\n", i, p); return 0; }
sys_nerr
) de mensajes de error.
strcmp()
).#include<stdio.h> #define DIM_primera 10 #define DIM_segunda 15 int main(int argc, char *argv[]) { char primera[DIM_primera], segunda[DIM_segunda]; int relacion, relacion2; printf("\n\nPor favor, escriba la primera cadena : "); fgets(primera,sizeof(primera),stdin); /* fgets() retiene el retorno de carro, lo descartamos */ primera[strlen(primera)-1] = '\0'; /* Descartamos el resto si lo hay */ fpurge(stdin); printf("\n\nPor favor,escriba la segunda cadena : "); fgets(segunda,sizeof(segunda), stdin); /* fgets() retiene el retorno de carro, lo descartamos */ segunda[strlen(segunda)-1] = '\0'; relacion = strcmp(primera, segunda); printf("\n\nRelación: "); if (relacion > 0) printf("%s es mayor que %s\n",primera, segunda); else if (relacion < 0) printf("%s es menor que %s\n", primera, segunda); else printf("%s es igual a %s\n", primera, segunda); /* Pero probemos lo que sucede si sólo se comparan 6 caracteres */ relacion2 = strncmp(primera, segunda, 6); printf("\n\nRelación2: "); if (relacion2 > 0) printf("%s es mayor que %s\n",primera, segunda); else if (relacion2 < 0) printf("%s es menor que %s\n", primera, segunda); else printf("%s es igual a %s\n", primera, segunda); printf("\n\nFin del programa\n\n"); return 0; } /* El resultado de la ejecución es como sigue: Por favor, escriba la primera cadena : abcdefppp Por favor,escriba la segunda cadena : abcdefzzz Relación: abcdefpp es menor que abcdefzzz Relación2: abcdefpp es igual a abcdefzzz */Como puede observarse en el resultado de la ejecución, la función strncmp() permite limitar la comparación al principio de las cadenas, produciendo resultados aparentemente falsos, aunque realmente sean exactos.
strstr()
).#include<stdio.h> #define FILAS 5 #define LONGITUD_CADENA 20 int main(int argc, char * argv[]) { char nombre[FILAS][LONGITUD_CADENA] = { "Juan", "Martín", "Pedro", "Luis", "Alonso"}; char apellido_1[FILAS][LONGITUD_CADENA] = {"Martin", "Perez", "Garcia", "Hernandez", "Lopez"}; char apellido_2[FILAS][LONGITUD_CADENA] = {"Alonso", "Gonzalez", "Martin", "Luis", "Gomez"}; char palabra[LONGITUD_CADENA]; int i; printf("\n\nBúsqueda de registros\n\n"); printf("La lista de registros es:\n\n"); for(i=0;i<FILAS;i++) printf("%d: %s %s %s\n", i, nombre[i], apellido_1[i], apellido_2[i]); printf("\n\nEscriba una palabra: "); scanf("%s", palabra); printf("\n\nPalabra buscada: %s\n\n", palabra); for(i=0;i<FILAS;i++) { if( strstr(palabra,nombre[i])!=NULL || strstr(palabra,apellido_1[i])!=NULL || strstr(palabra,apellido_2[i])!=NULL) printf("%s figura en el registro %d: %s %s %s\n", palabra, i, nombre[i], apellido_1[i], apellido_2[i]); } printf("\n\nFin del programa\n\n"); return 0; }
strtok()
).#include<stdio.h> #define FILAS 5 #define COLUMNAS 40 int main(int argc, char * argv[]) { char registro[FILAS][COLUMNAS] = { "Ana*Perez*Lopez", "Bernardo*Garcia*Gonzalez", "Carlos*Martin*Martin", "Damian*Garcia*Perez", "Elena*Gonzalez*Martin"}; int i; char * p; for(i=0;i<FILAS;i++) { printf("\n\nLos campos del registro %s son:\n\n", registro[i]); puts(strtok(registro[i], "*")); while (p = strtok(NULL,"*")) puts(p); } printf("\n\nFin del programa\n\n"); return 0; }El resultado de ejecutar este programa es precisamente el deseado:
/* Los campos del registro Ana*Perez*Lopez son: Ana Perez Lopez Los campos del registro Bernardo*Garcia*Gonzalez son: Bernardo Garcia Gonzalez Los campos del registro Carlos*Martin*Martin son: Carlos Martin Martin Los campos del registro Damian*Garcia*Perez son: Damian Garcia Perez Los campos del registro Elena*Gonzalez*Martin son: Elena Gonzalez Martin Fin del programa */