Cadenas de
caracteres.
Tienen un caracter nulo
al final ('\0').
Ej. "hello\n" -> h e l l o \n \0
Tipos de variables
Entre 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 '\014'
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 & '\077';
x = x | MASK;
x = x &
~'\077';
Ej. Función para tomar n bits a partir de la posición p
getbits(unsigned int x, p, 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 condicionales
Tienen 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 else
if
(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 if
Permite escribir condicionales de casos
EXCLUYENTES
if (expresión)
proposición
else if
(expresión)
proposición
else
proposición
switch
Sintetiza
una secuencia larga else if, incluyendo || (or).
Ej.
main() /* cuenta dígitos, espacios en blanco y otros */
{
case ' ':
case '\n':
case '\t':
default:
}
}
printf("digits= ");
for (i=0; i<10;
i++)
printf("%d",ndigit[i]);
printf("\n white space = %d, ",
"other =
%d\n",nwhite,nother);
return 0;
}
while y for
while (expresión)
proposición
Las siguientes
proposiciones "for" y "while" son equivalentes:
for (expr1; expr2;
expr3)
proposición
expr1;
while (expr2)
{
}
Uso de la coma
","
La coma "," permite el cálculo
secuencial de expresiones.
Por ejemplo:
reverse(s) /* invierte cadena s */
char s[];
{
}
}
do while
do
proposición
while
(expresión);
break
La proposición "break" permite una salida
forzada de:
|
| do |
| while |
| for |
| switch | |
Puede similarse usando otra "condición", pero no resulta tan
claro.
continue
Obliga 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 etiquetas
Generalmente 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];
}
void index(char s[], char
t[])
/* devuelve posición t en s; o -1 si no está */
{
int
i,j,k;
Compilación y encadenamiento
En 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 enteros
Se 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
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
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áticas
Las
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
bloques
Se 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ón
La 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','\0'};
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 Macros
Para 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 ARREGLOS
Se 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
Multidimensionales
En 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 4x4 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 funciones
En 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 estructuras
En 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 estructuras
Se 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
autoreferenciadas
Las 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.
Uniones
Las
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
typedef
El "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ándard
La 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.0\INCLUDE
---> 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;
}
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\0"
scanf("%2d %f %*d %25",&i,&x,name);
56789 0123
45182
=> i=56 x=789.0 name="45\0"
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\0" 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íneas
Para 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.h
En 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.h
Varias 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ámica
Al 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;