-^Blue Hacking^-

El mejor hacking al alcance de todos
 
ÍndiceFAQRegistrarseConectarse

Comparte | 
 

 Curso programación en C

Ver el tema anterior Ver el tema siguiente Ir abajo 
Ir a la página : Precedente  1, 2, 3  Siguiente
AutorMensaje
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Sáb Oct 25, 2008 9:04 pm

CURSO DE C. TEMA 6.6 (de 9).


/*-------------------------*/
/* Ejemplo en C nº 20: */
/* EJ20.C */
/* */
/* "if" incorrecto */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/* - PCC 2.1c */
/*-------------------------*/

#include <stdio.h>

int numero;

main()
{
printf("Escriba un número: ");
scanf("%d", &numero);
if (numero < 0)
printf("El número es negativo.\n");
else if (numero = 0) /* Error: asignación */
printf("El número es cero.\n");
else
printf("El número es positivo.\n");
}

/*-------------------------*/

Y si esto es un error, por qué el compilador "avisa" en vez de parar y dar un error "serio"? Pues porque no tiene por qué ser necesariamente un error: podemos hacer

a = b
if (a > 2) ...


o bien

if ((a=b) > 2) ...

Es decir, en la misma orden asignamos el valor y comparamos (parecido a lo que hacíamos con "b = ++a", por ejemplo).

Como ya hemos comentado en el apartado anterior, puede darse el caso de que tengamos que comprobar varias condiciones simultáneas, y entonces deberemos usar "y" (&&), "o" (||) y "no" (!) para enlazarlas:

if ((opcion==1) && (usuario==2)) ...
if ((opcion==1) || (opcion==3)) ...
if ((!(opcion==opcCorrecta)) || (tecla==kESC)) ...
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Sáb Oct 25, 2008 9:04 pm

CURSO DE C. TEMA 6.7 (de 9).


En C hay otra forma de asignar un valor según se dé una condición o no. Es el "operador condicional" ? : que se usa

condicion ? v1 : v2;

y es algo así como "si se da la condición, toma el valor v1; si no, toma el valor v2". Un ejemplo de cómo podríamos usarlo sería

resultado = (operacion == '-') ? a-b : a+b;

que, aplicado a un programita quedaría:

/*-------------------------*/
/* Ejemplo en C nº 21: */
/* EJ21.C */
/* */
/* Operador condicional ? */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/* - PCC 2.1c */
/*-------------------------*/

#include <stdio.h>

int a, b, resultado;
int operacion;

main()
{
printf("Escriba un número: ");

scanf("%d", &a);
printf("Escriba otro: ");
scanf("%d", &b);
printf("Escriba una operación (1 = resta; otro = suma): ");
scanf("%d", &operacion);
resultado = (operacion == 1) ? a-b : a+b;
printf("El resultado es %d.\n", resultado);
}

/*-------------------------*/

(Recordemos que en C las expresiones lógicas valían cero -falso- o distinto de cero -verdadero-, de modo que eso que he llamado "condición" puede ser realmente otros tipos de expresiones, como por ejemplo una operación aritmética).


Una nota sobre este programa: alguien avispado puede haberse dado cuenta de que en el ejemplo comparo con el símbolo '-' y en cambio en el programa comparo con 1. Y este cambio de actitud? No se podría haber usado scanf("%c",...) o bien "getchar()" para leer si se pulsa la tecla '-'?

Pues no es tan sencillo, desafortunadamente. El motivo es que cuando pulsamos INTRO tras teclear un número, esta pulsación se queda en el buffer del teclado, y eso es lo que leería el getchar.
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Sáb Oct 25, 2008 9:05 pm

CURSO DE C. TEMA 6.8 (de 9).


Vamos a verlo con un ejemplo:

/*-------------------------*/
/* Ejemplo en C nº 22: */
/* EJ22.C */
/* */
/* Problemas de "getchar" */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/* - PCC 2.1c */
/*-------------------------*/

#include <stdio.h>

int a, b, resultado;
char operacion;

main()
{
printf("Escriba un número: ");
scanf("%d", &a);
printf("Escriba otro: ");
scanf("%d", &b);
printf("Escriba una operación (1 = resta; otro = suma): ");
operacion = getchar(); /* Da resultado inesperado */
printf("\nLa operación es %c (%d).\n", resultado, resultado);
resultado = (operacion == '-') ? a-b : a+b;
printf("Y el resultado es %d.\n", resultado);
}

/*-------------------------*/

Leyéndolo, parece que todo debería salir bien, pero al ejecutarlo no nos deja ni teclear el símbolo, sino que toma el valor que hubiera en el buffer del teclado. Obtenemos el mismo resultado con

scanf("%c", &operacion);

Por tanto, deberíamos vaciar primero el buffer del teclado. En el próximo apartado veremos cómo hacerlo.
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Sáb Oct 25, 2008 9:05 pm

CURSO DE C. TEMA 6.9 (de 9).


Finalmente, cuando queremos ver varios posibles valores, sería muy pesado tener que hacerlo con muchos "if" seguidos o encadenados. La alternativa es la orden "switch", cuya sintaxis es

switch (expresión)
{
case caso1: sentencia1;
break;
case caso2: sentencia2;
sentencia2b;
break;
...
case casoN: sentenciaN;
break;
default:
otraSentencia;
};

Para quien venga de Pascal, el equivalente sería

case expresión of
caso1: sentencia1;
caso2: begin sentencia2; sentencia2b; end;
...
casoN: sentenciaN;
else
otraSentencia;
end;

Como la mejor forma de verlo es con un ejemplo, vamos allá:

/*-------------------------*/
/* Ejemplo en C nº 23: */
/* EJ23.C */
/* */
/* Uso de "switch" */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/* - PCC 2.1c */
/*-------------------------*/

#include <stdio.h>

char tecla;

main()
{
printf("Pulse una tecla y luego Intro: ");
tecla = getchar();
switch (tecla)
{
case ' ': printf("Espacio.\n");
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0': printf("Dígito.\n");
break;

default: printf("Ni espacio ni dígito.\n");
}
}

/*-------------------------*/

En primer lugar, insisto en una cosa: getchar() es lo mismo que scanf("%c",...). Por qué lo digo? Porque no basta con escribir la letra: la letra se leerá realmente cuando se procese todo el buffer del teclado, es decir, que debemos pulsar Intro después de la tecla que elijamos.

