===== 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.