- Tipos operadores y expresiones
- Operador asociatividad
- Control de flujo
- Funciones y la estructura del programa
- Entrada y salida
Cadenas de caracteres.Tienen un caracter nulo al final (''). Ej. "hellon" -> h e l l o n Tipos de variablesEntre los tipos de variables se cuentan: Automáticas Externas Las variables automáticas… – aparecen y desaparecen con la llamada de la función. Las variables externas… – se declaran globalmente: extern int max; – se definen fuera de las funciones: int max; – se declaran dentro de las funciones: extern int max;
TIPOS OPERADORES Y EXPRESIONES
Nombres de las variables Letra (Letra o Dígito)* -> 31 caracteres Las mayúsculas son diferentes de las minúsculas Es práctica común de C, denotar con – MAYUSCULAS las constantes – minúsculas las variables Tipos char int -> (CALIFICADORES: short, long, unsigned) float double Constantes Simbólicas (Preprocesamiento) Ej. #define PI 3.14159265359
#define MAXLINE 1000 char line [MAXLINE+1]
#define FORMFEED '14' Variables constantes: Ej. const float pi=3.14159265359; Alfanuméricas: Se evalúan en tiempo de compilación, no de ejecución. Ejs. 3.14159265359 123.456e-7 0.12E3 '0' -> ASCII 48, EBCDIC 240
Declaraciones: Se pueden declarar varias variables en pocos renglones: int lower,upper,step; char c, line[1000]; También se pueden escribir más explicitamente, para agregar comentarios: int lower; /* algunos comentarios */ int upper; int step; char c; char line[1000]; Inicialización de variables: char backslash='/'; int i=0; Operadores aritméticos: + – * / %(módulo) Ej. (uso del módulo) if (year%4==0 && year%100!=0 || year%400==0) es un año bisiesto; else no lo es; Operadores relacionales y lógicos: > >= <= < == != El operador que convierte en 0 algo distinto de 0, y en 1, el cero, es !. Ej. if (!inword) if (inword==0) Conversiones de Tipos Se hacen conversiones automáticas cuando tiene sentido. Ej. float + int -> float "char" e "int" se manejan indiscrimidamente. Ej. int atoi(char s[]) /* convierte s a int */ { int i,n; n=0; for (i=0; s[i]>='0' && s[i]<='9';++i) n=10*n+s[i]-'0'; return (n); } Conversión coaccionada "cast" Se puede forzar la conversión de un tipo en otro. El formato es: (nombre-de-tipo) expresión por ejemplo: sqrt( (double) n) Operadores de incremento y decremento si n=5, entonces x=n++; -> x=5 n=6 x=++n; -> x=6 n=6 Operadores lógicos para manejo de bits: & AND lógico | OR lógico ^ XOR lógico << desplazamiento a la izquierda >> desplazamiento a la derecha ~ complemento a uno (unario)
Ejs c = n & '77'; x = x | MASK; x = x & ~'77';
Ej. Función para tomar n bits a partir de la posición p
getbits(unsigned int x, p, n) {
return ((x>>(p+1-n)) & ~(~0 << n));
}Nota: (~0 << n) es 11..100..0 con n ceros a la derecha ~(~0 << n) es 00..011..1 con n unos a la derecha
Operadores y expresiones de asignación La expresión e1 op= e2 es idéntico a e1 = e1 op e2 donde "op" es un operador como {],-,*,/,%,<<,>>,&,^,|} Por ejemplo: i=i+2; es idÚntico a i+=2; Expresiones condicionalesTienen el siguiente formato e1 ? e2 : e3; si e1 es verdadero se evalúa e2, si e1 es falso, se evalúa e3. z=(a>b) ? a : b; /* z=max(a,b) */ Precedencia y orden de evaluación
OPERADOR ASOCIATIVIDAD () [] -> . -> ! ~ ++ — – (tipo) * & sizeof <- * / % -> + – -> << >> -> < <= > >= -> == != -> & -> ^ -> | -> && -> || -> ? : <- = += -= etc <- 0 -> ASOCIATIVIDAD-> izquierda a derecha <- derecha a izquierda i=i+2 i+=2
. CONTROL DE FLUJO
Proposiciones Se terminan con ; (";" es terminador, no separador) Nula: ; Simple: x=0; Compuesta: { proposiciones; } if elseif (expresión) proposición 1 else proposición 2 Las siguientes proposiciones son equivalentes: if (expresión) if (expresión != 0)
El "else" se asocia a la condicional más interna: if (n>0) if(a>b) z=a; else z=b; Se entendería como: if (n>0) if(a>b) z=a; else z=b; Si se quiere que el "else" se asocie a la condicional externa, es necesario usar llaves, como se muestra a continuación: if (n>0) { if(a>b) z=a;} else z=b; que se entiende como: if (n>0) { if(a>b) z=a; } else z=b;
else ifPermite escribir condicionales de casos EXCLUYENTES if (expresión) proposición else if (expresión) proposición else proposición
switchSintetiza una secuencia larga else if, incluyendo || (or).
Ej.
main() /* cuenta dígitos, espacios en blanco y otros */ {
int c, i, nwhite, nother, ndigit[10]; nwhite=nother=0; for (i=0; i<10; i++) ndigit[i]=0; while ((c=getchar()) != EOF) {
switch(c) {
case '0': case '1': … case '9':
ndigit[c-10]++; break;
case ' ': case 'n': case 't':
nwhite++; break;
default:
nother++; break;
}
}
printf("digits= "); for (i=0; i<10; i++) printf("%d",ndigit[i]); printf("n white space = %d, ", "other = %dn",nwhite,nother); return 0; }
while y forwhile (expresión) proposición Las siguientes proposiciones "for" y "while" son equivalentes: for (expr1; expr2; expr3) proposición expr1; while (expr2) {
proposición expr3;
}Uso de la coma ","La coma "," permite el cálculo secuencial de expresiones.
Por ejemplo:
reverse(s) /* invierte cadena s */ char s[]; {
int c, i, j; for (i=0; j=strlen(s)-1; i)
{
c=s[i]; s[i]=s[j]; s[j]=c;
}
}
do whiledo proposición while (expresión); breakLa proposición "break" permite una salida forzada de:
- do
- while
- for
- switch
Puede similarse usando otra "condición", pero no resulta tan claro. continueObliga a ejecutar la siguiente iteración del ciclo, en las proposiciones:
- do
- while
- for
Por ejemplo
/* procesa los valores positivos de un arreglo */ for (i=0; i { if (a[i]<0) /* salta los elementos negativos */ continue; /* aquí procesa los elementos positivos */ }
goto y etiquetasGeneralmente es usada en el manejo de errores. Se aconseja ser parcos en su uso. Por ejemplo
/* muestra el primer valor negativo de un */ /* arreglo multidimensional */ for (i=0; i for (j=0; j if (v[i][j]<0) goto found; /* no se encontró */ found: /* se encontró en la posición i,j */ ..
FUNCIONES Y LA ESTRUCTURA DEL PROGRAMA
En C no se pueden escribir funciones dentro de funciones. Por ejemplo:
/* Imprime las líneas que contengan cierto patrón */ #define MAXLINE 100 void index(char [], char []); extern void getline(char [], int); main() { char line[MAXLINE];
while(getline(line,MAXLINE)>0) if (index(line,"the")>=0 printf("%s",line);
}void index(char s[], char t[]) /* devuelve posición t en s; o -1 si no está */ { int i,j,k;
for (i=0; s[i]!=''; i++) {
for (j=i, k=0; t[k]!='' && s[j]==t[k]; j++,k++); if (t[k]=='') return (i);
Compilación y encadenamientoEn UNIX: $ cc main.c getline.c index.c produce: main.o getline.o index.o <- programas objeto a.out <- programa ejecutable Funciones que devuelven valores no enterosSe tienen las siguientes 2 puntos: – Por default el tipo de la función es "int" ("char" se convierte a "int"). – En caso de no ser "int", la función se debe declarar: – en la función: double atof(s) /* convierte la cadena s a double */ – en la llamada: double sum,atof(); … (… sum+=atof(line)); Reglas sobre el alcance de validez de una variable:Variable externa (declaración) Se dan a conocer las propiedades de una variable, por ejemplo tipo, tamaño, etc. Variable externa (definición) Causa una asignación de memoria. Solo se debe hacer una definición. Por ejemplo, las siguientes líneas
int sp; double val[MAXVAL];
cuando están fuera de cualquier función, definen las variables externas "sp" y "val", obligan a asignar memoria y sirven de declaración para el resto del archivo. Sin embargo, las líneas
extern int sp; double val[MAXVAL];
declaran para el resto del archivo que "sp" es un entero, y "val" es un arreglo double (cuyo tamaño se fija en otro lugar), pero no se crean las variables ni se les asigna memoria. Variables estáticasLas variables estáticas son accesibles solo dentro del archivo donde son definidas. Y pueden ser definidas dentro o fuera de una función. Ej. Si las 2 variables y las 2 funciones siguientes se compilan en un archivo con:
static char buf[BUFSIZE]; /* buffer para "ungetch" */ static int bufp=0; /* sig. pos. libre en "buf" */ getch() {…} ungetch() {…} el resto de las funciones de los otros archivos no tendrá acceso ni a "buf" ni a "bufp". Por lo anterior, no habrá conflictos si los mismos nombres son usados en otros programas. Una función estática es inaccesible fuera del archivo donde se declara (y define, por supuesto). Variables registro.Las variables registro sólo pueden ser las variables automáticas y los parámetros formales de la función. Las variables registro son usadas para variables muy usadas. El compilar coloca las variables registro en los registros de la CPU de la máquina, siempre y cuando sea posible. NO ES POSIBLE obtener la dirección de una variable estática. Se declaran de la siguiente manera: register int x; register char c; Y la declaración para parámetros formales de función, es: f(register int c,n) { … } Estructura de bloquesSe tienen las siguientes características: – No se pueden definir funciones dentro de funciones. Si embargo si se pueden declarar funciones dentro de funciones. Esta declaración hace referencia a funciones que se encuentran en otro parte. – Las declaraciones o definiciones (incluyendo inicializaciones), se colocan dentro de las llaves que introduce cualquier sentencia compuesta. Por ejemplo: if (n>0) { /* se declara una variable nueva "i" */ int i; for (i=0; i … InicializaciónLa inicialización tiene las siguientes consideraciones: – Si no se inicializan explicitamente las variables, se tiene: – Las variables externas y estáticas tendrán cero. – Las variables automáticas y registro tendrán basura. – Las variables simples, que no son arreglos ni estructuras, se inicializan así: int x; char squoute = '''; long day=60*24; /* minutos del día */ – Las variables externas estáticas se inicializan una vez en tiempo de compilación; las variables automáticas y registro, cada vez que se entra a la función. – Las variables automáticas y registro se pueden inicializar con valores definidos previamente, incluso llamadas a función. Por ejemplo: int low=0; int high=n-1; /* n definida anteriormente */ int mid; .. } – Los arreglos automáticos no pueden ser incializados. – Los arreglos externos y estáticos se inicializan con valores separados por ',' y entre llaves. Por ejemplo, el programa siguiente para contar dígitos, espacios y otros: /* cuenta dígitos, espacios y otros */ main() { int c,i,nwhite,nother; int ndigit[10]; nwhite=nother=0; for (i=0; i<10; i++) ndigit[i]=0; … } puede ser escrito como: /* cuenta dígitos, espacios y otros */ int nwhite=0; int nother=0; /* inicialización con 10 0's */ int digit[10]={0,0,0,0,0,0,0,0,0,0,0}; main() { int c,i; … } – Se pueden inicializar arreglos de caracteres de 2 maneras: – Como cadena de caracteres char pattern[]="the"; – Como arreglo de caracteres char pattern[]={'t','h','e',''}; Recursividad– Las funciones en C pueden llamarse a si mismas, es decir pueden ser recursivas. – Una función puede llamarse a si misma, directa o indirectamente. Por ejemplo:
/* imprime n en decimal (recursivamente) */ void printd(int n) { int i; if (n<0) { putchar('-'); n=-n; } if ((i=n/10) != 0) printd(i); putchar(n%10+'0'); }
El preprocesador de C Inclusión de archivos (include):Una línea de la forma #include "nombre" indica al preprocesador de C, substituir la línea con el contenido del archivo "nombre", de trayectoria absoluta. Una línea de la forma #include <nombre> indica al preprocesador de C, substituir la línea con el contenido del archivo "nombre", de la trayectoria PREDEFENIDA para los archivos "include". Los "include"s pueden ser anidados. Los "include"s son recomendables para tener las mismas definiciones en varios programas fuentes. Sustitución de MacrosPara sustituir SI por 1, usamos la línea siguiente. #define SI 1 aquí, las cadenas "SI=" no serán sustituidas. Para usar una sintaxis similar al PASCAL, usamos las líneas #define then #define begin { #define end } y podríamos escribir después if (i>0) then begin a=1; b=2 end Podemos usar parámetros en la sustitución de macros. Por ejemplo: #define max(A,B) ( (A)>(B) ? (A) : (B) ) y usarla como x=max(p+q,r+s); que sería sustituida por x=((p+q)>(r+s) ? (p+q) : (r+s)); NOTAS: – Es necesaria utilizar paréntesis en la definición de macros, para evitar resultados erróneos debido a la prioridad de las operaciones aritmÚticas. – Con una MACRO se puede crear una sola función para varios tipos de argumentos. – Las MACROS con parámetros son más eficientes que las llamadas a funciones, ya que son una expansión en línea. Por ejemplo, las funciones "getchar()" y "putchar()" están definidas como MACROS de las funciones "putc()" y "getc()", usando la salida estándard (stdout) y entrada estándard (stdin) respectivamente.
Apuntadores y direcciones.
Los símbolos * y & pueden ser usados para el contenido y la dirección de una variable, respectivamente. Por ejemplo, si "x" es una variable de tipo "int" y "y" es una variable de tipo "int" y "px" es una variable de tipo "apuntador a int" entonces, se podría hacer lo siguiente: Asignar la dirección de "x" al apuntador "px" px = &x; Aumentar en 1, el valor apuntado por "px" (es decir, el valor de "x") y asignarlo a la variable "y". y = *px+1; Aumentar en 1, el valor del apuntador "px" (es decir, el valor de una variable desconocida) y asignarlo a la variable "y". Aumentar en 1 el valor apuntado por "px" (es decir, el valor de "x"). *px+=1; o bien (*px)++; notar que en este último ejemplo es necesario usar parÚntesis, debido a que, aunque la prioridad de "*" y "++" es igual, la asociatividad es de DERECHA a IZQUIERDA. Así, sin los parÚntesis, la expresión "*px++;" se entendería como: "*(px++);".
APUNTADORES Y ARREGLOSSe definimos el arreglo de 10 enteros de la siguiente manera: int a[10]; podemos referenciar a[0] a[1] .. a[9]. Si además definimos el apuntador a entero "pa" int *pa; y le asignamos la dirección del primer elemento del arreglo "a", pa=&pa[0]; o bien pa=a; entonces, el contenido de "a[i]" puede ser visto con *(pa+i) En apuntadores a arreglos de caracteres, las 2 siguientes declaraciones son equivalentes: char s[]; char *s; AritmÚtica de direcciones. Son posibles las siguientes operaciones con apuntadores: – Sumar y restar un entero a un apuntador. – Restar y comparar 2 apuntadores. – Comparar un apuntador con NULL. Apuntadores a caracteres y a funciones. En el siguiente ejemplo, la variable "mensaje" es SOLO UN APUNTADOR. char *message="now is the time"; No se debe comfundir el envío de un APUNTADOR a un arreglo de caracteres con el envío del ARREGLO de caracteres. Por ejemplo, en la siguiente función se envían los apuntadores a 2 arreglos de caracteres (cuya memoria fue asignada en otra parte del programa). strcpy(s,t) /* copia t a s, con apuntadores */ { while (*s++=*t++) ; } Arreglos MultidimensionalesEn C, los arreglos de 2 dimensiones se implementan como arreglos unidimensionales. Donde cada elemento es, a su vez, un arreglo unidimensional. Por lo anterior, la sintaxis en C es day_tab[i][j]; /* [renglón] [columna] */ en lugar de: day_tab[i,j]; /* INCORRECTO */ Los arreglos de 2 dimensiones pueden ser inicializados como sigue: static int day_tab[2][13] = { {0,31,28,..,31}, {0,31,29,..,31}, }; Arreglo de apuntadores. Apuntadores a apuntadores. Inicialización de arreglos de apuntadores: static char *name[]= { "mes ilegal", "enero", … "diciembre", }; Diferencia entre apuntadores y arreglos multidimensionales.No se debe confundir las declaraciones: int a[10][20]; /* matriz de 10*20=200 enteros */ int *b[10]; /* vector de 10 apuntadores a enteros */ /* o 10 apuntadores a arreglos a enteros */ Si consideramos que en los 2 arreglos anteriores, usamos [renglón] [columna] Entonces tenemos 2 diferencias básicas: 1. En el arreglo "a" el número de columnas (elementos) x renglón es 20 para todos los renglones. En el arreglo "b" podemos tener un número diferente de columnas x renglón para cada renglón. Por ejemplo 2 elementos en el renglón 0; 5 en el renglón 1; e incluso una matriz de 4×4 en el renglón 3, etc. 2. La memoria ocupada por los 2 arreglos es diferente. Inclusive en el caso de que los apuntadores del arreglo "b" apuntaran a un arreglo de 20 elementos por renglón, la memoria ocupada por los 2 arreglos sería diferente. Para el arreglo "a", 200 enteros. Para el arreglo "b", 200 enteros + 10 apuntadores. Argumentos de la línea de comandos.Al correr el programa ejecutable producido por un programa fuente escrito en C, podemos agregar argumentos en la línea de comandos. Estos argumentos pueden ser usados para producir efectos diferentes al correr el mismo programa. Así, podríamos usar opciones como las usadas en los sistemas operativos: C:> copy C:> copy a:arch1 b: C:> copy a:arch1 b:arch2 El procesamiento de los argumentos de la línea de comandos se puede ejemplificar con el siguiente programa en C, que simplemente escribe los argumentos en la salida estándard.
/* escribe los argumentos en la salida estándard */ main(int argc, char *argv[]) { while(–argc>0) printf("%s%c",*++argv,(argc>1)?' ':'n'); }
"argc" y "argv" son palabras predefinidas. "argc" indica el número de argumentos (incluyendo el nombre del programa ejecutable). "argv" es un arreglo de apuntadores a arreglos de caracteres. "argv[i]" es un apuntador al arreglo de caracteres del argumento i. Por ejemplo, si "echo.exe" es un programa ejecutable producido por un programa escrito en C, entonces al ser corrido con: echo hello world los valores que tomarían "argc" y "argv" en caso de haber sido usados son: argc=3 *argv[0]=echo *argv[1]=hello *argv[2]=world Apuntadores a funcionesEn la definición: /* compara 2 cadenas */ int strcmp(); En la declaración: /* apuntador a función que devuelve un entero */ int (* comp)();
/* función que devuelve apuntador a entero */ int *comp();
Por ejemplo, si queremos crear el tipo "date" y crear 2 variables de ese tipo, "birthdate" y "hiredate". Podemos usar: struct date { int day; int month; int year; }; date birthdate, hiredate; o bien, también podemos escribir: struct date { int day; int month; int year; } birthdate, hiredate; Si solo queremos crear las variables, y no llamar a su tipo con algún nombre, podemos simplemente teclear: struct { int day; int month; int year; } birthdate, hiredate; Una estructura puede contener a su vez estructuras, como en: struct person { char name[NAMESIZE]; char address[ADRSIZE]; long zipcode; double salary; struct date birthdate; struct date hiredate; }emp; En el caso de haber declarado la variable "emp" de la forma anterior, podemos hacer referencia al mes de su fecha de nacimiento con: emp.birthdate.month También podemos inicializar estructuras estáticas y externas de la siguiente manera: struct date d={4,7,1776} Si declaramos apuntadores a estructuras, como en struct date *pd; podemos hacer referencias con la notación: pd->year o bien (*pd).year NOTAS: – punto(.) -> () [] tienen la máxima precedencia Por lo tanto, ++p->x incrementa x – La asociatividad de punto(.) y -> es de izq. a der. Por lo tanto, p->q->memb se entiende como (p->q)->memb emp.birthdate.month se entiende como (emp.birthdate).month
Arreglos de estructurasEn lugar de usar arreglos paralelos en C, como en el ejemplo siguiente para contar palabras reservadas de C, char *keyword[NKEYS]; int keycount[NKEYS]; se pueden usar estructuras, de la siguiente manera: struct key { char *keyword; int keycount; } keytab[NKEYS]; pero tendremos que asignar valores despuÚs a "keytab". También podemos usar declaración explicita como en: struct key { char *keyword; int keycount; } keytab[]= { "break",0, "case",0, … "while",0 }; #define NKEYS (sizeof(keytab)/sizeof(struct key)) donde se define NKEYS en base a los tamaños de la estructura y la declaración explícita, para no tener que calcular su valor y recalcularlo cada vez que el contenido de "keytab" cambie.
Apuntadores a estructurasSe pueden declarar apuntadores a estructuras, como por ejemplo, si usamos la estructura "key" anteriormente definida: struct key *binary(),*pk; y podemos referenciar a los campos de la estructura con: pk->keyword pk->keycount Estructuras autoreferenciadasLas estructuras pueden contener apuntadores a si mismas como se muestra en el siguiente ejemplo: struct tnode /* el nodo */ { char *word; /* apuntador a los caracteres */ int count; /* número de ocurrencias */ struct tnode *left; /* hijo izquierdo */ struct tnode *right; /* hijo derecho */ }; Se puede convertir un apuntador a caracter en un apuntador de estructura mediante el uso de un 'cast' (conversión forzada). char *p; … (struct tnode *) p … La anterior conversión es usada principalmente en el uso de memoria dinámica.
UnionesLas uniones son usadas cuando se necesita que una variable tome valores de tipos diferentes. En el ejemplo siguiente: union u_tag { int ival; float fval; char *pval; } uval; la variable "uval" de tipo "u_tag" puede tomar valores "int" si se usa "uval.ival" "float" si se usa "uval.fval" "char *" si se usa "uval.pval" Es necesario el uso consistente de uniones. Es decir, el tipo utilizado debe ser el tipo más recientemente almacenado. Las uniones pueden ser anidadas en una estructura, como se muestra: struct { char *name; int flags; int utype; union { int ival; float fval; char *pval; } uval; } symtab[NSYM]; y se hace referencia de la siguiente manera: symtab[i].uval.ival para ival symtab[i].uval.pval para el primer caracter de pval typedefEl "typedef" permite declarar sinónimos de tipos. Por ejemplo typedef int LENGTH; declara el nombre de LENGTH como sinónimo de int.
Este "nuevo tipo" podría ser usado en LENGTH len, maxlen; LENGTH *lengths[]; /* arreglo de apuntadores a tipo LENGTH=int */ Algunas observaciones del uso de "typedef" – Sintácticamente "typedef" se parece a las clases de almacenamiento "extern", "static", etc. – No crea un nuevo tipo, sino añade un nuevo nombre. – Es parecido a "#define" pero con mayor alcance. Por ejemplo, el siguiente sinónimo es para un apuntador a una función que devuelve un int. typedef int (*PFI) (); .. PFI strcmp, numcmp, swap;
Acceso a la biblioteca estándardLa línea siguiente se coloca al principio de un programa fuente. Define ciertas macros y variables empleadas por la biblioteca estándard de E/S. #include Se usa < >, en vez de "", para que el archivo sea buscado en el directorio de información estándard, por ejemplo: /usr/include —> en UNIX TURBOC2.0INCLUDE —> en MS-DOS
Entrada y salida estándard: putchar(), getchar()El uso de la entrada y salida estándard permite hacer redireccionamiento de y hacia archivos. prog < arch_ent prog > arch_sal o bien procesamiento entubado, como en: prog | otr_prog otr_prog | prog Por ejemplo, el siguiente programa copia la entrada estándard (teclado) a la salida estándard (monitor).
#include <stdio.h> main() { int c;
while ((c=getchar())!=EOF) putchar(isupper(c) ? tolower(c) : c);
}y podría correrse, en el casO de llamarse "lower", de la siguiente manera: C:> type file1 | lower > salida
Salida con formato, printf()La sintaxis es la siguiente: printf(control,arg1,arg2,…,argn) donde: control::="{char}%[-][long_max]convers.[char_o_decim]{char}" char ::=cualquier caracter – justificación a la izquierda long_max::=longitud máxima. Si no cabe el valor, se viola convers::=d decimal o octal x hexadecimal u unsigned c caracter s cadena(string) e float o double => [-]m.nnnnnnE[+-]xx f float o double => [-]mmm.nnnnnn g más corto que %e o %f. Los ceros no significativos no se imprimen char_o_dec número de caracteres de la cadena o número de decimales Por ejemplo, si "s" es un apuntador a "hello, world", se tiene: 0 1 2 1234567890123456789012 printf(":%10s:",s) => :hello world: printf(":%-10s:",s) => :hello world: printf(":%20s:",s) => : hello world: printf(":%-20s:",s) => :hello world : Entrada con formato, scanf() La sintaxis es la siguiente: scanf(control,arg1,arg2,…,argn) donde: control::="{char}%[*]convers[long_max]convers" char::=cualquier caracter convers::= d decimal o octal x hexadecimal h entero corto (short) c caracter s cadena f,e punto flotante. Puede usar E+ o E- l long (double) Por ejemplo, si declaramos: int i; float x; char name[50]; entonces, la llamada de la función, líneas leídas y valores asignados, se muestran a continuació: scanf("%d %f %s",&i,&x,name); 25 54.32E-1 Torres => i=25 x=5.432 name="Torres" scanf("%2d %f %*d %25",&i,&x,name); 56789 0123 45182 => i=56 x=789.0 name="45"
Conversión de formato en memoria, sprintf(), sscanf().sprintf() y sscanf() hacen la misma función que printf() y sscanf(), pero en lugar de enviar el resultado a la salida estándar o leerlo de la entrada estándar, usan variables de tipo de arreglo de caracteres. Por ejemplo, si n=123, la siguiente llamada a sprintf() almacena "temp123" en el arreglo de caracteres name. sprintf(name,"temp%d",n); Y si name contiene "temp123", el valor 123 se asignará a n al ejecutarse la siguiente llamada sscanf(name,"temp%d",n);
Acceso a archivos (funciones de stdio.h)En la librería <stdio.h> está definido el tipo FILE. Este tipo permite guardar información de posición y descripción del buffer asociado a un archivo. El tipo FILE se usa de la siguiente forma: FILE *fopen(), *fp; donde "fp" puede ser usado como apuntador del archivo que se abre: fp=fopen(nombre,modo); donde: nombre es el nombre del archivo que se va a abrir modo puede ser: lectura("r"), escritura("w"), y añadido("a"). NOTA: si se produce un error, fopen regresa NULL. Para escribir y leer información de un archivo, usamos: putc() y getc(), de la siguiente manera: c=getc(fp); /* lee un caracter del archivo apuntado fp */ putc(c,fp); /* escribe un caracter al arch. apunt. fp */ Existen en la librería <stdio.h> las definiciones para 3 apuntadores estándard a archivos: stdin archivo de la entrada estándard stdout archivo de la salida estándard stderr archivo de la salida del error estándard En base a los apuntadores estándard anteriores, se pueden definir las funciones putchar() y getchar(). #define getchar() getc(stdin) #define putchar(c) putc(c,stdout) También podemos usar entrada y salida formateada a archivo, con las funciones: fscanf(fp,formato,lista de variables); fprintf(fp,formato,lista de variables); con la misma sintaxis que scanf(),printf(). NOTA: Cuando se detecta un error de entrada/salida en archivos, es conveniente escribir el mensaje de error en stderr.
Entrada y salida de líneasPara almacenar en el arreglo de caracteres line, una línea de un máximo de MAXLINES caracteres leída del archivo apuntado por fp, usamos: fgets(line,MAXLINE,fp) Para escribir en el archivo apuntado por fp el arreglo de caracteres line, usamos fputs(line,fp);
Operaciones con cadenas de caracteres, string.hEn el directorio include de la versión de C que se estÚ manejando existen encabezados de otras librerías, entre las que se cuentan string.h. En este encabezado están las declaraciones para las funciones de manejo de cadenas de caracteres que se listan a continuación. En adelante, s y t son de tipo apuntador a caracter, y c y n son enteros. strcat(s,t); concatena s al final de t strncat(s,t,n); concat. n caracts. de t al final de s. strcmp(s,t); regresa negativo, cero, o positivo para: s<t, s==t, s > t strncmp(s,t,n); i gual que strcmp pero solo con n caract. strcpy(s,t); copia t en s strncpy(s,t,n); copia a lo mas n caract. de s en t strlen(s); regresa la longitud de s strchr(s,c); regresa un apuntador al primer c que estÚ en s, o NULL si no está presente. strrchr(s,c); regresa un apuntador al último c que estÚ en s, o NULL si no está presente.
Prueba y conversión de cadenas de caracteres, ctype.hVarias de las funciones declaradas en <ctype> realizan pruebas y conversiones de caracteres. En lo que se muestra a continuación, c es un int que se puede representar como un unsigned char o EOF. Las funciones regresan int isalpha(c); != 0 si c es alfabÚtica, 0 otro caso. isupper(c); mayúscula islower(c); minúscula isdigit(c); dígito isalnum(c); isalpha(c) || isdigit(c) isspace(c); blanco, tabulador, nueva línea, retorno, avance de línea, tabulador vertical toupper(c); regresa c convertida a mayúscula tolower(c); regresa c convertida a minúscula ungetc(c,fp) Esta función se usa para devolver el caracter c al archivo apuntado por fp.
Funcion system()La función system() permite hacer una llamada al sistema operativo. La sintáxis es la siguiente: system("date"); system("dir");
Memoria DinámicaAl declarar (o definir) una variable en C, normalmente se establece una memoria limitada. Por ejemplo: int i; int a[10]; int *c; Sin embargo si nosotros queremos que ciertos datos se almacenen en memoria no limitada (salvo la limitación del "heap" del sistema), podemos usar memoria dinámica. El siguiente ejemplo muestra como almacenar una cadena arbitrariamente larga de caracteres.
/* programa para almacenar cadena de longitud ilimitada */ #include <stdio.h> #include <malloc.c> struct scar {
char car; /* caracter */ struct caract *psig; /* apuntador al siguiente */
} pini, pfin;
main() { int c; struct caract pc;
/* lectura y almacenamiento */ pini=pfin=NULL if (c=getchar()!=EOF) do c=getchar(); for (pini=pfin=NULL; (c=getchar())!=EOF; ) pfin=(struct scar *) malloc(sizeof(struct scar)) if (pfin=NULL) puts("No hay memoria",stderr); else pini->car=c, pini->psig=NULL) while (c!=EOF);
Oscar Quintero