Otra cosa: no se pueden definir subrangos de la forma '0'..'9' ("desde 0 hasta 9", cosa que sí ocurre en Pascal), sino que debemos enumerar todos los casos. Pero, como se ve en el ejemplo, para los casos repetitivos no hace falta repetir las sentencias a ejecutar para cada uno, porque cada opción se analiza hasta que aparece la palabra "break". Por eso, las opciones '1' hasta '0' hacen lo mismo (todas terminan en el "break" que sigue a '0').

Finalmente, "default" indica la acción a realizar si no es ninguno de los casos que se han detallado anteriormente. Esta parte es opcional (si no ponemos la parte de "default", simplemente se sale del "switch" sin hacer nada cuando la opción no sea ninguna de las indicadas). Después de "default" no hace falta poner "break", porque se sabe que ahí acaba la sentencia "switch".
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Dom Oct 26, 2008 8:17 pm

CURSO DE C. TEMA 7. Bucles.

Vamos a ver cómo podemos crear partes del programa que se repitan un cierto número de veces (bucles).

Según cómo queramos que se controle ese bucle, tenemos tres posibilidades, que básicamene se pueden describir como:
· for: La orden se repite desde que una variable tiene un valor inicial hasta que alcanza otro valor final.

· while: Repite una sentencia mientras que sea cierta la condición que indicamos. La condición se comprueba antes de realizar la sentencia.
· do..while: Igual, pero la condición se comprueba después de realizar la sentencia.

Las diferencias son: "for" normalmente se usa para algo que se repite un número concreto de veces, mientras que "while" se basa en comprobar si una condición es cierta (se repetirá un número indeterminado de veces).


For

El formato de "for" es

for (valorInic; CondicRepetic; Incremento)
Sentencia;

Así, para contar del 1 al 10, tendríamos 1 como valor inicial, <=10 como condición de repetición, y el incremento sería de 1 en 1. Por tanto, el programa quedaría:

/*-------------------------*/
/* Ejemplo en C nº 24: */
/* EJ24.C */
/* */
/* Escribe del 1 al 10, */
/* con "for". */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/* - PCC 2.1c */
/*-------------------------*/

#include <stdio.h>

int contador;

main()
{
for (contador=1; contador<=10; contador++)
printf("%d ", contador);
}

/*-------------------------*/

Recordemos que "contador++" es una forma abreviada de escribir "contador=contador+1", con lo que aumentamos la variable de uno en uno.

Realmente, la parte de la orden que he llamado "Incremento" no tiene por qué incrementar la variable, aunque ése es su uso más habitual. Es simplemente una orden que se ejecuta cuando se termine la "Sentencia" y antes de volver a comprobar si todavía se cumple la condición de repetición. Por eso, si escribimos la siguiente línea:

for (contador=1; contador<=10; )

la variable "contador" no se incrementa nunca, por lo que nunca se cumplirá la condición de salida: nos quedamos encerrados dando vueltas dentro de la orden que siga al "for".

Un caso todavía más exagerado de algo a lo que se entra y de lo que no se sale sería la siguiente orden:
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Dom Oct 26, 2008 8:17 pm

CURSO DE C. TEMA 7.2 (de 7).


Los bucles "for" se pueden anidar (incluir uno dentro de otro), de modo que podríamos escribir las tablas de multiplicar del 1 al 5 con:

/*-------------------------*/
/* Ejemplo en C nº 25: */
/* EJ25.C */
/* */
/* Escribe las tablas de */
/* multiplicar del 1 al */
/* 5, con "for". */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/* - PCC 2.1c */
/*-------------------------*/

#include <stdio.h>

int tabla, numero;

main()
{
for (tabla=1; tabla<=5; tabla++)
for (numero=1; numero<=10; numero++)
printf("%d por %d es %d\n", tabla, numero, tabla*numero);
}

/*-------------------------*/
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Dom Oct 26, 2008 8:17 pm

CURSO DE C. TEMA 7.3 (de 7).


En estos casos después de "for" había un única sentencia. Si queremos que se hagan varias cosas, basta definirlas como un bloque (una sentencia, compuesta) encerrándolas entre llaves. Por ejemplo, si queremos mejorar el ejemplo anterior haciendo que deje una línea en blanco entre tabla y tabla, sería:

/*-------------------------*/
/* Ejemplo en C nº 26: */
/* EJ26.C */
/* */
/* Escribe las tablas de */
/* multiplicar del 1 al */
/* 5, con "for". Deja */
/* espacios intermedios. */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/* - PCC 2.1c */
/*-------------------------*/

#include <stdio.h>

int tabla, numero;

main()
{
for (tabla=1; tabla<=5; tabla++)
{
for (numero=1; numero<=10; numero++)
printf("%d por %d es %d\n", tabla, numero, tabla*numero);
printf("\n");
}

}

/*-------------------------*/
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Dom Oct 26, 2008 8:17 pm

CURSO DE C. TEMA 7.4 (de 7).


Al igual que en Pascal, para "contar" no necesariamente hay que usar números:

/*-------------------------*/
/* Ejemplo en C nº 27: */
/* EJ27.C */
/* */
/* Escribe las letras de */
/* la 'a' a la 'z', con */
/* "for". */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/* - PCC 2.1c */
/*-------------------------*/

#include <stdio.h>

char letra;

main()
{
for (letra='a'; letra<='z'; letra++)
printf("%c ", letra);
}

/*-------------------------*/

Creo que este programa se explica por sí solo: empezamos en la "a" y terminamos en la "z", aumentando de uno en uno.
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Dom Oct 26, 2008 8:17 pm

CURSO DE C. TEMA 7.5 (de 7).


Si queremos contar de forma decreciente, o de dos en dos, o como nos interese, basta indicarlo en la condición de finalización del "for" y en la parte que lo incrementa:

/*-------------------------*/
/* Ejemplo en C nº 28: */
/* EJ28.C */
/* */
/* Escribe las letras de */
/* la 'z' a la 'a', (una */
/* sí y otra no), con */
/* "for". */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/* - PCC 2.1c */
/*-------------------------*/

#include <stdio.h>

char letra;

main()
{
for (letra='z'; letra>='a'; letra-=2)
printf("%c ", letra);
}

/*-------------------------*/

Estos son los casos sencillos de la orden "for", pero también podemos rizar el rizo, y usar dos variables (o más) para controlarlo, o salir de un bucle "for" con otras órdenes, como "break", "continue" y "goto" .
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Dom Oct 26, 2008 8:18 pm

CURSO DE C. TEMA 7.6 (de 7). While.

Habíamos comentado que "while" podía aparecer en dos tipos de construcciones, según comprobemos la condición al principio o al final.

En el primer caso, su sintaxis es

