Esta mensage és una especie de mini-FAQ con los errores más comunes o las preguntas más frecuentes que se dan al empezar a pogramar C o C++
Si quereis proponer algun añadido al FAQ ponedlo en este hilo.
PREGUNTAS Y ERRORES MÃS FRECUENTES
1.- Valores de retorno y parámetros de la función main()
Según el estándar, la función main sólo tiene un posible valor de retorno: int. Ni void, ni un valor indeterminado (es decir, no poner nada) ni ningún otro.
Si te fastidia tener que escribir un return al final de la ejecución de la función main(), te alegrará saber que, al menos en C++, no es necesario escribir una instrucción return de forma explÃcita, ya que el compilador asume por defecto un return 0, que se considera que la ejecución ha finalizado correctamente.
Por el momento no tengo información acerca de lo que dice el estándar de C, asà que si quieres estár seguro de que sigues el estándar tendrás que poner un return 0 en la función main() (venga, que no son tantos caracteres)
Adicionalmente los únicos parámetros estándar a la función main son, o bien ninguno: int main () {} (para los programadores de C, no hay que poner void) o bien: int main (int argc, char * argv[]) {}
En este link está la opinión de Bjarne Stroustrup - creador del lenguaje C++ - al respecto: http://www.research.att.com/~bs/bs_faq2.html#void-main
2.- Uso de la biblioteca conio.h
La biblioteca conio.h no es estándar sino que es propiedad de Borland, y está incluida en sus compiladores. Es una biblioteca de manejo de entrada y salida de datos en la consola para el sistema DOS. Que no sea estandar significa que no la encontrarás implementada -o al menos totalmente- en muchos compiladores, y más importante, en otros sistemas, lo que reduce la portabilidad de tu código. Por otro lado la biblioteca se considera obsoleta, y es posible que algunas de las funciones que incluye no funcionen o lo hagan incorrectamente.
Pero lo más importante es darse cuenta de que esta biblioteca no es estrictamente necesaria para programar. Por experiencia las funciones más usadas de esta biblioteca son, con diferencia, clrscr() gotoxy() y getch().
clrscr() borra la pantalla, algo que por lo general no es necesario; si estás ejecutando un programa en lÃnea de comandos es fácil y rápido teclear un comando (cls, clear) que borre la pantalla en cuestión si lo necesitas. Por contra puede que sea molesto que un programa tome la iniciativa y borre la pantalla, ya que quizá tenga datos que el usuario quiera conservar. Es decir que no pasa nada si no borras la pantalla. Normalmente la persona que usa un programa de lÃnea de comandos no espera que eso ocurra.
gotoxy() el cursor en una determinada posición de la pantalla. Esto puede resultar útil en ocasiones, pero seamos sinceros: no suele ser muy necesario mover el cursor por la pantalla, o al menos no extrictamente necesario. Por ejemplo si estás aprendiendo a programar, eso es completamente supérfluo. Si programas una aplicación avanzada es casi seguro que tendrá un interface Gráfico. En último caso, si estás programando una aplicación avanzada en consola, prosiblemente a esas alturas ya sabrás cómo hacer tú mismo esta función utilizando llamadas al sistema, lo que tiene el beneficio de que puedes encapsular esa función de cara a la portabilidad, en vez de confiar en una función de biblioteca dependiente del sistema.
getch() recoge un carácter de la entrada estándar. Para eso tienes cerca de miles de funciones estándar que lo hacen. Vale, quizá no tantas, pero tienes al menos una en C getchar() y otra en C++ std::cin.get() ¿Porque usar getch() que NO es estándar cuando puedes usar una función estándar que hace exactamente lo mismo?
Antes de que me llameis mentiroso, hay que reconocer que getch() no hace exactamente lo mismo que las otras dos funciones. getch() NO presenta el caracter capturado por la pantalla y la entrada es sin buffer (lo que viene a decir que no hace falta pulsar enter) Sin embargo no creo que eso por lo general sea un grave problema, es más, una entrada sin buffer puede ser incluso un problema: no puedes corregir la entrada si te equivocas.
3.- ¿Cómo aprendo a programar en C/C++?
Aquà tienes dos buenos manuales, uno para C:
http://mec21.etsii.upm.es/ayudainf/aprendainf/AnsiC/leng_c.pdf
y otro para C++.
http://mec21.etsii.upm.es/ayudainf/aprendainf/Cpp/manualcpp.pdf
4.- ¿Y qué hago para programar?
Para poder llevar a la práctica lo que aprendas necesitarás un editor para escribir el código, como puede ser el bloc de notas o emacs. Un compilador para construir los ejecutables. Si además quieres depurar tus programas necesitarás un depurador. A menudo el editor, el compilador y el depurador vienen en un sólo programa conocido como Entorno de Desarrollo Integrado o IDE (Integrated Development Environment)
5.- Quiero un IDE ¿qué IDE puedo usar?
DeberÃas haber mirado el tema pegado en el que puedes encontrar entornos de programación y compiladores para todos los lenguajes, pero ya que no lo has hecho puedes descargar el dev cpp de la siguiente página:
http://www.bloodshed.net/devcpp.html
Tienes además un manual de instalación y primeros pasos con dev cpp hecho por el mismÃsimo Zheo en:
http://www.hispabyte.com/biblioteca/index.php?action=file&id=319
Editado: No hace falta aprender C para saber C++ (de hecho puede crear malos vicios)Por otro lado IDE estaba bien escrito
Errores Comunes:
*Olvidarse poner el & referenciando un lugar de memoria:
Código:
...
//Mal
scanf ("%d", numero);
//Bien
scanf ("%d", &numero);
...
*No Abrir/Cerrar correctamente Parentesis:
Código:
//MAL !!
Void main ()
{
...
for (...)
{
....
while (...)
{
....
}} //dos parentesis, falta uno de cierre !!!
//Bien
Void main ()
{
...
for (...)
{
....
while (...)
{
....
}
}
}
Si se tiene una indentación adecuada, no surgen este tipo de error.
*Mal uso de operadores:
Un ejemplo tipico es usar el operador unario ++ en cualquier lado y no poner bien los parentesis.
Código:
si a =1, b = 4, c = 5 -->
resultado = a + b / --c; //Total --> 2
resultado = (a + b) / (c - 1); //Total --> 5/4 > 2
Hay que saber bien las Precedencias de los operadores. Y poner parentesis es un segundo de vida que les cuesta, pero es un minuto ganado en cuando a la legibilidad del programa.
Crear un escaner
Este enlace, contiene el código fuente de una utilidad PING codeada en C++, héchale un ojo, parece muy interesante.
http://www.el-lolo.net/codigo/ping.zip (http://www.el-lolo.net/codigo/ping.zip)
Bueno, aquà os pongo un source que hice para manejar parte de los procesos.
Concretamente, puede listarlos junto con el pid de cada uno, cerrarlos por su pid/nombre, obtener información, y obtener el pid a partir del nombre.
Código:
/*
-------------------------------------------------------------------------------------
|| Name: Process Administration Tool 1.0 ||
|| Description: Programa para listar procesos, matar procesos por id/nombre, ||
|| obtener información sobre un proceso determinado, y obtener el PID ||
|| a través del nombre del Módulo. ||
|| Autor: el_mejor_demolidor ||
|| ||
|| ||
|| ||
-------------------------------------------------------------------------------------
D:\>Process.Administration.Tool.1.0.exe
Process Administrator Tool 1.0 - Coded by el_mejor_demolidor
----------------------------------------------------
[!] Argumentos incorrectos
Opciones:
+ Listar procesos: -l
+ Cerrar proceso mediante PID: -kpid pid
+ Cerrar proceso mediante nombre: -kname nombre
+ Informacion sobre un proceso: -i pid
+ Buscar PID: -pid nombre
D:\>
*/
#include <stdio.h>//I/O
#include <windows.h>//Para usar con Tlhelp32.h
#include <Tlhelp32.h>//CreateToolhelp32Snapshot, Process32First, Process32Next,PROCESSENTRY32
#include <string.h>//strlen
/*CIERRA UN PROCESO MEDIANTE SU PID*/
int CierraPID(DWORD pid);
/*devuelde la descripción del error, a partir de su código*/
char *MensajeError(DWORD error_num);
void main(int argc, char *argv[])
{
/*LISTA LOS PROCESOS ACTUALES*/
int ListarProcesos();
/*BUSCA EL PID DE UN PROCESO*/
int BuscaPid(char *nombre);
/*DA INFORMACION SOBRE UN PROCESO*/
int InfoProc(DWORD pid);
//para cojer la salida de algunas funciones
int datos;
printf("\n Process Administrator Tool 1.0 - Coded by Lympex");
printf("\nContact: lympex[at]gmail[dot]com & http://l-bytes.tk");
printf("\n----------------------------------------------------\n");
if(argc<=1)
{
printf("\n[!] Argumentos incorrectos");
printf("\n
Opciones:");
printf("\n + Listar procesos: -l");
printf("\n + Cerrar proceso mediante PID: -kpid pid");
printf("\n + Cerrar proceso mediante nombre: -kname nombre");
printf("\n + Informacion sobre un proceso: -i pid");
printf("\n + Buscar PID: -pid nombre\n");
return;
}
//LISTAMOS LOS PROCESOS
if(!strcmp(argv[1],"-l"))
{
printf("\nPROCESO PID");printf("\n---------------------------------\n");ListarProcesos();
printf("\n");
return;
}
//CERRAR PROCESO MEDIANTE PID
if(!strcmp(argv[1],"-kpid"))
{
if(argc<2)
{
printf("\n[!] Debes indicar un PID\n");
}else{
CierraPID(atoi(argv[2]));
}
return;
}
//CERRAR PROCESO SEGÚN EL NOMBRE
if(!strcmp(argv[1],"-kname"))
{
if(argc<2)
{
printf("\n[!] Debes indicar un PID\n");
}else{
datos=BuscaPid(argv[2]);
if(datos!=-1)
{
CierraPID(datos);
}else{
printf("\n[!] No existe el proceso \x22%s\x22\n",argv[2]);
}
}
return;
}
//DA INFORMACIÃ"N SOBRE UN PROCESO
if(!strcmp(argv[1],"-i"))
{
if(argc<2)
{
printf("\n[!] Debes indicar un PID\n");
}else{
InfoProc(atoi(argv[2]));
}
return;
}
//CERRAR PROCESO SEGÚN EL NOMBRE
if(!strcmp(argv[1],"-pid"))
{
if(argc<2)
{
printf("\n[!] Debes indicar un PID\n");
}else{
datos=BuscaPid(argv[2]);
if(datos!=-1)
{
printf("\n
PID de \x22%s\x22 --> %d\n",argv[2],datos);
}else{
printf("\n[!] No existe el proceso \x22%s\x22\n",argv[2]);
}
}
return;
}
printf("\n[!] Argumentos incorrectos\n");
}
/*LISTA LOS PROCESOS ACTUALES*/
int ListarProcesos()
{
char *mid(char *str1, unsigned int inicio, unsigned int fin);
/*cargamos todos los procesos*/
HANDLE proc=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
/*para almacenar los datos de cada proceso*/
PROCESSENTRY32 datos;
/*para almacenar el código del error*/
int errorn;
/*para bucles*/
unsigned int i;
/*miramos si el handle se ha creado*/
if(proc==INVALID_HANDLE_VALUE)
{
printf("\n[!] Error al crear el handle\n");
return 1;
}
Process32First(proc,&datos);
printf("\n%s",datos.szExeFile);
/*creamos un bucle cada vez que tenemos que mostrar un proceso, para saber cuántos espacios tenemos que colocar,
para mostrar los pid´s alineados*/
for(i=0;i<30-strlen(datos.szExeFile);i++)
{
printf("_");
}printf("%d",datos.th32ProcessID);
for(;
{
Process32Next(proc,&datos);
errorn=GetLastError();
if(errorn>0)
{
if(errorn!=ERROR_NO_MORE_FILES)
{
printf("\n[!] Error %d - %s",errorn,MensajeError(errorn));
}
return 0;
}
if(strlen(datos.szExeFile)>=26)
{
printf("\n%s",mid(datos.szExeFile,0,26));
}else{
printf("\n%s",datos.szExeFile);
}
/*creamos un bucle cada vez que tenemos que mostrar un proceso, para saber cuántos espacios tenemos que colocar,
para mostrar los pid´s alineados*/
if(strlen(datos.szExeFile)>=26)
{
for(i=0;i<4;i++)
{
printf("_");
}printf("%d",datos.th32ProcessID);
}else{
for(i=0;i<30-strlen(datos.szExeFile);i++)
{
printf("_");
}printf("%d",datos.th32ProcessID);
}
}
CloseHandle(proc);
return 0;
}
//CIERRA UN PROCESO MEDIANTE SU PID
int CierraPID(DWORD pid)
{
//handle para manejar el proceso
HANDLE proceso;
//abrimos el proceso
if((proceso=OpenProcess(PROCESS_TERMINATE,FALSE,pid))==NULL)
{
//lo anunciamos
printf("\n[!] Error al abrir el proceso con PID=%d # %s",pid,MensajeError(GetLastError()));
return 1;
}
//cerramos el proceso
if(TerminateProcess(proceso,0)==TRUE)
{
printf("\n
Proceso cerrado!\n");
}else{
printf("\n
Error al cerrar el proceso # %s\n",MensajeError(GetLastError()));
}
CloseHandle(proceso);
return 0;
}
/*BUSCA EL PID DE UN PROCESO*/
int BuscaPid(char *nombre)
{
/*cargamos todos los procesos*/
HANDLE proc=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
/*para almacenar los datos de cada proceso*/
PROCESSENTRY32 datos;
/*para almacenar el código del error*/
int errorn;
/*devuelde la descripción del error, a partir de su código*/
char *MensajeError(DWORD error_num);
/*miramos si el handle se ha creado*/
if(proc==INVALID_HANDLE_VALUE)
{
printf("\n[!] Error al crear el handle\n");
return -1;
}
Process32First(proc,&datos);
if(!strcmp(nombre,datos.szExeFile))
{
return datos.th32ProcessID;
}
for(;
{
Process32Next(proc,&datos);
errorn=GetLastError();
if(errorn>0)
{
if(errorn!=ERROR_NO_MORE_FILES)
{
printf("\n[!] Error %d - %s",errorn,MensajeError(errorn));
}
return 0;
}
if(!strcmp(nombre,datos.szExeFile))
{
return datos.th32ProcessID;
}
}
CloseHandle(proc);
return 0;
}
/*DA INFORMACION SOBRE UN PROCESO*/
int InfoProc(DWORD pid)
{
HANDLE proc;
/*estructuras que recojen información del proceso*/
MODULEENTRY32 datos;
/*función que nos indica la descripcion de un error*/
char *MensajeError(DWORD error_num);
printf("\n
Creando handle...");
//abrimos el proceso
proc=CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,pid);//con posibilidad para acceder a todo
if(proc==INVALID_HANDLE_VALUE)
{
printf("ERROR - %s",MensajeError(GetLastError()));
return 0;
}printf("OK");
if(Module32First(proc,&datos)==FALSE)
{
printf("\n[!] Error al obtener los datos\n");
CloseHandle(proc);
return 0;
}
//obtenemos los datos
printf("\n
Obteniendo datos del pid=%d...",pid);
if(Module32First(proc,&datos)==FALSE)
{
printf("\n[!] Error al obtener los datos\n");
CloseHandle(proc);
return 0;
}printf("OK");
//mostramos la info
printf("\n\n
Informacion sobre el proceso con pid=%d",pid);
printf("\n-----------------------------------------------");
printf("\n + Nombre el ejecutable: %s",datos.szModule);
printf("\n + Ruta del ejecutable: %s",datos.szExePath);
printf("\n + PID: %d",datos.th32ProcessID);
printf("\n + Tam. Modulo: %d bytes",datos.modBaseSize);
printf("\n-----------------------------------------------");
printf("\n\n
Cerrando handle...");
//cerramos el handle
if(CloseHandle(proc)!=0)
{
printf("OK\n");
}else{
printf("\nERROR - %s\n",MensajeError(GetLastError()));
}
return 0;
}
//devuelde la descripción del error, a partir de su código
char *MensajeError(DWORD error_num)
{
char *lpMsgBuf;
//cojemos el mensaje del error
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error_num,
0,
(LPTSTR) &lpMsgBuf,
0,
NULL
);
return lpMsgBuf;
}
//parte una string desde 'inicio' hasta 'fin'
char *mid(char *str1, unsigned int inicio, unsigned int fin)
{
char *resultado;
unsigned int i;
if(inicio>fin)
{
return NULL;
}
resultado=(char *) malloc((fin-inicio)*sizeof(char));
for(i=inicio;i<fin;i++)
{
resultado[i-inicio]=str1;
resultado[(i-inicio)+1]='\0';
}
return resultado;
}
EL LENGUAJE DE PROGRAMACIÃ"N C.
M. en C. Luis Rubén Rusiles Zamora
CONTENIDO
CapÃtulo 1. INTRODUCCIÃ"N GENERAL
CapÃtulo 2. TIPOS OPERADORES Y EXPRESIONES
CapÃtulo 3. CONTROL DE FLUJO
CapÃtulo 4. FUNCIONES Y LA ESTRUCTURA DEL PROGRAMA
CapÃtulo 5. APUNTADORES Y ARREGLOS
CapÃtulo 6. ESTRUCTURAS
CapÃtulo 7. ENTRADA Y SALIDA
INDICE ALFABÉTICO
CapÃtulo 1. INTRODUCCIÃ"N GENERAL
A continuación se muestra un programa para imprimir
hello, world
--------------------------------------------------------------------------------
#include <stdio.h>
main(){
printf("hello, ");
printf("world ");
printf("\n");
}
--------------------------------------------------------------------------------
Algunos caracteres escape
\t tabulador
\b retroceso
\" comillas
\\ diagonal
Tipos de datos básicos
int entero
float real
char character
short entero corto
long entero largo
double real doble precisión
Proposición while
La sintaxis de la proposición while es la siguiente:
while (fahr <= upper)
{
...
}
Constantes simbólicas
#define LOWER 0
#define UPPER 30
#define STEP 20
Algunos Ejemplos de programas simples en C
--------------------------------------------------------------------------------
main() /* copia la entrada a la salida, versión 2 */
{
int c;
while ((c=getchar)!=EOF)
putchar(c);
}
--------------------------------------------------------------------------------
main() /* Table Farenheit-Celsius */
{
int fahr;
for (fahr=0; fah<=300; fahr=fahr+20)
printf("4d %6.1f\n",fahr,(5.0/9.0)*(fahr-32));
}
--------------------------------------------------------------------------------
main() /* cuenta los caracteres leÃdos */
{
double nc;
for (nc=0; getchar()!=EOF; ++nc)
;
printf("%.0f\n",nc);
}
--------------------------------------------------------------------------------
Asignación multiple.
Se propaga de derecha a izquierda, por ejemplo:
nl=nw=nc=0;
se entiende como:
nc=(nl=(nw=0)));
OR
if (c==' ' || c=='\n' || c=='\t')
if
if (expresión)
proposición 1
else
proposición 2
Declaración de un vector de 10 enteros. El primero es ndigit[0];
int ndigit[10];
Funciones
Formato de una función:
[tipo] nombre (lista de argumentos si los hay)
{
declaraciones;
proposiciones;
}
solo se usa "tipo" si se regresa un valor.
Ej. Función "power"
--------------------------------------------------------------------------------
power(int x, int n)
{
int i, p;
p=1;
for (i=1; i<=n; ++i)
p=p*x;
return (p);
}
Ej. Uso de la función "power"
--------------------------------------------------------------------------------
#include
main()
{
for (i=0; i<10; ++i)
printf("%d %d %d\n", i, power(2,i), power (-3,i));
}
--------------------------------------------------------------------------------
Las llamadas en C son por valor.
Lo que permite modificar las copias privadas y temporales, es decir, las variables automáticas.
Cuando se usa un arreglo como argumento, se pasa la dirección del mismo.
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;
CapÃtulo 2. 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>='0' && s<='9';++i)
n=10*n+s-'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)
{
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 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
CapÃtulo 3. 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 */
{
int c, i, nwhite, nother, ndigit[10];
nwhite=nother=0;
for (i=0; i<10; i++)
ndigit=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);
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)
proposicion
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;
s=s[j];
s[j]=c;
}
}
--------------------------------------------------------------------------------
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<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[j]<0)
goto found;
/* no se encontró */
found: /* se encontró en la posición i,j */
..
--------------------------------------------------------------------------------
CapÃtulo 4. 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!='\0'; i++)
{
for (j=i, k=0; t[k]!='\0' && s[j]==t[k]; j++,k++)
;
if (t[k]=='\0')
return (i);
}
}
escrebo más de una parte,porque pasava de el limite de caracteres!!
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
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á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=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
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.
CapÃtulo 5. Apuntadores y arreglos.
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" 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[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" 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();
CapÃtulo 6. ESTRUCTURAS
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.uval.ival para ival
symtab.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;
CapÃtulo 7. ENTRADA Y SALIDA
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
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\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 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 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
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 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
#include
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);
}
el unico problema és:és un poco avanzado!!
curso pratico (http://espanol.geocities.com/ssomeonexx/cursoprt.zip%5B/url) <---tyene el basico para empezar!
Solo una recomendacion para todos los usuarios y aficionados a C++...... Cuando esten trabajando con punteros traten de declararlos antes de todas sus variables, por que el declararlo en cualquier parte provoca que los punteros almacenen basura