===== Strings =====
C no posee un tipo de datos para los strings como es el caso de //String// en Java. En C los strings corresponden a arreglos de caracteres o punteros a caracteres que por convención deben terminar con un byte en 0.
Por ejemplo:
char *s= "hola";
char r[]= {'h', 'o', 'l', 'a', 0};
char t[81]= { 0 };
Tanto r como s representan el mismo string. El diagrama es el siguiente:
{{ :strings2.png?nolink&400 |}}
La única diferencia es que el string "hola" queda almacenado en un área del proceso que no permite escribirla.
s[0]= 'H'; /* segmentation fault en muchas plataformas */
r[0]= 'H'; /* correcto! */
==== Funciones para manipular strings ====
Hay varias funciones que permiten manipular strings:
^ Nombre ^ Ejemplo ^ Descripción ^
| strlen | int l= strlen("hola"); | calcula el largo de un string |
| strcmp | strcmp(fuente, "hola"); | compara 2 strings entregando < 0, 0 o > 0 |
| strncmp | strncmp(fuente, "hola", n); | compara hasta n caracteres |
| strcpy | strcpy(dest, fuente); | copia el string fuente en dest |
| strncpy | strncpy(dest, fuente, n); | copia a lo más n caracteres de fuente en dest |
| strcat | strcat(dest, fuente); | agrega el string fuente al string dest |
* La función strcmp entrega < 0 si el primer argumento es lexicográficamente menor que el segundo, 0 si son iguales y > 0 si es mayor.
* La función strncmp es similar solo que compara hasta n caracteres.
* Antes de invocar strcpy y strcat se debe reservar suficiente memoria en dest para almacenar el resultado.
* En el caso de strncpy, se debe reservar exactamente n caracteres. Si ''strlen(fuente)>=n'' entonces dest no termina en 0.
* Estas funciones retornan dest para que se puedan hacer varias invocaciones en una sola instrucción:
strcat( strcat ( strcpy (t, "hola"), " que"), " tal"); /* t queda en "hola que tal" */
=== Ejemplos ===
Las siguientes son implementaciones ultra compactas de strlen y strcpy:
int strlen(char *s) {
char *r= s;
while (*r++)
;
return r-s-1;
}
char *strcpy(char *d, char *s) {
char *t= d;
while (*t++ = *s++)
;
return d;
}
=== Ejercicio 1 ===
Resuelva la pregunta 1 del [[http://users.dcc.uchile.cl/~lmateu/CC3301/controles/c1-141.pdf|control 1 del semestre Otoño de 2014]]. Pruebe su solución con el archivo [[http://users.dcc.uchile.cl/~lmateu/CC3301/download/subst.zip|subst.zip]]. Implemente su solución en el archivo substituir.c. Compílelo con make y ejecútelo. El programa le dirá si su solución satisface todos los tests del enunciado.
==== Desplegar texto en la salida estándar ====
Una de las funciones más usadas en C es printf, que significa //print formatted//. Se usa de la siguiente forma: printf( //formato//, //argumento1//, //argumento2//, ...). En donde formato es un string que incluye órdenes de reemplazo del estilo %d, %s, %c. Normalmente los caracteres del formato se copian literalmente en la salida estándar pero cuando se encuentra una orden de reemplazo entonces se estrae secuencialmente uno de los argumentos y se envía a la salida estándar. Por ejemplo:
int x= 5;
char c= '*';
char *s= "hola";
printf("x= %d c= %c s= %s\n", x, c, s); /* envía: x= 5 c= * s= hola */
El caracter '\n' (//léase backslash n//) es un caracter de control que significa cambio de línea.
En Unix Ud. puede obtener una detallada documentación (aunque críptica) de una función en C mediante el comando man de Unix:
% man 3 printf
También existen estas variantes:
^ función ^ ejemplo ^ descripción ^
| fprintf(//file//, //formato//, ...) | ''fprintf(stderr, "error %d\n", errno());'' | envía al archivo //file// |
| sprintf(//string//, //formato//, ...) | ''char t[80]; sprintf(t, "%d\n", i);'' | escribe en el string t |
* La variable stderr corresponde a la salida estándar de errores. stdin corresponde a la entrada estándar y stdout a la salida estándar.
* En el caso de escribir en un string, es reponsabilidad del progrador reservar suficiente espacio en el string.
=== Ejemplo ===
El siguiente programa convierte los números en hexadecimal de la línea de comandos a decimal:
#include
int hex2i(char *s) {
int v= 0;
while (*s) {
switch (*s) {
case '0' :
case '1' :
case '2' :
case '3' :
case '4' :
case '5' :
case '6' :
case '7' :
case '8' :
case '9' :
v = v*16 + *s-'0';
break;
case 'a' :
case 'b' :
case 'c' :
case 'd' :
case 'e' :
case 'f' :
v= v*16 + *s-'a'+10;
break;
default:
printf("Error en el caracter %c\n", *s);
}
s++;
}
return v;
}
int main(int argc, char **argv) {
int k;
for (k= 1; k
* Llame a su archivo hex.c
* Compile con: gcc hex.c -o hex
* Ejecute con: ./hex 5 a f 10 1ea
* o también: ./hex g
* Cuando se lanza un programa, los argumentos de la línea de comandos se reciben en argv.
* El número de argumentos se recibe en argc.
* argv[0] siempre corresponde al nombre del ejecutable.
=== Ejercicio 2 ===
Programe la función que lleva un string a letras mayúsculas. Por ejemplo:
int main() {
char str[80];
strcpy(str, "el numero pi es 3.14");
mayusculas(str);
printf("%s\n", str); /* despliega EL NUMERO PI es 3.14 */
return 0;
}
==== Leer de la entrada estándar ====
Se pueden usar estas funciones:
^ función ^ ejemplo ^ descripción ^
| getchar() | ''char c= getchar();'' | Lee un caracter |
| gets(//dest//) | ''gets(t);'' | Lee una línea terminada en '\n' |
| fgets(//dest//, //max//, //file//) | ''fgets(t, 80, stdin);'' | Lee una línea terminada en '\n' de //file// |
| scanf(//formato//, //arg1//, ...) | ''fscanf("%d", &i);'' | Lee datos de la entrada estándar |
**Observación**: ''gets'' no recibe como parámetro el tamaño del arreglo de caracteres en donde se deja el resultado
y por lo tanto no se debe usar para leer datos de la red o de un archivo de origen desconocido porque puede
ser blanco de ataques de gusanos o virus. Consulte en la web por //buffer overflow attack//. En su lugar use ''fgets''.
=== Ejercicio 3 ===
El siguiente programa despliega la línea más larga de la entrada estándar:
#include
#include
#define N 1000
int main() {
char lin[N], larga[N];
int largo= 0;
/* nunca while (gets(lin)!=NULL) pues sería una brecha de seguridad */
while (fgets(lin, N, stdin)!=NULL) {
if (strlen(lin)>largo) {
strcpy(larga, lin);
largo= strlen(lin);
}
}
printf("%s\n", larga);
return 0;
}
¡Cuidado! este código no funciona:
char *larga;
...
if (...) {
larga= lin;
...
}
...
La asignación solo involucra punteros, no los contenidos.