while (condición)
sentencia;

Es decir, la sentencia se repetirá mientras la condición se cierta. Si la condición es falsa ya desde un principio, la sentencia no se ejecuta nunca. Si queremos que se repita más de una sentencia, basta agruparlas entre { y }.

Un ejemplo que nos diga el triple de cada número que tecleemos, y que pare cuando tecleemos el número 0, podría ser:

/*-------------------------*/
/* Ejemplo en C nº 29: */
/* EJ29.C */

/* */
/* Escribe el triple de */
/* los números tecleados, */
/* con "while" */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/* - PCC 2.1c */
/*-------------------------*/

#include <stdio.h>

int numero;
char frase[60];

main()
{
printf("Teclea un número (0 para salir): ");
scanf("%d", &numero);
while (numero)
{
printf("Su triple es %d.\n", numero*3);
printf("Teclea otro número (0 para salir): ");
scanf("%d", &numero);
}
}

/*-------------------------*/

Y eso de "while (numero)"? Pues es lo mismo que "while (numero != 0)". En caso de duda, toca repasar el tema anterior.

En este ejemplo, si se introduce 0 la primera vez, la condición es falsa y ni siquiera se entra al bloque del "while", terminando el programa inmediatamente.
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Dom Oct 26, 2008 8:18 pm

CURSO DE C. TEMA 7.7 (de 7).

Como ejemplo de la otra aplicación (la condición se comprueba al final), vamos a ver cómo sería la típica clave de acceso, pero con una pequeña diferencia: como todavía no sabemos manejar cadenas de texto con una cierta soltura, la clave será un número:

/*-------------------------*/
/* Ejemplo en C nº 30: */
/* EJ30.C */
/* */
/* Clave de acceso numé- */
/* rica, con "do..while" */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/* - PCC 2.1c */
/*-------------------------*/

#include <stdio.h>

int valida = 711;
int clave;

main()
{
do
{
printf("Introduzca su clave numérica: ");
scanf("%d", &clave);
if (clave != valida) printf("No válida!\n");
}
while (clave != valida);
printf("Aceptada.\n");
}

/*-------------------------*/

En este caso, se comprueba la condición al final, de modo que se nos preguntará la clave al menos una vez. Mientras que la respuesta que demos no sea la correcta, se nos vuelve a preguntar. Finalmente, cuando tecleamos la clave correcta, el ordenador escribe "Aceptada" y termina el programa.
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Lun Oct 27, 2008 9:51 am

CURSO DE C. TEMA 8. Definición de constantes.

Cuando desarrollamos un programa, nos podemos encontrar con que hay variables que realmente "no varían" a lo largo de la ejecución de un programa, sino que su valor es constante.

Hay una manera especial de definirlas, que es con el especificador "const", que tiene el formato

const Nombre = Valor;

Así, en el ejemplo anterior (la clave de acceso numérica) habría sido más correcto hacer

/*-------------------------*/
/* Ejemplo en C nº 31: */
/* EJ31.C */
/* */
/* Clave de acceso numé- */
/* rica, con "do..while" */
/* y "const" */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/*-------------------------*/

#include <stdio.h>

const valida = 711;
int clave;

main()
{
do
{
printf("Introduzca su clave numérica: ");
scanf("%d", &clave);

if (clave != valida) printf("No válida!\n");
}
while (clave != valida);
printf("Aceptada.\n");
}

/*-------------------------*/

El ejemplo lo he dejado así para que resulte más familiar a quien venga de Pascal, pero realmente en C la palabra "const" es un modificador, algo que da información extra, de modo que su uso habitual sería:

const int valida = 711;

que se leería algo parecido a "La variable 'válida' es un entero, de valor 711, y este valor deberá permanecer constante".

Cuando tenemos varias constantes, cuyos valores son números enteros, y especialmente si son números enteros consecutivos, tenemos una forma abreviada de definirlos. Se trata de enumerarlos:

enum diasSemana { LUNES, MARTES, MIERCOLES, JUEVES, VIERNES, SABADO,
DOMINGO };


(Las he escrito en mayúsculas para que se sepa "de un vistazo" que son constantes, no variables. Más adelante comentaré algo más sobre este convenio.)

La primera constante valdrá 0, y las demás irán aumentando de una en una, de modo que en nuestro caso valen:

LUNES = 0, MARTES = 1, MIERCOLES = 2, JUEVES = 3, VIERNES = 4,
SABADO = 5, DOMINGO = 6

Si queremos que los valores no sean justo estos, podemos dar valor a cualquiera de las contantes, y las siguientes irán aumentando de uno en uno. Por ejemplo, si escribimos

enum diasSemana { LUNES=1, MARTES, MIERCOLES, JUEVES=6, VIERNES,
SABADO=10, DOMINGO };

Ahora sus valores son:

LUNES = 1, MARTES = 2, MIERCOLES = 3, JUEVES = 6, VIERNES = 7,
SABADO = 10, DOMINGO = 11


En C tenemos 3 tipos de variables, según si su valor se puede modificar o no, y de qué forma:

· Las que habíamos visto hasta ahora, a las que podíamos asignar un valor.

· Las que definimos y no permitiremos que se altere su valor, para lo que usamos el modificador "const".
· Las que pueden cambiar en cualquier momento, y para ellas usaremos el modificador "volatile". Es para el caso (poco habitual) de que su valor pueda ser cambiado por algo que no sea nuestro programa principal, y así obligamos al compilador en este caso a que lea el valor de la variable en memoria, en vez de mirarlo en algún registro temporal en el que lo pudiera haber guardado para mayor velocidad.
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Lun Oct 27, 2008 9:52 am

CURSO DE C. TEMA 8.2 (de 4)

Hay otra forma muy frecuente en C de definir constantes, que es con la directiva #define. Al tratarse de una directiva del preprocesador, es una información que no llega al compilador cuando tiene que traducir a código máquina, sino que ya en una primera pasada se cambia su nombre por el valor correspondiente en cada punto en que aparezca.

Así, algo como

#define Valida 711
if (numero == Valida) [...]

se convertiría inmediatamente a

if (numero == 711) [...]

que es lo que realmente el compilador traduciría a código máquina. Por eso es frecuente llamarlas "constantes simbólicas".

Así, este último ejemplo quedaría

/*-------------------------*/
/* Ejemplo en C nº 32: */

/* EJ32.C */
/* */
/* Clave de acceso numé- */
/* rica, con "do..while" */
/* y "#define" */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/* - PCC 2.1c */
/*-------------------------*/

#include <stdio.h>

#define kVALIDA 711

int clave;

main()
{
do
{
printf("Introduzca su clave numérica: ");
scanf("%d", &clave);
if (clave != kVALIDA) printf("No válida!\n");
}
while (clave != kVALIDA);
printf("Aceptada.\n");
}

/*-------------------------*/

¿Y por qué lo he puesto en mayúsculas y precedido por una k?

Simplemente por legibilidad: en C es inmediato diferenciar una variable de una función, porque, como ya hemos mencionado y veremos de nuevo en el próximo tema, una función necesariamente acaba con paréntesis, incluso aunque no tenga parámetros; en cambio, no hay nada que distinga "a simple vista" una constante de una variable. Por eso, es frecuente "obligar" a que sea fácil distinguir las constantes de las variables sólo con ojear el listado, y las formas más habituales son escribir las variables en minúsculas y las constantes en mayúsculas, o bien comenzar las constantes con una "k", o bien ambas cosas.
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Lun Oct 27, 2008 9:52 am

CURSO DE C. TEMA 8.3 (de 4). Definición de tipos.

El tipo de una variable nos indica el rango de valores que puede tomar. Tenemos creados para nosotros los tipos básicos, pero puede que nos interese crear nuestros propios tipos de variables, para lo que usamos "typedef".

El formato es "typedef tipo nombre;" y realmente lo que hacemos es darle un nuevo nombre, lo que nos puede resultar útil por ejemplo si venimos de Pascal (cómo no) y consideramos más legible tener un tipo "boolean". Vamos a verlo directamente con un ejemplo:

/*-------------------------*/
/* Ejemplo en C nº 33: */
/* EJ33.C */
/* */
/* Clave de acceso numé- */
/* rica retocada, con */
/* typedef y #define */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/* - PCC 2.1c */
/*-------------------------*/


#include <stdio.h>

#define VALIDA 711 /* Clave correcta */

#define TRUE 1 /* Nostalgia de los boolean */
#define FALSE 0

typedef int boolean; /* Definimos un par de tipos */
typedef int integer;

integer clave; /* Y dos variables */
boolean acertado;

main()
{
do
{
printf("Introduzca su clave numérica: ");
scanf("%d", &clave);
acertado = (clave == VALIDA);
if (acertado == FALSE) printf("No válida!\n");
}
while (acertado != TRUE);
printf("Aceptada.\n");
}

/*-------------------------*/

Todo esto se puede hacer más cortito, claro, pero es para que se vea un ejemplo de su uso.
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Lun Oct 27, 2008 9:53 am

CURSO DE C. TEMA 8.4 (de 4).

También podemos usar "typedef" para dar un nombre corto a todo un struct:

/*-------------------------*/
/* Ejemplo en C nº 34: */
/* EJ34.C */
/* */
/* Uso de "typedef" con */
/* "struct". */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/* - PCC 2.1c */
/*-------------------------*/

#include <stdio.h>

typedef struct { /* Defino el tipo "datos" */
int valor;
float coste;
char ref;
} datos;

datos ejemplo; /* Y creo una variable de ese tipo */

main()
{
ejemplo.valor = 34;
ejemplo.coste = 1200.5;
ejemplo.ref = 'A';
printf("El valor es %d", ejemplo.valor);
}

/*-------------------------*/


N.
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Mar Oct 28, 2008 1:00 pm

CURSO DE C. TEMA 9.

La programación estructurada trata de dividir el programa el bloques más pequeños, buscando una mayor legibilidad, y más comodidad a la hora de corregir o ampliar.

La idea es que un programa que cumpla una cierta misión tendrá que seguir ciertos pasos. Por ejemplo: para resolver un sistema de ecuaciones, los pasos podrían ser:

Pedir Número de Ecuaciones y de Incógnitas.
Para cada ecuación:
- Pedir coeficientes de x(i).
- Pedir término independiente.
Escribir el sistema como matriz.
Hacer ceros bajo la diagonal principal.
Aplicar sustitución regresiva.
Mostrar resultados.

Esos son los pasos que uno piensa cuando se plantea cómo resolver el problema (el "algoritmo"). Nosotros pensamos en "cosas a hacer", no en líneas de programa, por eso la forma más "natural" de trabajar es descomponer el problema en partes, en vez de intentar resolverlo tecleando una línea tras otra.

Es la clásica idea del "divide y vencerás": un programa que hayamos dividido en bloques (con una cierta conexión, no a lo loco) será más fácil de crear y de mantener.


En muchos lenguajes de programación (como Pascal o Basic), estos bloques son de dos tipos: procedimientos ("procedure") y funciones ("function").

La diferencia entre ellos es que un procedimiento ejecuta una serie de acciones que están relacionadas entre sí, y no devuelve ningún valor, mientras que la función sí que va a devolver valores.

Por ejemplo, un procedimiento podría saludarnos: da unos ciertos pasos, muestra un mensaje en pantalla y ya está.

Saluda;

Por el contrario una función podría ser la raíz cuadrada: da una serie de pasos, destinados a calcular cual es el valor, pero finalmente tiene que respondernos diciendo qué valor ha obtenido.

x = Raiz(9); (y el ordenador debe "devolverme" el número: 3)


En C sólo existen funciones, pero éstas pueden devolver unos valores de tipo "nulo", con lo cual salen a equivaler a un procedimiento (lo veremos algo más adelante).

Vamos a verlo mejor con un par de ejemplos.


Primero crearemos una función "potencia", que eleve un número entero a otro número entero, dando también como resultado un número entero. La forma de conseguir elevar un número a otro será a base de multiplicaciones, es decir:

3 elevado a 5 = 3 · 3 · 3 · 3 · 3

(multiplicamos 5 veces el 3 por sí mismo). En general, como nos pueden pedir cosas como "6 elevado a 100", usaremos la orden "for" para multiplicar tantas veces como haga falta:

/*-------------------------*/
/* Ejemplo en C nº 35: */
/* EJ35.C */
/* */
/* Función "potencia" */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/*-------------------------*/

#include <stdio.h>

int potencia(int base, int exponente)
{
int temporal = 1; /* Valor que voy hallando */
int i; /* Para bucles */

for(i=1; i<=exponente; i++) /* Multiplico "n" veces */
temporal *= base; /* Y calculo el valor temporal */

return temporal; /* Al final de las multiplicaciones,
} obtengo el valor que buscaba */


main()
{
int num1, num2;

printf("Introduzca la base: ");
scanf("%d", &num1);
printf("Introduzca el exponente: ");
scanf("%d", &num2);
printf("%d elevado a %d vale %d", num1, num2, potencia(num1,num2));
}

/*-------------------------*/

Vamos a comentar cosas sobre este programa:

· base y exponente son dos valores que se "le pasan" a la función: son sus "parámetros", unos datos que la función necesita. En nuestro caso, queremos elevar un número a otro, luego deberemos indicarle cual es el número que queremos elevar (la base) y a qué número lo queremos elevar (el exponente).

· El cuerpo de la función se indica entre llaves, como cualquier otro bloque de un programa.
· i y temporal son variables locales a la función "potencia": no se puede acceder a ellas desde ninguna otra parte del programa. Igual ocurre con num1 y num2: sólo se pueden leer o modificar desde "main". Las variables que habíamos visto hasta ahora no estaban dentro de ninguna función, por lo que eran globales a todo el programa. Una detalle importante: las variables locales se deberán declarar al principio de la función, antes de que aparezca ninguna orden.
· Con "return" salimos de la función e indicamos el valor que queremos que se devuelva. En este caso es lo que habíamos llamado "temporal", que era el valor que íbamos calculando para la potencia en cada paso.

· Recordemos que los "int" en MsDos están limitados a 32767, luego si el resultado es mayor, obtendremos valores erróneos. Esto se puede evitar usando enteros largos o números reales.
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Mar Oct 28, 2008 1:00 pm

CURSO DE C. TEMA 9.2 (de 6).

Ahora vamos a ver un ejemplo de función que no devuelva ningún valor:

/*-------------------------*/
/* Ejemplo en C nº 36: */
/* EJ36.C */
/* */
/* Función "hola" */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/*-------------------------*/

#include <stdio.h>

void saludo(char nombre[])
{
printf("Hola, %s, cómo estás?", nombre);
}

main()
{
saludo("Eva");
printf("\n");
}

/*-------------------------*/

Y los comentarios de rigor:
· Esta función es de tipo "void" (nulo), con lo que indicamos que no queremos que devuelva ningún valor.

· Con eso de "char nombre[]" indicamos que le vamos a pasar una cadena de caracteres, pero no hace falta que digamos su tamaño, como hacíamos cuando las declarábamos. Así podremos pasar cadenas de caracteres tan grandes (o tan pequeñas) como queramos.
· Esta función no termina en "return", porque no queremos que devuelva ningún valor. Aun así, sí que podríamos haber escrito

void saludo(char nombre[])
{
printf("Hola, %s, cómo estás?", nombre);
return;
}


pero la diferencia con respecto a la función "potencia" está en que en este caso hemos dejado el "return" sólo para que se vea dónde termina la función, y no indicamos nada como "return valor", dado que no hay ningún valor que devolver.

Este tipo de funciones, que no devuelven ningún valor, son el equivalente de los "procedures" del lenguaje Pascal.
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Mar Oct 28, 2008 1:01 pm

CURSO DE C. TEMA 9.3 (de 6).

Recordemos en este punto que "main" es una función, de tipo entero (se considera así si no se indica el tipo, que es lo que estábamos haciendo hasta ahora), por lo que la forma más correcta de escribir el ejemplo de la potencia sería:

/*-------------------------*/
/* Ejemplo en C nº 35 (b) */
/* EJ35B.C */
/* */
/* Función "potencia"; */
/* función "main" */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/*-------------------------*/

#include <stdio.h>

int potencia(int base, int exponente)
{
int temporal = 1; /* Valor que voy hallando */
int i; /* Para bucles */

for(i=1; i<=exponente; i++)
temporal *= base;

return temporal;
}

int main()
{
int num1, num2;

printf("Introduzca la base: ");
scanf("%d", &num1);
printf("Introduzca el exponente: ");
scanf("%d", &num2);
printf("%d elevado a %d vale %d", num1, num2, potencia(num1,num2));
return 0;
}

/*-------------------------*/

Hasta ahora no habíamos puesto eso de "int main()" porque en cuanto el compilador de C ve eso de (), sabe que se trata de una función, y si no le indicamos cómo es el valor que va a devolver, considera que se trata de un número entero. Por eso, cuando creamos la función "potencia", también podríamos haber escrito:
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Mar Oct 28, 2008 1:01 pm

CURSO DE C. TEMA 9.4 (de 6).

Vamos a ver ahora cómo modificar el valor de un parámetro de una función. Vamos a empezar por un ejemplo típico:

/*-------------------------*/
/* Ejemplo en C nº 37: */
/* EJ37.C */
/* */
/* Función "modif1" */
/* Parámetros por valor. */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/*-------------------------*/

#include <stdio.h>

void modif1(int v)
{
v ++;
printf("Ahora vale: %d\n", v);
}

main()
{
int valor = 2;
printf("Ahora vale: %d\n", valor);
modif1(valor);
printf("Ahora vale: %d\n", valor);
}

/*-------------------------*/


Sigamos cada paso que vamos dando

· Incluimos "stdio.h" para poder usar "printf".

· La función "modif1" no devuelve ningún valor. Se le pasa una variable de tipo entero, aumenta su valor en uno y lo escribe.
· Empieza el cuerpo del programa: valor = 2.
· Lo escribimos. Claramente, será 2.
· Llamamos al procedimiento "modif1", que asigna el valor=3 y lo escribe.
· Finalmente volvemos a escribir valor... 3?

¡Pues no! Escribe un 2. Las modificaciones que hagamos a "dato" dentro de la función "modif1" sólo son válidas mientras estemos dentro de esa función. Esto es debido a que realmente no modificamos "dato", sino una copia que se hace de su valor.
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Mar Oct 28, 2008 1:01 pm

CURSO DE C. TEMA 9.5 (de 6).

Eso es pasar un parámetro por valor. Pero, cómo lo hacemos si realmente queremos modificar el parámetro? Eso sería pasar un parámetro por referencia, y en C se hace pasando a la función la dirección de memoria en la que está el valor. Va un ejemplo, que comentaré después:

/*-------------------------*/
/* Ejemplo en C nº 38: */
/* EJ38.C */
/* */
/* Función "modif2" */
/* Parámetros por ref. */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/*-------------------------*/

#include <stdio.h>

void modif2(int *v)
{
(*v) ++;
printf("Ahora vale: %d\n", *v);
}


main()
{
int valor = 2;
printf("Ahora vale: %d\n", valor);

modif2( &valor );
printf("Ahora vale: %d\n", valor);
}

/*-------------------------*/

Este programa resulta bastante menos legible, pero al menos funciona, que es de lo que se trata. Vamos a ir comentando las cosas raras que aparecen:

· El parámetro lo pasamos como "int *v". Eso quiere decir que v es un "puntero a entero", o, en un castellano más normal, "una dirección de memoria en la que habrá un dato que es un número entero" (veremos los punteros con más detalle en el próximo tema). De ahí viene eso de "paso por referencia": no le decimos a la función el valor de la variable, sino que le damos referencias sobre ella, le decimos dónde se encuentra.

· Para modificarlo no hacemos "v++" sino "(*v) ++". Recordemos que ahora v es una dirección de memoria, que no es lo que queremos incrementar. Queremos aumentar el valor que hay en esa dirección. A ese valor se accede como "*v". Por tanto, lo aumentaremos con (*v)++
· A la hora de llamar a la función también tenemos que llevar cuidado, porque ésta espera que le digamos una dirección de memoria de la que leerá el dato. Eso es lo que hace el operador "&", de modo que "&valor" se leería como "la dirección de memoria en la que se encuentra la variable valor".

Pues con este jaleo de pasar una dirección de memoria y modificar el contenido de esa dirección de memoria sí que podemos variar el valor de un parámetro desde dentro de una función.

¿Y para qué vamos a querer hacer eso? Pues el ejemplo más claro es cuando queremos que una función nos devuelva más de un valor. Otro uso de los pasos por referencia es cuando tratamos de optimizar velocidad al máximo, dado que cuando pasamos un dato por valor, se crea una copia suya (ver los comentarios al ejemplo 37), cosa que no ocurre al pasar ese parámetro por referencia.
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Mar Oct 28, 2008 1:02 pm

CURSO DE C. TEMA 9.6 (de 6).

En el próximo tema volveremos a los punteros con más detalle. Ahora vamos a comentar qué es la recursividad, para dar este tema por finalizado:

La idea es simplemente que una función recursiva es aquella que se llama a sí misma. El ejemplo clásico es el "factorial de un número":

Partimos de la definición de factorial:
n! = n (n-1) (n-2) ... 3 2 1

Por otra parte,
(n-1)! = (n-1) (n-2) (n-3) ... 3 2 1

Luego podemos escribir cada factorial en función del factorial del siguiente número:
n! = n (n-1)!

Pues esta es la definición recursiva del factorial, ni más ni menos. Esto, programando, se haría:

/*-------------------------*/
/* Ejemplo en C nº 39: */

/* EJ39.C */
/* */
/* "factorial" recursivo */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/*-------------------------*/

#include <stdio.h>

long fact(int n)
{
if (n==1) /* Aseguramos que termine */
return 1;
return n * fact (n-1); /* Si no es 1, sigue la recursión */
}

main()
{
int num;
printf("Introduzca un número entero: ");
scanf("%d", &num);
printf("Su factorial es: %ld\n", fact(num));
}

/*-------------------------*/

Las dos consideraciones de siempre:
· Atención a la primera parte de la función recursiva: es MUY IMPORTANTE comprobar que hay salida de la función, para que no se quede dando vueltas todo el tiempo y nos cuelgue el ordenador.

· Los factoriales crecen rápidamente, así que no conviene poner números grandes: el factorial de 16 es 2004189184, luego a partir de 17 empezaremos a obtener resultados erróneos, a pesar de haber usado enteros largos.
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Jue Oct 30, 2008 7:33 am

CURSO DE C. TEMA 10. Variables dinámicas.

(Nota antes de empezar: este es un tema denso, y que a mucha gente le asusta al principio. Puede ser necesario leerlo varias veces y experimentar bastante para poder sacarle todo el jugo).

Hasta ahora teníamos una serie de variables que declaramos al principio del programa o de cada función. Estas variables, que reciben el nombre de ESTATICAS, tienen un tamaño asignado desde el momento en que se crea el programa.

Este tipo de variables son sencillas de usar y rápidas... si sólo vamos a manejar estructuras de datos que no cambien, pero resultan poco eficientes si tenemos estructuras cuyo tamaño no sea siempre el mismo.

Es el caso de una agenda: tenemos una serie de fichas, e iremos añadiendo más. Si reservamos espacio para 10, no podremos llegar a añadir la número 11, estamos limitando el máximo. Una solución sería la de trabajar siempre en el disco: no tenemos límite en cuanto a número de fichas, pero es muchísimo más lento.

Lo ideal sería aprovechar mejor la memoria que tenemos en el ordenador, para guardar en ella todas las fichas o al menos todas aquellas que quepan en memoria.

Una solución "típica" (pero mala) es sobredimensionar: preparar una agenda contando con 1000 fichas, aunque supongamos que no vamos a pasar de 200. Esto tiene varios inconvenientes: se desperdicia memoria, obliga a conocer bien los datos con los que vamos a trabajar, sigue pudiendo verse sobrepasado, etc.

La solución suele ser crear estructuras DINAMICAS, que puedan ir creciendo o disminuyendo según nos interesen. Ejemplos de este tipo de estructuras son:
· Las pilas. Justo como una pila de libros: vamos apilando cosas en la cima, o cogiendo de la cima.

· Las colas. Como las del cine, por ejemplo (en teoría): la gente llega por un sitio (la cola) y sale por el opuesto (la cabeza).
· Las listas, en las que se puede añadir elementos, consultarlos o borrarlos en cualquier posición.

Y la cosa se va complicando: en los árboles cada elemento puede tener varios sucesores, etc.

Todas estas estructuras tienen en común que, si se programan bien, pueden ir creciendo o decreciendo según haga falta, al contrario que un array, que tiene su tamaño prefijado.

En todas ellas, lo que vamos haciendo es reservar un poco de memoria para cada nuevo elemento que nos haga falta, y enlazarlo a los que ya teníamos. Cuando queramos borrar un elemento, enlazamos el anterior a él con el posterior a él (si hace falta, para que no "se rompa") y liberamos la memoria que estaba ocupando.

Así que para seguir, necesitamos saber cómo reservar memoria y cómo liberarla. Antes, vamos a ver algo que ya se comentó de pasada en el tema anterior: eso de los punteros.

Un puntero no es más que una dirección de memoria. Lo que tiene de especial es que normalmente un puntero tendrá un tipo asociado: por ejemplo, un "puntero a entero" será una dirección de memoria en la que habrá almacenado (o podremos almacenar) un número entero.

Ahora vamos a ver qué símbolos usamos en C para designar los punteros:

int num; "num" es un número entero
int *pos; "pos" es un "puntero a entero" (dirección de
memoria en la que podremos guardar un entero)

num = 1; ahora "num" vale 1
pos = 1000; "pos" ahora es la dirección 1000 (peligroso)
*pos = 25; en la posición "pos" guardamos un 25
pos = &num; "pos" ahora es la dirección de "num"

Por tanto, con el símbolo * indicamos que se trata de un puntero, y & nos devuelve la dirección de memoria en la que se encuentra una variable.

Lo de "pos=1000" es peligroso, porque no sabemos qué hay en esa dirección, de modo que si escribimos allí podemos provocar una catástrofe. Por ejemplo, si ponemos un valor al azar que coincide con la instrucción en código máquina de formatear el disco duro, no nos hará nada de gracia cuando nuestro programa llegue hasta esa instrucción. Normalmente las consecuencias no son tan graves, pero hay que llevar cuidado. La forma de trabajar será pedir al compilador que nos reserve un poco de memoria donde él crea adecuado.

Para eso usamos la orden "malloc" (necesitaremos incluir el fichero de cabecera <stdlib.h> o <malloc.h> -es preferible usar el primero, que es estándar-). Una vez que hemos terminado de usar esa memoria, suele ser conveniente liberarla
, y para eso empleamos "free". Vamos a ver un ejemplo:

/*-------------------------*/
/* Ejemplo en C nº 40: */
/* EJ40.C */
/* */
/* Introducción a los */
/* punteros. */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/*-------------------------*/

#include <stdio.h>
#include <stdlib.h>

int num; /* Un número entero */
int *pos; /* Un "puntero a entero" */

main()
{
printf("Num vale: %d (arbitrario)\n", num);

printf("La dirección Pos es: %p (arbitrario)\n", pos);

/* La siguiente línea está deshabilitada porque es "peligrosa"
(en GCC dará error) */
/* printf("El contenido de Pos es: %d (arbitrario)\n", *pos); */

num = 1; /* ahora "num" vale 1 */
printf("Num vale: %d (fijado)\n", num);

/* Reservamos espacio */
pos = (int *) malloc (sizeof(int));

printf("La dirección Pos es: %p (asignado)\n", pos);
printf("El contenido de Pos es: %d (arbitrario)\n", *pos);

*pos = 25; /* En la posición "pos" guardamos un 25 */
printf("El contenido de Pos es: %d (fijado)\n", *pos);

free(pos); /* Liberamos lo reservado */

pos = &num; /* "pos" ahora es la dirección de "num" */

printf("Y ahora el contenido de Pos es: %d (valor de Num)\n", *pos);
}

/*-------------------------*/

El formato de "free" no tiene ninguna pega: "free(pos)" quiere decir "libera la memoria que ocupaba pos". El de "malloc" es más rebuscado:

malloc (tamaño)

Como queremos reservar espacio para un entero, ese "tamaño" será lo que ocupe un entero, y eso nos lo dice "sizeof(int)".

¿Y eso de (int*)? Es porque "malloc" nos devuelve un puntero sin tipo (un puntero a void: void*). Cómo queremos guardar un dato entero, primero debemos hacer una conversión de tipos (typecast), de "puntero sin tipo" a "puntero a entero" (int *).

De principio puede asustar un poco, pero se le pierde el miedo en cuanto se usa un par de veces. Vamos a analizar ahora la salida de este programa.

Con Turbo C++ 1.01 obtenemos:

Num vale: 0 (arbitrario)
La dirección Pos es: 0000:0000 (arbitrario)
El contenido de Pos es: 294 (arbitrario)
Num vale: 1 (fijado)
La dirección Pos es: 0000:0004 (asignado)
El contenido de Pos es: 1780 (arbitrario)
El contenido de Pos es: 25 (fijado)
Y ahora el contenido de Pos es: 1 (valor de Num)

Y con Symantec C++ 6.0

Num vale: 0 (arbitrario)
La dirección Pos es: 0000 (arbitrario)
El contenido de Pos es: 21061 (arbitrario)
Num vale: 1 (fijado)
La dirección Pos es: 2672 (asignado)
El contenido de Pos es: -9856 (arbitrario)
El contenido de Pos es: 25 (fijado)
Y ahora el contenido de Pos es: 1 (valor de Num)

En ambos casos vemos que

· Inicialmente "num" vale 0, pero como no es algo que hayamos obligado nosotros, no podemos fiarnos de que siempre vaya a ser así.

· El puntero POS es 0 al principio (no apunta todavía a ninguna dirección). Esto es un "puntero nulo", para el que todavía no se ha asignado un espacio en memoria. De hecho, para lograr una mayor legibilidad, está definida (en "stdio.h" y en otras cabeceras) una constante llamada NULL y de valor 0, de modo que podemos hacer comparaciones como

if (pos == NULL) ...


Recordemos que if (pos != NULL) es igual que if (pos) ... (ya visto en el tema de condiciones).


· Como esta dirección ("pos") no está reservada aún, su valor puede ser cualquier cosa. Y de hecho, puede seguirlo siendo después de reservarla: el compilador nos dice dónde tenemos memoria disponible, pero no "la vacía" para nosotros.

· Una vez que hemos reservado memoria y hemos asignado el valor, ya sabemos con certeza que ese número 25 se guardará donde debe.

· Finalmente, si queremos asignar a "pos" el valor de la dirección en la que se encuentra "num", como hemos hecho en la penúltima línea, no hace falta reservar memoria con "malloc" (de hecho, lo he usado a propósito después de liberar la memoria con "free"). Esto es debido a que "num" ya tenía reservado su espacio de memoria, al que nosotros ahora podemos acceder de dos formas: sabiendo que corresponde a la variable "num", o bien teniendo en cuenta que "pos" es la dirección en la que está ese dato.

Finalmente, con GCC la siguiente línea da problemas:

printf("El contenido de Pos es: %d (arbitrario)\n", *pos);

Estamos intentando acceder a un espacio de memoria que no hemos reservado, lo cual no es posible bajo Linux. Esto hace que en el momento de ejecutar el programa, aparezca un error de "Segmentation Fault". En cambio, los dos compiladores que trabajan bajo DOS aceptan esta orden. Aun así, insisto en que no se debe modificar el contenido de zonas de memoria que no hemos reservado.
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Jue Oct 30, 2008 7:34 am

CURSO DE C. TEMA 10.2 (de 4). Aritmética de punteros.

Aquí hay que hacer una observación: como un puntero tiene un valor numérico (la dirección en la que se nos ha reservado memoria), podemos aumentar este número haciendo cosas como

pos ++; o bien pos += 3;

Pero también hay que recordar que normalmente un puntero va a estar asociado a un cierto tipo de datos. Por ejemplo, "int *" indica un puntero a entero.

¿A qué viene esto? Es sencillo: si un entero ocupa 2 bytes, al hacer "pos++" no deberíamos avanzar de 1 en 1, sino de 2 en 2, para saltar al siguiente dato.

Vamos a ver un ejemplo que cree un array de enteros dinámicamente, y que lo recorra usando los índices del array (como habríamos hecho hasta ahora) y también usando punteros:

/*-------------------------*/
/* Ejemplo en C nº 41: */

/* EJ41.C */
/* */
/* Punteros y arrays */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/*-------------------------*/

#include <stdio.h>
#include <stdlib.h>

int *num; /* Puntero a número(s) entero(s) */
int *temporal; /* Temporal, para recorrer el array */
int i; /* Para bucles */

main()
{
/* Reservamos espacio para 10 números (array dinámico) */
num = (int *) malloc (10 * sizeof(int));

for (i=0; i<10; i++) /* Recorremos el array */
num[i] = i*2; /* Dando valores */

printf("La dirección de comienzo del array es: %p\n", num);

printf("Valores del array: ");

for (i=0; i<10; i++) /* Recorremos el array */
printf("%d ",num[i]); /* Mostrando los valores */


printf("\nValores del array (como puntero): ");
temporal=num;
for (i=0; i<10; i++) /* Recorremos como puntero */
printf("%d ",*temporal++); /* Mostrando los valores y aumentando */

free(num); /* Liberamos lo reservado */

}

/*-------------------------*/

Como se ve, en C hay muy poca diferencia entre arrays y punteros: hemos declarado "num" como un puntero, pero hemos reservado espacio para más de un dato, y hemos podido recorrerlo como si hubiésemos definido

int num[10];

Esto es lo que da lugar a la gran diferencia que existe en el manejo de "strings" (cadenas de texto) en lenguajes como Pascal frente al C, por lo que he preferido dedicarles un tema entero (se verá más adelante).

Se puede profundizar mucho más en los punteros, especialmente tratando estructuras dinámicas como las pilas y colas, las listas simples y dobles, los árboles, etc., pero considero que cae fuera del propósito de este curso, que es básicamente introductorio (sí se tratan brevemente en la versión ampliada).
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Dom Nov 02, 2008 1:41 am

CURSO DE C. TEMA 11. Manejo de ficheros.

Vamos a comenzar por ver cómo leer un fichero de texto. Primero voy a poner un ejemplo que lea y muestre el AUTOEXEC.BAT, y después lo iré comentando:

/*-------------------------*/
/* Ejemplo en C nº 42: */
/* EJ42.C */
/* */
/* Ficheros de texto (1) */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/*-------------------------*/

#include <stdio.h>

FILE* fichero;
char texto[80];

main()
{
fichero = fopen("c:\\autoexec.bat", "rt");
/* En Linux: fichero = fopen("/proc/cpuinfo", "rt"); */
if (fichero == NULL)
{
printf("No existe el fichero!\n");
exit(1);
}
while (! feof(fichero)) {
fgets(texto, 80, fichero);
printf("%s", texto);

}
fclose(fichero);
}

/*-------------------------*/

Aquí van los comentarios sobre este programa:

· FILE es el tipo asociado a un fichero. Realmente se trata de un "puntero a fichero", por eso aparece el asterisco * a su derecha.

· Para abrir el fichero usamos "fopen", que lleva dos parámetros: el nombre del fichero y el modo de lectura. En el nombre del fichero, la barra \ aparece repetida a propósito, porque (como vimos al hablar de "printf") es un código de control, de modo que \a sería la señal de alerta (un pitido), que no es lo que queremos leer. Por eso, ponemos \\, que se traduce como una sola barra. Lo de "rt" indica que el modo será de lectura (r) en un fichero de texto (t). Si usamos GCC bajo Linux, no existe "c:\autoexec.bat", así que podemos leer "/proc/cpuinfo", o "/proc/devices", o ".profile", por ejemplo.
· Como "fichero" es un puntero (a fichero), para mirar si ha habido algún problema, comprobamos si ese puntero sigue siendo nulo después de intentar acceder al fichero.

· Después repetimos una parte del programa hasta que se acabe el fichero, de lo que nos informa "feof" (EOF es la abreviatura de End Of File, fin de fichero).
· Con "fgets" leemos una cadena de texto, que podemos limitar en longitud (en este caso, a 80 caracteres), desde el fichero. Esta cadena de texto conservará los caracteres de avance de línea.
· Finalmente, cerramos el fichero con "fclose".
Volver arriba Ir abajo
Ver perfil de usuario
duirk
Co-Administrador
Co-Administrador


Mensajes : 88
Fecha de inscripción : 28/09/2008

MensajeTema: Re: Curso programación en C   Dom Nov 02, 2008 1:42 am

CURSO DE C. TEMA 11.2 (de 4)

Si queremos crear un fichero, los pasos son muy parecidos, sólo que lo abriremos para escritura (w), y escribiremos con "fputs":

/*-------------------------*/
/* Ejemplo en C nº 43: */
/* EJ43.C */
/* */
/* Ficheros de texto (2) */
/* */
/* Comprobado con: */
/* - Turbo C++ 1.01 */
/* - Djgpp 2.01 */
/* - Symantec C++ 6.0 */
/* - GCC 2.6.3 */
/* - PCC 2.1c */
/*-------------------------*/

#include <stdio.h>

FILE* fichero;
char texto[80];

main()
{
fichero = fopen("basura.dat", "wt");
if (fichero == NULL)
{
printf("No se ha podido crear el fichero!\n");
exit(1);
}
fputs("Esto es una línea\n", fichero);
fputs("Esto es otra", fichero);
fputs(" y esto es continuación de la anterior\n", fichero);
fclose(fichero);

}
Volver arriba Ir abajo
Ver perfil de usuario
Contenido patrocinado




MensajeTema: Re: Curso programación en C   Hoy a las 3:01 pm

Volver arriba Ir abajo
 
Curso programación en C
Ver el tema anterior Ver el tema siguiente Volver arriba 
Página 2 de 3.Ir a la página : Precedente  1, 2, 3  Siguiente
 Temas similares
-
» Como poner iconos de resyelto, en curso, etc.
» Resuelto, En curso, etc.
» imagen que siga el curso y que diga registrarse
» Manual Android
» Abuso en curso porque no quieren recibir correos... ¿como lo elimino?

Permisos de este foro:No puedes responder a temas en este foro.
-^Blue Hacking^- :: Hacktivismo :: Programación En General-
Cambiar a: