No lo copiéis sin autorización explícita mía, en todo caso, linkead a:
http://shell.pchack.n et/
(sin espacios, Shallow soy yo)
¿Porqué escribo este tutorial? Simplemente, en respuesta al thread "Peticiones para la Scene"
Espero entendáis que me llevará un tiempo completar este tutorial, y iré poco a poco completándolo.
1. Introducción
C, como cualquier otro lenguaje de programación no requiere más que tiempo, una cabeza y mucha paciencia. No pretendáis que leyendo este tutorial aprendáis C a nivel experto en un día, y empecéis a programar homebrew como quien no quiere la cosa.
Empezar a programar en C puede ser algo muy bueno o muy malo. C es un lenguaje que, si no te lo tomas con calma y te lo relees todo hasta entenderlo, no puedes continuar avanzado, este tutorial está pensado para ir progresando poco a poco. En cambio, una vez sepas o domines C, cualquier otro lenguaje de programación te parecerá igual, sino muy fácil. Es decir, empezar con este tutorial sin ánimos, a desganas, o con la creencia de que no aprenderéis nada, será contraproducente.
No me mal interpretéis, pues yo soy el primero que quiero que aprendáis, pero también soy el primero que no quiero ver gente desanimándose por el camino.
Nota: para programar en la SDK de sony o el psl1ght, hacen falta unos buenos conocimientos de C, asi que, ¡toca empaparse de C primero!
Hasta el capítulo 5 no veremos un primer programa en ejecución ni haremos ninguno, pues primero es necesario entender un par de conceptos.
Si queréis, para entrar en calor y ver que pretendo que sepáis hacer, como mínimo, cuando acabe este tutorial, os dejo un video mio también, donde explicaba como crear una DLL para hackear juegos (de ordenador). Aun que el lenguaje en que se explica en el video es C++, se ven un par de conceptos interesantes.
Si no sabéis programar nada, el vídeo os aburrirá, así que, pasad al tutorial xd
[C++] [VideoTutorial] Hack DLL [Nivel: Medio]
Mas adelante, cuando creemos nuestro primer programa, utilizaré este vídeo como referencia, pero sois libres de usar el programa que queráis para programar.
2. Variables y tipos de variables
Primero de todo, ¿que es una variable? Una aproximación teórica podría ser, por ejemplo y para que sea fácil de entender, un "contenedor" donde se puede guardar un valor. Asi por ejemplo,
- Código: Seleccionar todo
int variable1;
Seria una variable.
- Código: Seleccionar todo
variable1 = 10
variable1 = 5
Aquí vemos como en el primero caso, la variable "variable1" le guardamos un valor de 10, y luego le guardamos un 5.
Esto es básicamente una variable, algo que nos permite guardan en ella valores.
Para dar un nombre a una variable, en el anterior ejemplo "variable1" se deben seguir unas normas muy simples.
- Una variable debe empezar con una letra, es decir, una variable como "1variable" no es válida y provocaría un error.
- Una variable no puede contener caracteres especiales (como "#¢∞¬" etc.), a excepción de "_" pues "variable_1" si sería correcto
- Una variable no puede contener espacios, pues "variable 1" daría error.
- Hay nombres reservados que no pueden ser usados para nombrar variables (por ejemplo: int, char, public, private, class, void, y algunos otro que ya iremos viendo).
- El lenguaje C distingue entre mayúsculas y minúsculas, no siendo lo mismo, pues "VARIABLE1" que "variable1" ni "VaRiAbLe1"
- No podemos tener 2 variables con el mismo nombre
Con esto ya podemos dar nombre a las variables, pero, en C existen tipos de variable, es decir, si queremos que una variable contenga un número no la declararemos (declarar: acto de escribir o programar una variable) del mismo modo que haríamos con una que fuera a contener, por ejemplo, letras. Hagamos una breve referencia a los diferentes tipos de variable, y luego pondremos ejemplos y explicaciones más detallas de su funcionamiento. Normalmente nos referimos a los tipos de variable por su nombre ingles, "Data Type".
Veremos a continuación los nombres "signed" y "unsigned", luego explicaremos que significa cada uno
char -> caracteres o números pequeños enterossigned: -128 to 127
unsigned: 0 to 255
short int (short) -> variable numéricas enterossigned: -32768 to 32767
unsigned: 0 to 65535
int -> variables numéricassigned: -2147483648 to 2147483647
unsigned: 0 to 4294967295
long int (long) -> variables numéricas enterossigned: -2147483648 to 2147483647
unsigned: 0 to 4294967295
Notese que en máquinas UNIX a 64 bits, esto equivale a long long, cuyo rango es (gracias waninkoko):
-9,223,372,036,854,775,808 hasta 9,223,372,036,854,775,807
float, double, long double -> variables numéricas con decimales (de punto flotante)
Como las int se diferencia en el tamaño que pueden contener.
Algunos de vosotros os preguntaréis que significan los valores que hay debajo de los diferentes tipos de variable. Bien, eso es el tamaño máximo del número que pueden contener. Pongamos un ejemplo práctico
int variable1 = 1; //CORRECTO
int variable2 = 2147483649; //INCORRECTO!
long variable3 = 2147483649; //CORRECTO
Expliquemos algunas de las características de los siguientes ejemplos. Primero de todo y el más importante, ¿veis ese punto y coma (;)? En C, asi como en otros lenguajes como C++ o PHP, toda instrucción (instrucción: "pedazo" o función de código, aquí por ejemplo la instrucción es declarar la variable), antes de la siguiente instrucción debemos poner un ";", para indicar el fin de la anterior instrucción, de lo contrario nos daría error.
Luego, supongo que habéis visto esos "//", eso significa un comentario. Los comentarios son simples textos que son ignorados por el compilador (compilador: programa que "convierte" el código en un archivo "ejecutable".). Los comentarios pueden contener todo lo que quieras y se pueden poner donde quieras, pero existe una única limitación, todo lo que vaya a continuación de los // formará parte del comentario hasta fin de linea. Para evitar esta limitación existe otro tipo de comentario
- Código: Seleccionar todo
/*
TODO ESTO QUE HAY AQUI
es un comentario, incluido si
saltamos de linea
*/
Es el que sigue se encuentra entre /* aquí */. Nótese que mediante este tipo de comentarios podemos hasta hacer lo siguiente.
- Código: Seleccionar todo
int /*esto es una variable numérica*/ variable1;
- Código: Seleccionar todo
int variable1;
NOTA: Los comentarios con // son propios de C++ mientras que los de /* */ son propios de C. Aunque hoy en día un buen compilador te aceptará ambos sin ningún problema.
Pero, volvamos al ejemplo que hemos hecho hace un momento. ¿Porqué el segundo ejemplo pone INCORRECTO? Mirad otra vez el cuadro de los Data Type, ¿veis el limite de las variables int? ¡Lo estamos superando!
La solución es declararlo como "long".
Ese ejemplo no tiene mucho más a decir, asi que, proseguimos a explicar que es eso de "signed" y "unsigned" que ponía allí. Para aquellos que no lo hagáis deducido ya, la diferencia es muy simple, las variables "unsigned" no pueden contener valores negativos, y los "signed" pueden contener valores negativos y positivos.
Por defecto, si no se pone de que tipo es, se declaran como signed. Veamos ahora un ejemplo de como poner el signed y el unsigned.
- Código: Seleccionar todo
signed int variable1;
unsigned int variable2;
NOTA: Para aquellos que hagáis programado ya anteriormente, en C no existe el tipo de variable "bool". En posteriores capítulos veremos soluciones más o menos complicadas a este "problema"
Para declarar una variable y darle un valor lo podemos hacer asi:
int variable1 = 2; //donde lo guardamos directamente
//o asi, que es lo mismo
int variable2;
variable2 = 2;
Notese, que el la segunda parte, como ya hemos declarado la variable en la primera línea, en la segunda ya no volvemos a poner de que tipo es (en este caso int)
Explicaremos ahora con más detalle los otros tipos de variables, puesto que los int no tienen demasiado secreto.
2.1 - Tipo char
Bueno, para hacerlo fácil, primero de todo tenemos que entender que cada letra del abecedario tiene un equivalente numérico. Podemos ver una lista de referencia en esta web: http://www.asciitable.com/
Pongamos un ejemplo usando la letra 'a'.
char variable1 = 'a';
char variable2 = 97;
Si miramos la tabla anterior, veremos que a == 97, por lo que variable1 == variable2;
(En posteriores capítulos explicaremos porque aquí ponemos ==, y no =, puesto que no es un error).
Daros cuenta que al asignar el valor a variable1, hemos usado comillas simples (') para delimitar la letra 'a'. Por lo general en C, como en cualquier lenguaje de programación, las cadenas de letras se delimitan por comillas dobles ("), pero el caso de char es especial, ya que lo que queremos indicar es el valor númerico de la letra, no la letra en sí misma. Por lo demás, las variables char se comportan de la misma forma que una variable númerica como "int".
2.2 - Tipo float
Esta variable tiene un comportamiento muy similar, sino igual, a las variables int. La única diferencia existente con estas, es que una variable float admite decimales, cosa que no hace una int.
Veamos un ejemplo que lo demuestre:
- Código: Seleccionar todo
int variable_1 = 1.5;
int variable_2 = 1.6;
float variable_3 = 1.653;
Primero de todo y lo más importante, ya que puede conducir a errores tontos. En C los decimales siguen a un punto (.), puesto que se usa el sistema inglés.
Veamos la variable_1, hemos dicho que no podemos ponerle decimales, ¿verdad?, entonces, os preguntaréis, ¿que pasa aquí? La respuesta es simple, el compilador va a coger ese numero y, simplemente, quitarle los decimales. Es decir, tanto variable_1 como variable_2 se convertirán en, 1.
En cambio, variable_3, al aceptar decimales por ser del tipo float, permenecerá intacta.
Aun que no lo haya puesto en la tabla de arriba, las variables float también tienen un limite máximo. Para más información, buscad en google Data Type, y obtendréis la respuesta.
3. Operaciones con variables numéricas
Primero de todo introduciremos un concepto que recibe el nombre de "Type Casting". ¿Suena a chino no?, pero en realidad, es algo muy común y necesario. (NOTA: los ejemplos siguientes, para gente que ya ha programado, me dirá que es innecesario hacerlo, pero, van bien para explicar). Imaginaros que tenemos una variable float, con sus respectivos decimales, y decidimos que queremos pasarla a int. ¿Y como hago eso? En un principio, si dos variables son de tipo diferente, no podemos igualarlas por las buenas. Pongamos un ejemplo:
- Código: Seleccionar todo
float variable1 = 16.5;
int variable2;
variable2 = variable1;
(Repito, gente que ya haya programado en C o C++, absteneros de comentarios no constructivos aquí).
Bien, esto en un principio no seria posible, ya que las variables son de tipo diferente, y por lo tanto, no podemos hacer esto, que consiste en darle a la variable1 el valor que tiene la variable2, es decir, seria lo mismo que escribir variable2 = 16.5, puesto que variable1 hemos dicho que valia 16.5
Pero, existe un método mediante el cuál si es posible hacerlo:
float variable1 = 16.5
int variable2;
variable2 = (int)variable1;
Nótese ese (int), importante que esté entre paréntesis. Eso significa que queremos que primero convierta el 16.5 ha int, es decir, quedaria 16, y luego, ese 16 lo guarda en variable2. Algunos compiladores modernos, quizás se quejen de que esto puede provocar perdidas de datos, pero si lo pensamos bien, es normal, puesto que perdemos 5 decimales (.5) por el camino.
Quizás ahora esto suene raro, pero a base de practicar, es muy fácil y intuitivo.
Cabe decir que la variable sigue siendo de su tipo original, el type casting solo se aplica allí donde se pone, por lo demás, la variable se comporta igual.
Prosigamos pues, a explicar como hacemos operaciones con variables numéricas. En este caso, pondré ejemplos, y explicaré solo en detalle aquellos que me parezcan un poco más complicados de explicar. Por ahora solo pondré operaciones sencillas, echas con números de base 10 (los de toda la vida), y no tocaré nada del tema bits, eso irá más adelante.
//EJEMPLO 1
int variable1 = 10;
int variable2 = 2;
int resultado;
resultado = variable1 + variable2; //resultado = 10 + 2 = 12
resultado = variable1 - variable2; //resultado = 10 - 2 = 8
resultado = variable1 * variable2; //resultado = 10 * 2 = 20
resultado = variable1 / variable2; //resultado = 10 / 2 = 5
//Para poder hacer el siguiente ejemplo, hace falta incluir un archivo. En el capítulo 5 veremos como se incluye
//#include <math.h>
resultado = pow(variable1, variable2); //resultado = 10 ^ 2 = 100, potencia de 10 a la 2
//EJEMPLO 2
int variable1 = 10;
int variable2 = 2;
int resultado = variable1 * variable2; //Lo mismo que antes, pero directamente
//EJEMPLO 3
int variable1 = 10;
int variable2 = 5;
int variable3 = 2;
int resultado1 = variable1-variable2*variable3; //resultado1 = 0 = 10 - 5 * 2
/* En C, como en cualquier lenguaje de programación, a no ser que nosotros pongamos paréntesis, se respecta el orden de operaciones según el signo */
int resultado2 = (variable1-variable2)*variable3; //resultado = (10-5)*2 = 5*2 = 10
//EJEMPLO 4 - UTILIZANDO DIFERENTES TIPOS DE VARIABLE
int variable1 = 2.5; //Sabemos que esto acabará siendo un simple 2
int variable2 = 2.5; //Sabemos que esto acabará siendo un simple 2
int resultado = variable1 * variable2; //resultado = 2 * 2 = 4;
//Probemos ahora con una simple modificación
float f_variable1 = 2.5;
float f_variable2 = 2.5
//Sabemos que en ambas se guarda el resultado
int resultado = (int)(f_variable1 * f_variable2); //resultado = (int)(2.5 * 2.5) = (int)6.25 = 6;
/*A diferencia del caso anterior, con float se ha guardado el resultado, y aunque lo convertimos a int, tal como vemos eso es el último paso, y en ese último paso, ya tenemos el 6, simplemente, le quitamos los decimales.
Vemos pues, como puede variar mucho entre un caso y entro, ¡son 2 enteros de diferencia!*/
Más cositas, quizás ahora no tiene mucho sentido, pero lo tendrá en el capitulo 10:
variable < numero (menor que)
variable > numero (mayor que)
variable <= numero (menor o igual que)
variable >= numero (mayor o igual que)
Ejemplos
- Código: Seleccionar todo
int var = 8;
//var < 9 (puesto que 8 menor que 9)
//var > 1 (puesto que 8 mayor que 1)
//var <= 10 (puesto que 8 es menor ([b]o[/b] igual) que 10
//var <= 8 (puesto que 8 es menor ([b]o[/b] igual) que 8
//var >= 2 (puesto que 8 es mayor ([b]o[/b] igual) que 2
//var >= 8 (puesto que 8 es mayor ([b]o[/b] igual) que 8
4. Array
No se si lo habéis pensado ya, pero, con los tipos de variables anteriores, ¿como podríamos, por ejemplo, poner una sola cadena de texto, como "hola mundo" en una sola variable? Bien, ahora veremos la respuesta.
Imaginaros por un momento una cajonera. En sí misma es una sola cosa, pero en "detalle" puede tener 2, 3, 4... cajones. Esto es una array.
Vamos a hacerlo prácticamente. Recordáis la variable tipo char? Pues la vamos a utilizar para ilustrar el ejemplo. (Esto que viene a continuación es solo para que se entienda, no es necesario para formar una array). Si quisiéramos poner el "hola mundo" en variables tipo char, haríamos esto:
char letra1 = 'h';
char letra2 = 'o';
char letra3 = 'l';
char letra4 = 'a';
char letra5 = ' ';
etc.
Pero como comprenderéis, el día que tengáis que hacer lo mismo con un texto de 1000 letras, os vais a morir en el intento. Ahora imaginaros que estas variables, eran los cajones de la dicha cajonera. ¿No podríamos unirlos, y hacer de ello una sola variable? Sí:
char texto[] = "Hola Mundo";
Fijaros, ¿que ha cambiado aquí? En primer lugar, el texto está delimitado por comillas dobles, muy importante. En segundo lugar, detrás del nombre de la variable, texto, encontramos unos corchetes []. En realidad, allí en medio habría un número. Ese número equivaldría al número de letras más 1, contando desde 0. Pero C, y C++, tienen la ventaja, de que si inicializamos la variable (le guardamos un valor) al mismo tiempo que la declaramos, podemos omitir ese número.
Fijaros en la afirmación anterior: "[...]contando desde 0[...]". Las arrays no empiezan en el número 1 como sería de pensar, sino que empiezan por el 0. Si descomponemos la anterior array, tendríamos esto:
- Código: Seleccionar todo
texto[0] = 'H'; //Pensad que cada uno de los "cajones" es una variable char, en conjunto, un char array.
texto[1] = 'o'; //Y así hasta llegar a:
texto[10] = 'o';
Pero, revisemos otra vez la afirmación "[...] al número de letras más 1[...]". ¿Y ese más 1? Resulta que en C, y C++, es necesario indicar que la cadena de texto se ha acabado. En el caso anterior, donde lo inicializamos al momento, no es necesario añadir nada más, pero si siguiéramos descomponiendo esa array, encontraríamos esto:
- Código: Seleccionar todo
texto[11] = '\0'; //Esto, \0, indica final de una cadena
Podríamos entonces decir que lo siguiente es lo mismo:
- Código: Seleccionar todo
char texto1[] = "Hola Mundo";
char texto1[11] = "Hola Mundo";
¿Y que pasaría si en vez de 11, pongo 21? Absolutamente nada (aparte de que ocuparás más memoria), porque recuerdas ese '\0', seguirá estando en 11, no en 21.
¿Y si pongo 10? Error como una casa xd
De la misma forma que podemos hacer arrays con variables char, las podemos hacer con cualquier otro tipo.
- Código: Seleccionar todo
int arraydeint[10];
arraydeint[0] = 1;
arraydeint[1] = 2;
etc.
¿Y no puedo poner en esta algo del tipo "111111"? No, porque recuerda que las comillas dobles (") indican cadena de texto, y esto deben ser números. ¿Y entonces, si quiero inicializarlo directamente?
- Código: Seleccionar todo
int arraydeint[] = {0,1,2,3,4,5,6};
//Lo cual es lo mismo que hacer:
//arraydeint[0] = 0;
//arraydeint[1] = 1;
//etc.
5. Nuestro primer programa - Hola Mundo
Para realizar este capítulo es necesario de disponer de alguna plataforma en que programar, o al menos, un compilador. Una fácil de usar y muy útil, porque corrige los errores de sintaxis (sin taxis quiere decir, por ejemplo, las ; al final de instrucción) usaremos el Visual C++ Express 2010. Lo podemos encontrar aquí:
http://www.microsoft.com/express/Downloads/
Buscamos Visual C++ 2010 Express, escogemos idioma, descargamos e instalamos.
Quizás os preguntaréis porqué usamos uno en cuyo nombre pone C++ y no C. Resulta que un compilador que compila C++, también va a compilar C.
Una vez instalado, le damos a (algo parecido a lo que diré, ya que yo lo tengo en inglés):
Inicio->archivo->nuevo
Le damos a "Proyecto Vacío" en la nueva ventana que saldrá y le damos un nombre
Una vez ya en el entorno de programación, es decir, la pantalla principal, le damos a:
Proyecto->Añadir nuevo elemento
Seleccionamos en la nueva ventana "Archivo C++ (.cpp)" le damos un nombre, normalmente recibe el nombre de "main" ya que es el archivo principal (main, inglés = principal) y le damos a "Añadir".
Notese que normalmente los archivos en C no acaban en .cpp, ya que dicha terminación es propia de los archivos C++, tal y como dice allí, sino que acaban en .c. Pero, para no complicarnos la vida, lo dejaremos en .cpp, que tampoco pasa nada, va a compilar igual.
Bien, todo esta listo. Si no se ha abierto ya el archivo "main.cpp" lo abrimos desde el explorador de la izquierda.
Empecemos a programar. Primero de todo, tenemos que plantearnos una pregunta, ¿como haremos para enseñar un texto en una pantalla? Por suerte, esto ya viene hecho, y todo lo que tenemos que hacer, es incluir un archivo que tiene dicho código ya programado.
Para este programa, bastará que incluyamos el archivo "stdio.h", lo cuál haremos de la siguiente forma:
- Código: Seleccionar todo
#include <stdio.h>
Bien, ahora ya tenemos lo esencial para que el programa pueda funcionar. Pero seguimos teniendo un problema, el código no lo puedes escribir por ahí libremente, debe estar dentro de una función. De momento no entraremos en detalle sobre que es una función, simplemente debemos saber que en C, siempre debe existir la función "main". Esta es la primera función que se ejecutará de nuestro programa. De no existir, tendríamos un grave error y problema. Insisto en que por ahora no explicaré como funcionan las funciones, así que, escribiremos esto (en una nueva linea):
- Código: Seleccionar todo
int main(){
//El código va aquí, entre las llaves {}.
return 0;
}
Ya que he dicho lo de una nueva línea, en C los espacios en blanco, tanto antes de una instrucción como después, como los saltos de línea, no influyen en nada, es una mera cuestión de estilo y entendimiento. Pues, es mucho mas fácil de entender:
- Código: Seleccionar todo
int main(){
//El código va aquí
return 0;
}
Que no el anterior ejemplo, ya que podemos ver la jerarquía, es decir, que ese comentario está dentro de una función.
¿Veis ese return 0; del final de todo? Eso simplemente indica que el programa ha terminado, y que se cierra bien (en este caso, más adelante veremos para que sirve realmente la palabra return).
Vale, ¿quién se acuerda de como poníamos en una variable un texto entero? Esto es lo que vamos a hacer ahora, simplemente tenemos que escribirlo dentro de la función main:
- Código: Seleccionar todo
char texto[] = "Hola Mundo";
Perfecto, ahora, como nos lo montamos para que este texto salga en la consola (cuando ejecutéis el programa, ya veréis que es una consola). Pues utilizando la siguiente función:
- Código: Seleccionar todo
printf(/*variable o texto*/);
Aplicado quedaría
- Código: Seleccionar todo
printf(texto);
Si probáramos de ejecutar ahora el programa compilado, no llegaríamos a leer el texto, por el simple hecho de que printf se limita a mostrar el texto, pero recordemos que a continuación tenemos el return 0; que para el programa.
Una bonita forma de evitar esto, es usando lo siguiente:
- Código: Seleccionar todo
getchar();
Esto permite que el usuario (tú) escribas algo en la consola, y hasta que no pulses [ENTER] no continua. En este caso, continuar equivale a terminar el programa.
Para probar el programa, en el visual c++ podemos compilar pulsando F5, y automáticamente, si no hay errores, se abrirá el programa, o con
Depurar->Empezar a depurar
El programa acabado quedaría de la siguiente forma:
- Código: Seleccionar todo
#include <stdio.h>
int main(){
char texto[] = "Hola Mundo";
printf(texto);
getchar();
return 0;
}
Otra solución alternativa al tema de pausar la consola, con getchars() es el uso de system("pause"), para ello debemos incluir un nuevo archivo justo debajo del anterior
- Código: Seleccionar todo
#include <stdlib.h>
Y substituir el getchar(); por: system("pause");
- Código: Seleccionar todo
#include <stdio.h>
#include <stdlib.h>
int main(){
char texto[] = "Hola Mundo";
printf(texto);
system("pause");
return 0;
}
Os animo a que experimentéis un poco vosotros con lo ya dado, y que hagáis pequeñas modificaciones a este "Hola Mundo".
Os quiero dar un regalito, antes de irme a la cama
- Código: Seleccionar todo
char resultado[100];
int numero = 5;
sprintf(resultado, "Esto es una cadena de texto donde le añadimos un numero: %d", numero);
//ahora resultado es: Esto es una cadena de texto donde le añadimos un numero: 5
Si hacemos printf(resultado), veremos en pantalla dicho texto.
¡Probad cosas ahora, antes de pasar al siguiente capítulo. (No temáis que en el siguiente os explico esto con más detalle, ¡pero siempre va muy bien experimentar por uno mismo las cosas primero!)
6. Punteros I. Introducción
En este capítulo veremos un "tipo de variable" llamados puntero, del inglés "pointer". Os tengo que pedir varias cosas en este capítulo:
Es un capítulo denso, aunque intentaré hacerlo lo más sencillo y entendedor posible. Si no lo entendierais, releerlo, pero hacedlo a conciencia. Si aun así no conseguís entenderlo, enviad un post con la duda.
Empecemos pues, primero de todo debemos entender el concepto dirección de memoria. La memoria de un ordenador es el "sitio" donde nuestro código se ejecuta. Esta memoria va des de un punto 0, hasta un punto X que es el máximo, no entro en detalles en esto. Entonces, una dirección de memoria es la posición en la que se encuentra una orden (orden: "pedazo" de código, instrucción). Estas direcciones no están en el sistema numérico habitual, sino que se encuentran en sistema hexadecimal. ¿Y que significa esto? Esto significa que "no hay 10 números" sino 16:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F
Para poder explicar el ejemplo que pondré, aprovecho para deciros que, tanto en C como en C++ siempre que vayamos a escribir un número en hexadecimal, este debe ir precedido de "0x" (sin las comillas).
Para poder entender el siguiente ejemplo, debo hablaros sobre el tipo de variable "BYTE", "WORD" y "DWORD" (entre otros). En realidad, estes solo existe en C++, pero, podemos "usarlo" tambien en C.
En C, un BYTE equivale a un "unisgned char", lo cual si conocemos.
En C, una WORD equivale a un "unsigned short", y esto si sabes que es, ¿verdad?
En C, una DWORD equivale a un "unsigned long".
Los que hayáis trabajado ya en C++, acostumbraros a los unsigned short y unsigned long.
Vamos a ver, ¿y porqué exite algo llamado BYTE, WORD y algo llamado DWORD, no bastaría con solo 1? El caso, es que estas variables también tienen un rango, mirad la tabla superior. Normalmente usaremos los unsigned long, asi que, no nos preocuparemos más por este tema.
¿Todos sabemos que es un BYTE? Bueno, para no complicarnos, un diremos que un byte es dos numero en hexadecimal. Sabiendo esto, decimos que una "unsigned short" esta formada por 2 bytes. O_o ¿Y que me estás contando, no? xd
0xAABB0xAA es un byte
0xBB es otro byte
0xAABB es un "unsigned short"
Un "unsigned long" es el doble que un "unsigned short", es decir, 4 bytes:
0xAABBCCDD0xAA es un byte
0xBB es otro byte
0xCC es otro byte
0xDD es otro byte
0xAABBCCDD es un "unsigned long"
Vale, para entender esta empanada mental que os acabo de soltar, vamos poner un ejemplo:
- Código: Seleccionar todo
unsigned long hexa_1 = 0x0000A; //realmente, esto es lo mismo que poner 0x0A, RECORDAT, que un BYTE son 2 numericos, asi que, poner 0xA estaria mal!
unsigned long hexa_2 = 0x00006; //0x06
//vamos a sumarlo, puesto que son numeros, podemos hacerlo
unsigned long resultado = hexa_1 + hexa_2;
/*
resultado = 0x06 + 0x0A;
resultado = 0x10
Que acaba de pasar aquí, ¿no?
Pensemos que esto es una suma decimal de toda la vida, (los números no son equivalentes)
suma = 8 + 2, que es lo que pasa aquí, cuando llegamos a 9, si le sumamos otro, pasa a ser 10, no? allí es lo mismo, tenemos
0x0A + 0x01 = 0x0B
0x0B + 0x01 = 0x0C
0x0C + 0x01 = 0x0D
0x0D + 0x01 = 0x0E
0x0E + 0x01 = 0x0F
0x0F + 0x01 = 0x10
cuando llegamos a F, lo que hacemos es pasar a 1 y dejar un 0, lo mismo que con decimal.
*/
Esto no ha acabado aun... la siguiente parte explicaré las variables puntero. Lo haré con imagenes si puedo, para que sea más fácil de entender, de momento me voy que he quedado. Espero que esta parte sea entendible, y os animo a practicar con ella. Por cierto, si queréis probarlo en un programa real, os dejo esto: =P
- Código: Seleccionar todo
#include <stdio.h>
int main(){
unsigned long hexa_1 = 0xAABBCCDD;
char texto[100];
sprintf(texto, "0x%x", hexa_1); //Copiamos en la variable texto el hexa_1
//NOTA: el %x se sustituye por hexa_1, pero no pone el 0x, por eso lo ponemos nosotros delante
//es simple estética, ¡no influye en nada!
printf(texto); //va a mostrar 0xAABBCCDD
getchar();
return 0;
}
Nota: ¡La función sprintf está explicada a en capítulo 7!, realmente, su uso es guardar algo en una variable, por lo que en el anterior ejemplo también podríamos hacer esto:
- Código: Seleccionar todo
#include <stdio.h>
int main(){
unsigned long hexa_1 = 0xAABBCCDD;
printf("0x%x", hexa_1); //va a mostrar 0xAABBCCDD
getchar();
return 0;
}
Esto se aplica a cualquier ejemplo donde haya usado o use sprintf y posteriormente haga un printf(...); Mi intención era simplemente mostraros como guardar un texto dentro de una variable, aunque ocupe más espacio en la memoria. El 2o ejemplo hace lo mismo en menos espacio y gastando menos memoria.
Vamos a empezar con lo que realmente pretende enseñar este capítulo. Veamos la siguiente imagen:

¿Que vemos en esta imagen? Primero de todo deberías poder identificar una variable de tipo int con nombre "var". El número en hexadecimal de arriba es la dirección (imaginaria) de memoria donde se encuentra esta variable, y lo que hay dentro del recuadro es el contenido de dicha dirección, es decir, de la variable. En este caso es una simple variable, y no tiene mas misterio que eso.
Si nosotros quisiéramos ver el contenido de esa variable, simplemente utilizaríamos métodos anteriores como:
- Código: Seleccionar todo
char texto[100];
int var = 75;
sprintf(texto, "%d", var);
printf(texto);
Pero, y si lo que queremos ver es la dirección de memoria donde se encuentra esta variable? La respuesta es el signo &. Veamoslo con el ejemplo anterior.
- Código: Seleccionar todo
char texto[100];
int var = 75;
sprintf(texto, "La variable tiene un valor de: %d ; y está en la dirección de memoria: 0x%x", var, &var);
printf(texto);
Vamos a ver, la función sprintf, que en el siguiente capitulo veremos con más detalle, sustituye los %d por variables numéricas, y las %x por variables numéricas en hexadecimal por el orden en que las encuentra, aquí lo que hace es (%d = var) , (%x = &var).
Veis el & que os decía? simplemente, en vez de devolver el valor de var, nos devolverá la dirección.
El caso de los punteros (punteros de memoria es su nombre concreto) es similar al anterior. Un puntero, tal y como dice su nombre, no contiene un valor, sino que contiene una dirección de memoria.
Veamos otra imagen:

Lo primero que os debería llamar la atención es como declaramos la variable, ¿veis que antes del nombre hay un asterisco (*)? Ese asterisco indica que la variable en cuestión es un puntero. Luego, el contenido de la variable, tal y como podéis ver, es una dirección de memoria. Si vamos a esa dirección, encontramos un valor. Vamos a adaptar el anterior ejemplo:
- Código: Seleccionar todo
int var = 70;
//Hemos dicho que un puntero se declaraba con *
int *p;
/*Debemos hacer que el puntero p, tenga un contenido, de lo contrario el programa final daría error (atención, el compilador no nos diría nada, pero obtendríamos un bonito crash de no darle un valor.
El valor debe ser una dirección de memoria que contenga algo, y que casualidad, justo arriba tenemos una variable con un contenido, ¿como podríamos obtener su dirección? Exactamente, con &
p = &var;
/*Fijaros, el * solo lo utilizamos cuando declaramos el puntero, poner un asterisco aquí haría otra cosa que explicaremos a continuación*/
//Vamos a volver a mostrar un texto
char texto[200];
sprintf(texto, "Contenido var: %d, dirección de var: 0x%x, contenido del puntero: 0x%x", var, &var, p); /*Otra vez p sin *, ya que queremos ver su contenido*/
//Vereis que ahora, la dirección de var y el contenido de p, es lo mismo.
printf(texto);
Vale, supongo que ahora mismo no le veis ninguna aplicación a esto, tranquilos, es normal. En posteriores capítulos empezaremos a ver aplicaciones de los punteros.
Para terminar este capítulo, ¿qué haríamos si quisiéramos leer el contenido de la dirección donde apunta (un puntero apunta a una dirección, es decir, el contenido del puntero es la dirección a la que apunta)? Bien, ¿os acordáis de que en un ejemplo he puesto varias veces "aquí no ponemos * porqué tendría otra utilidad? Pues esta es, si nosotros tenemos un puntero, y queremos ver que contiene la dirección donde apunta, debemos ponerle un * delante:
- Código: Seleccionar todo
int var = 70;
int *p;
p = &var;
char texto[300];
sprintf(texto, "Contenido var: %d, contenido de la dirección en que apunta el puntero p: %d, dirección de var: 0x%x, contenido del puntero: 0x%x", var, *p, &var, p);
/*Fijaros en la segunda variable que se sustituye, pone *p, justo lo que os explicaba.*/
printf(texto);
7. Operando con las cadenas de texto
Para poder entender este capítulo debemos haber entendido el anterior, en caso contrario pueden pasar dos cosas:
A. Que este capítulo te ayude a entenderlo y al final se te quede
B. Que no entiendas nada y te rayes.
¿Os acordáis de que dije que había dos formas de declarar una array de char?
- Código: Seleccionar todo
char texto[] = "Hola Mundo";
//y
char texto[11] = "Hola Mundo";
Bueno, en realidad hay otra que ahora ya os puedo explicar gracias al capítulo anterior.
- Código: Seleccionar todo
char *texto = (char*)malloc(11);
Primero de todo y lo más importante, para poder usar la función malloc, que ahora os explicaré que hace, debemos incluir el archivo
- Código: Seleccionar todo
#include <malloc.h>
¿Qué es malloc(tamaño);? Se trata de una función que reserva un espacio en la memoria y devuelve la dirección donde empieza dicha reserva. Analizemos esto. Malloc nos devuelve una dirección donde podemos guardar un contenido, y la única cosa que nos sirve para almacenar direcciones con contenido son los punteros ¿no? Es mas, queremos que sea un texto, así que, ¿porqué no hacemos un puntero de tipo char?
Luego, os fijáis que malloc pide un tamaño, (11), ¿de donde sale ese 11? Quizás aquí parezca obvio porqué el texto tiene 11 caracteres (recordad, empezamos por 0 y le sumamos 1 debido al \0), pero, no es tan obvio.
En realidad, allí debería poner
- Código: Seleccionar todo
char *texto = (char*)malloc(11 * sizeof(char))
¿... sizeof(variable o tipo variable)? Sí, la función sizeof nos devuelve el espacio de memoria que ocupa un tipo de variable en concreto. Lo que pasa que un "char" ocupa 1 espacio de memoria, es decir, queda como: malloc(11*1), lo cual es: malloc(11);
Pensadlo, es una array de char, por lo que está formada por variables tipo char, y cuantas de ellas tenemos, 11. ¿Y si tuviéramos un puntero de int?
- Código: Seleccionar todo
//Esto es solo para explicar el sizeof
int *punteroaint = (int*)malloc(1 * sizeof(int));
*punteroaint = 10; //Recordad, este * aquí implica que estamos editando el valor de la dirección donde apunta, ¡no el puntero!
Sigamos, ¿porqué hay un (char*) antes de malloc? Si recordáis lo del "Type Casting", eso lo explica todo. La función malloc no devuelve algo del tipo (char*) porque, imaginaros que quisiéramos guardar espacio para un puntero int (int*), no habría forma de convertir ese (int*) a (char*) por más typecasting que hiciéramos. En cambio, nos lo devuelve en un tipo llamado (void*). Para entendernos, cuando hablamos de variables y no de funciones, void* es un tipo "universal".
Vale, ahora tenemos una variable llamada texto, que es un puntero char, con un espacio de memoria. Pero, no tiene texto. ¿Como podemos ponerle texto? Primero de todo debemos incluir otro archivo:
- Código: Seleccionar todo
#include <string.h>
Nota: normalmente una cadena de texto es referida por string, pero no confundáis esto con un tipo de variable exclusivo de C++ que tiene el mismo nombre.
Luego, podríamos darle un valor a texto mediante:
- Código: Seleccionar todo
strcpy(texto, "Hola Mundo");
Esto simplemente borra todo lo que haya en texto, y le guarda "Hola Mundo".
¿Es realmente necesario que para usar la función strcpy() inicialicemos las variables de ese modo? No, realmente no lo es, también podríamos hacerlo con "char texto[11];" y luego usar strcpy.
Hagámoslo "más complicado". Imaginad que primero queremos ponerle solo "Hola ", y luego ponerle "Mundo". Si usamos strcpy() no podremos, ya que como ya he dicho, primero borra cualquier cosa que haya en la variable, en cambio haremos esto:
- Código: Seleccionar todo
strcpy(texto, "Hola ");
strcat(texto, "Mundo");
Otra función más a la lista de funciones! strcat() simplemente coge una variable de tipo char* y le añade otro texto a continuación, con lo que nuestra variable "texto" ya quedaría con "Hola Mundo".
Aquí vemos nuestro Hola Mundo complicado al completo:
- Código: Seleccionar todo
#include <stdio.h>
#include <malloc.h>
#include <string.h>
int main(){
char *texto = (char*)malloc(11);
strcpy(texto, "Hola ");
strcat(texto, "Mundo");
printf(texto);
getchar();
return 0;
}
Quizás alguien se ha preguntado una cosa, porque cuando usamos strcpy, strcat o printf no usamos "*texto" y usamos "texto", puesto que es un puntero, y lo que queremos es editar el contenido de la dirección donde apunta, no el contenido del puntero, ¿no? La cuestión reside en que esas funciones nos piden explícitamente una variable de tipo (char *) y no de tipo (char) (ya que *(char *) = char), es decir que tenemos que usar el puntero, "texto". ¿Pero entonces, porqué tambien podemos utilizar las array de char, tipo texto[11]? Digamos que una array se comporta como un puntero, pero ahora no voy a entrar en detalles sobre este tema.
Para finalizar este capítulo y cumplir una promesa que he hecho en anteriores ejemplos, os explico la función sprintf.
Aun y parecerse a printf(), esta hace una cosa distinta. Mientras que printf nos enseña algo en la pantalla directamente, sprintf nos guarda un texto en una variable. ¡Como lo hace?, veamos un ejemplo:
- Código: Seleccionar todo
char texto[100];
char mundo[] = "Mundo"
sprintf(texto, "Hola %s, este es mi %d ejemplo", mundo, 5);
Veis todas esas letras que están a continuación del %, bien, sprintf se dedica a substituir esos "códigos" por el valor de variables. Lo hace siguiendo un orden, que es básicamente, lo que sigue al texto, el 2o parámetro (parámetro: cada uno de los valores o variables que ponemos en una función, aquí el 2o parámetro es la cadena "Hola %s, este....") hasta que se acaba la función.
Es decir, el primer %s se sustituye por el 3r parámetro, en este caso, mundo; y el %d se sustituye por el 4o parámetro, 5 (no hace falta que sea una variable, puede ser un valor directamente). Si hubiera otro %(algo) al final, ahora explico este algo, se substituiría por el 5o parámetro. Siempre pensad que va en orden, el 1r %(algo) con el 1r valor o variable (entendemos que el 1o es en realidad el 3o, tal y como he dicho arriba), el 2o con el 2o, etc.
El primer parámetro de todos de la función, en este caso "texto", es la variable tipo (char*) donde se guarda el resultado.
¿Porqué he puesto en un lado %s y en otro %d? Resulta que debemos especificar el tipo de valor por el cual vamos a substituir, veamos esta tabla:
(Pensad que la primera letra, iría después de un %; es decir, en la primera linea, sería %c)
c Carácter (char) a
d o i signed int 392
e Notación científica, X*10^Y 3.9265e+2
E Notación científica, X*10^Y 3.9265E+2
f float 392.65
g Use the shorter of %e or %f 392.65
G Use the shorter of %E or %f 392.65
o Signed octal (numero en sistema octal, base 8) 610
s string (cadena de texto) sample
u Unsigned int 7235
x Unsigned hexadecimal integer (lo que vendría siendo todos los hexadecimales) 0x7fa
X Unsigned hexadecimal integer (lo que vendría siendo todos los hexadecimales) 0x7FA
p Puntero de memoria B800:0000
n No se muestra nada. Debe ser un puntero a un signed int, donde se guarda el número de caracteres a escribir.
% Si escribimos %%, simplemente se subtituirá por un %, puesto que poner un solo % no es valido.
NOTA: No hay diferencia destacable entre aquellos iguales pero uno en mayúsculas y otro en minúsculas.
NOTA2: Haber si consigo subirlo en una imagen, porque aquí no se ve muy claro, ¿no?
Un Hola Mundo con sprintf podría ser:
- Código: Seleccionar todo
#include <stdio.h>
int main(){
char texto[11];
char hola[] = "Hola";
sprintf(texto, "%s %s", hola, "Mundo");
printf(texto);
getchar();
return 0;
}
8. Funciones
Ya hemos visto, muy brevemente, en que consiste una función, pero ahora profundizaremos un poco más. Cada vez que nosotros hacemos algo del estilo "printf(texto);" estamos llamando a una función (del inglés "call a function"). Llamar una función no significa nada más que utilizar-la, en este caso la función es printf. Lo que escribimos entre los paréntesis () son los parámetros. Simplemente son valores que la función nos pide para poder funcionar.
También hemos visto ya como podemos nosotros crear una función, como es el caso de "main". ¿Os acordáis? Vamos a ver con mucho más detalle esto.
Este capítulo es vital para programar, ya sea en C o en cualquier otro lenguaje. Es muy importante que lo entendáis y que lo practiquéis mil veces.
La estructura de una función es como sigue:
- Código: Seleccionar todo
/*
tipo_de_variable_que_devuelve nombre_funcion ( parametros_funcion ){
...
return variable_o_valor_acorde_al_tipo_de_variable_que_devuelve
}
Mmmm... apliquemos esto a la función main, para que quede más claro:
- Código: Seleccionar todo
int main(){
...
return 0;
}
¿De que tipo es esta función? int
¿Nombre de la función? main
¿Parámetros de la función? Ninguno, pues entre los () no hay nada. No es obligatorio que una función tenga parámetros
¿Qué es el return 0;? De momento dejo la idea en el aire, y luego veremos con más detalle que es esto del return. Como he dicho en el ejemplo anterior no, el otro, la función debe "devolver" un valor o variable acorde al tipo de función. Esta función es "int", asi que si devuelve un 0 está bien, puesto que 0 es un numero.
Vamos a declarar nuestra primera función. Es más, quiero que primero lo intentéis solos, y luego miréis como se hace. Imaginarios que os viene un empresario y os dice que quiere una función tipo "int", con 2 parámetros int, que la función sume esos parámetros, y devuelva el resultado. No os agobiéis, se que no es fácil. Si no sale, es normal, pues aun hay muchas cosas que no he explicado.
¿Lo habéis intentado? Vamos a ver como sería el código:
- Código: Seleccionar todo
int sumar(int variable1, int variable2){
int resultado;
resultado = variable1 + variable2;
return resultado;
}
Los parámetros, cuando declaramos una función, también deben ser declarados, por eso pone "int variable1". Luego, si queremos declarar otro parámetro, debemos poner una coma ",", y declarar normalmente el parámetro.
Tened en cuenta que estos parámetros solo son válidos dentro de la misma función, es decir, así como en la función luego hace "variable1 + variable2", si intentamos hacer lo mismo en otra función, sin declarar estas variables, nos daría error. Los parámetros de una función solo son válidos en la misma función.
"return resultado;": esto viene a significar que cuando se llega a este punto, ya no se hace nada más. Si nosotros hubiéramos puesto, "resultado = 0;" justo después del "return", eso no llegaría a pasar. En vez de esto, a partir de esto momento, la función "sumar" adquiere un valor, resultado. pongamos un ejemplo:
- Código: Seleccionar todo
int me_da_palo_hacerlo_a_mano;
me_da_palo_hacerlo_a_mano = sumar(2, 3);
//la función sumar hará, 2+3 = 5
//y el return quedará como, return 5;
//es decir, al final tendremos esto:
//me_da_palo_hacerlo_a_mano = 5;
En conjunto quedaría asi:
- Código: Seleccionar todo
#include <stdio.h>
int sumar(int variable1, int variable2){
int resultado;
resultado = variable1 + variable2;
return resultado;
}
int main(){
int resultado = sumar(2, 3);
printf("%d", resultado);
//NOTA: printf utiliza el mismo sistema que sprintf, pero lo muestra directamente en pantalla, tal y como ya se ha comentado
getchar();
return 0;
}
Fijaros que la función sumar(...) esta antes que la función main(), si lo hicierais al revés, daría error, ahora explico porqué y como solucionarlo.
Vale, imaginaros que hemos puesto la función sumar después de la función main. Esto en C es un error (a no ser que hagamos una cosa que luego explico) ya que en C, y C++, para utilizar algo primero tenemos que declararlo. En este hipotético caso, primero lo utilizamos, y luego lo declaramos (ERROR!).
Una perfecta solución sería cambiarlo de orden, pero, ¿y si no podemos o no queremos? La respuesta son los prototipos (del inglés "prototype"). Un prototipo es algo muy muy simple, pues consiste en coger la primera linea de la función, donde la declaramos, en este caso "int main(int variable1, variable2);" y ponerlo al principio, después de los include.
Fijaros en una cosa, después de los parámetros, no ponemos los {}, sino que ponemos directamente un ;
Veamoslo en conjunto:
- Código: Seleccionar todo
#include <stdio.h>
int sumar(int variable1, int variable2);
int main(){
int resultado = sumar(2, 3);
printf("%d", resultado);
//NOTA: printf utiliza el mismo sistema que sprintf, pero lo muestra directamente en pantalla, tal y como ya se ha comentado
getchar();
return 0;
}
int sumar(int variable1, int variable2){
int resultado;
resultado = variable1 + variable2;
return resultado;
}
Vale, luego, se pueden hacer tantos tipos de funciones como tipos de variable. Eso experimentarlo vosotros. Yo solo os voy a hablar de un tipo en concreto, "void".
- Código: Seleccionar todo
void funcion(){
return;
}
Hay una cosa que, mirando lo que os he explicado antes, no debería cuadraros. Los parámetros están bien, pues hemos dicho que no tenían porque haber, pero, ¿y el "return;"? ¿Como es que solo pone return y nada más? Sencillamente, las funciones void no devuelven nada. Estas funciones están pensadas para reducir el tamaño del código, entre cosas. Imaginaros que en vuestro programa hacéis 50 veces esto:
- Código: Seleccionar todo
char texto[] = "Hola Mundo";
printf(texto);
Quizás no es el mejor ejemplo, pero que es más cómodo, poner 50 veces todo eso, o 50 veces "funcion()" (o el nombre que le deis). Pues podemos poner todo lo anterior en un "void" y llamar al void 50 veces.
- Código: Seleccionar todo
void funcion(){
char texto[] = "Hola Mundo";
printf(texto);
}
//dentro de alguna otra función
funcion();
Habéis notado que aquí, en este ejemplo, no he puesto el "return;". Puesto que la función no va a devolver nada, la función de "return" es simplemente la de parar la función, es decir, que todo lo que haya después del "return;" no se ejecute.
Del mismo modo que podemos hacer funciones de cualquier tipo, también podemos hacer que una función sea un puntero, por ejemplo:
- Código: Seleccionar todo
char *dame_un_texto(){
char *texto = (char*)malloc(11);
strcpy(texto, "Hola Mundo");
return texto;
}
Y, quizás alguno os preguntéis, porqué no ha echo esto?:
- Código: Seleccionar todo
char *dame_un_texto2(){
char texto[] = "Hola Mundo";
return texto;
}
Esto me lleva a explicaros la "area de efecto" de una variable, puesto que no se me ocurre mejor manera de decirlo xd.
Hay 2 formas de declarar una variable, de forma global, o de forma local.
Cuando declaramos una variable, lo podemos hacer fuera de toda función. Si hacemos esto, la variable sera accesible (se podrá usar) en cualquier función (recordad que, primero declaramos, luego usamos, vigilad el orden). Veamos un ejemplo:
- Código: Seleccionar todo
int a;
void funcion1(){
a = 1;
}
void funcion2(){
a = 2;
}
En cambio, si hacemos esto:
- Código: Seleccionar todo
void funcion1(){
int a;
a = 1;
}
void funcion2(){
a = 2; //ERROR!
}
En esto 2o caso, la variable a es local, puesto que solo sirve para la funcion1, si la utilizamos fuera, nos dará error el compilador.
Hay más formas de declarar variables, peeero, las veremos mas tarde.
Retomemos el tema de las funciones que devuelven punteros. ¿Os acordáis de los ejemplos aun? Si no los recordáis, echadles una ojeada antes. En el segundo, la variable es local, puesto que simplemente hacemos una array de texto. Devolver un puntero, una dirección, a una variable local es un error, puesto que la variable, en cuando se termine la función dejará de existir, así que, la dirección esa, apuntará a la nada.
En cambio, en el primero, usamos la función malloc() para guardar una memoria. Guardar memoria es un acto global, así que, cuando termine la función esa memoria seguirá existiendo, por lo que no hay problema alguno en devolver esa dirección.
Esto es denso, aseguraros de entenderlo, y practicad, practicad mucho. Si lo entendéis y lo habéis practicado continuad.
Del mismo modo, los parámetros también pueden ser de cualquier tipo, incluidos punteros. Si habéis entendido todos los temas anteriores, también entenderéis lo que hace esta función, pero de todas formas, añado comentarios:
- Código: Seleccionar todo
void cambiar_valor(int *variable, int valor){
*variable = valor;
/*
La función pide un puntero, es decir una dirección de memoria como parámetro 1.
Luego, va a la dirección de ese puntero (*variable) y le cambia el valor por el de la variable "valor".
*/
}
int main(){
int var = 5;
printf("%d\n", var); //Nota, "\n" hace que salte de linea en la consola. Linea nueva. Saldrá un 5 en la pantalla
cambiar_valor(&var, 10);
/*
Como ya sabemos, la función pide un puntero int, una dirección. Pues le damos la dirección de nuestra variable var.
Luego nos pide un valor para guardarle, 10.
Si simplificáramos todo el proceso, haríamos esto:
*(&var) = 10;
Pues, primero pasamos el puntero, &var, y luego obtenemos el valor de la dirección con *, *(&var)
y esto, aun más simplificado, es:
var = 10;
*/
printf(%d\n", var); //Saldrá un 10 en la pantalla
getchar();
return 0;
}
Para terminar este capítulo, solo me queda deciros una cosa, ¡practicad hasta aburriros!
9. Profundizando en las arrays
Bueno, pequeño cambio en el planning. Antes de explicaros el tema de las estructuras y "sus hermanos", vamos a profundizar en el tema arrays.
Vamos a dar primero una definición un poco más, "real", de lo que es una array: es una secuencia de objetos del mismo tipo. Es decir, valores o objetos de un mismo tipo de variable. Sabemos ya, además, como se declara una array:
- Código: Seleccionar todo
tipo nombreArray[numeroDeElementos];
Un tema que he dado por entendido, pero que no he explicado, es que, a cada posición de la array(0, 1, 2, .., numeroDeElementos-1) podemos guardar un valor y acceder a el:
- Código: Seleccionar todo
nombreArray[0] = X; //X es un valor acorde al tipo de variable
Este 0, o lo que ponemos entre los corchetes [] será llamado a partir de ahora como "indice".
Los elementos de una array se almacenan de forma contigua en la memoria, veamos una imagen que lo ilustra:

Tengamos en cuenta que, la separación entre cada una de estas casillas en memoria, equivale al tamaño del tipo de variable. Recordad "sizeof(tipo)" nos dirá el tamaño de x tipo en memoria. Así, si en esta foto podríamos hablar de una array de char, puesto que entre cada casilla solo hay 1 espacio de memoria, y un char ocupa 1 espacio de memoria.
Imaginaros que hacemos una array de 604 elementos, y más tarde, en otra función, queremos comprobar el tamaño de dicha array y no nos acordamos del número. Entonces recurriremos otra vez a sizeof(), en este caso "sizeof(nombreArray);" nos devolvería el numero de elementos que existen en una array. Veamos un ejemplo:
- Código: Seleccionar todo
char char_array[1024];
printf("%d", sizeof(char_array) / sizeof(char)); //Mostraría 1024.
Varías cosas a tener presente:
¿Porqué dividimos por sizeof(char)? Recordad que he dicho que cada casilla, de esa imagen para tener una referencia, estaba separada de la otra por el tamaño del tipo de variable, es decir, que lo que realmente devuelve sizeof(nombreArray) es el número de elementos multiplicado por lo que ocupa cada elemento en memoria. Si queremos obtener el número de elementos real, debemos dividirlo por el espacio que ocupa en memoria, de otra forma obtendríamos el espacio que ocupa, no el número de elementos. En el caso de una array de char no hay problema, puesto que un char ocupa 1 espacio, por lo que estamos dividendo por 1, y el resultado será el mismo, pero si fuera otro tipo, como int, estaríamos dividiendo por 4. Probarlo vosotros, haced un programa simple basado en el ejemplo anterior y mirad que pasa si ponéis la división o no la ponéis.
Otra cosa tener en cuenta, si que es verdad que la anterior array tiene 1024 elementos, pero, tiene 1024 porqué empezamos desde 0 hasta 1023, y el 1024 se reserva para el '\0', es decir, intentar escribir algo en la posición 1024 probablemente provocaría un error de corrupción de datos. Digo probablemente porque todo dependería de en que posición de memoria se encuentra y de que tiene detrás.
Más cosas, como podemos comprobar si podemos guardar algo en una posición de una array, me explico imaginad que la array tiene 10 elementos, y por error intentamos escribir en el 12. Deberíamos primero de comprobar el tamaño de la array para asegurarnos de que nuestro programa va a funcionar bien y no intentará escribir en el 12 cuando el máximo seria 10.
Vamos a otro tema dentro de las arrays, las arrays multidimensionales. Las arrays anteriores eran unidimensionales, puesto que solo admitían 1 indice, en cambio, nosotros podemos hacer que nuestra array acepte más de un indice, veamos un prototipo de como sería:
- Código: Seleccionar todo
tipo nombreArray[indice1][indice2]
Nota: Aveces nos referimos a las arrays unidimensionales como listas (no confundir con el termino de C++ std::list) y a las arrays multimensionales como tablas o matrices.
Explicaré esto a partir de dos imágenes y ejemplos, pues es mucho mas sencillo:


Yo creo que se ve bastante claro, pero por si acaso: Tenemos un primer índice donde no podemos guardar valores, sin embargo, dentro de este indice si podemos guardar cosas.
Ejemplo:
- Código: Seleccionar todo
int i_array[6][3];
i_array[0][1] = 1;
i_array[5][2] = 0;
i_array[2][0] = 2;
//etc.
Si queremos, también podemos inicializar directamente esta array a un número:
- Código: Seleccionar todo
int i_array[6][3] = {/*estamos en el primer indide, 0 */ { /*accedemos al segundo indice*/ 0 /*posicion 0*/, 1, 2 }, {/*2o indice 1*/ 3,4,5}, etc.}
//Veamoslo bien hecho y sin comentario de por medio:
int i_array[6][3] = {{0,1,2},{3,4,5},{6,7,8},{9,10,11},{12,13,14},{15,16,17}};
//Esto es equivalente a esto:
int i_array[6][3] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17};
/*Puesto que automaticamente, cada 3 números (el máximo del 2o indice) aumentará el primer indice en 1, cosa que hacíamos manualmente en el primer ejemplo mediante el uso de comas "," y llaves {}. */
Del mismo modo que podemos hacer arrays bidimensionales, podemos hacer de 3, 4, 5 e infinitas dimensiones:
- Código: Seleccionar todo
int array1[1][1][10];
int array2[10][12][100][1000];
En el siguiente capítulo, puesto que aun no hemos visto bucles ni condicionales, trataremos estos y explicamos como ordenar los elementos (numéricos) de una array de menor a mayor.
10. Condicionales (if, switch)
Como todo buen lenguaje de programación, C es capaz de ejecutar código solo si se confirma una condición. Para ello, usaremos el llamado "if", que traducido a español significa "Si". Veamos su estructura y funcionamiento:
- Código: Seleccionar todo
if( condicion ){
//Codigo a ejecutar si la condición es verdadera
}
Me gustaría hacer una alusión a un lenguaje de programación llamado VB (Visual Basic), en el cual los if se declaran de forma diferente, ahora entenderéis porqué hago esta referencia:
- Código: Seleccionar todo
if condicion then
//codigo
endif
Bien, el hecho esta en que, todas ellas son palabras inglesas:
if -> Si
then -> entonces
endif -> fin de la condicion
En C, este then se sustituye por un "{" y el endif por "}". Es decir, la traducción de lo que hace el if es
Si se cumple esta condicion entonces
ejecutar este codigo
fin de la condicion
Veamos un ejemplo práctico:
- Código: Seleccionar todo
int var = 1;
if(var == 1){
printf("La condicion del if, var == 1, es verdadera");
}
Recordad el uso de "==", puesto que estamos comparando, no guardándole un valor.
Probad a cambiar el 1 por cualquier otro número, y veréis como no se cumple la condición, y en consecuencia, no aparece nada en pantalla.
NOTA: No podemos comparar variables del tipo char*. Así pues, sería incorrecto hacer lo siguiente:
- Código: Seleccionar todo
char h[] = "hola";
if(h == "hola")..... // ERROOOR COMO UNA CASA
Más tarde ya trataremos como comparar, entre otras cosas, variables del tipo char*.
Notese que si podemos comparar variables de tipo char.
- Código: Seleccionar todo
char c = 'a';
if(c == 'a')...
Puesto que si lo pensáis bien, simplemente estamos comparando números, recordad la definición de char.
Es importante destacar que, igual que pasaba con los bucles for y while, podemos omitir los { }, en caso de que el cuerpo sea una sola sentencia, asi pues, el anterior ejercicio podría quedar como:
- Código: Seleccionar todo
int var = 1;
if(var == 1)
printf("La condicion del if, var == 1, es verdadera");
Introduzcamos ahora una palabrita más, "else". Su traducción aproximada sería "sino". Con esto ya podríamos deducir que lo que esta palabra hace, es ejecutar un código cuando la condición no se cumple:
- Código: Seleccionar todo
if( condicion ){
//La condición es verdadera
//Ejecutar código
}else{
//La condición es falsa
//Ejecutar otro código
}
Notese que aquí también podemos omitir los { } tanto en el if como en el else, de forma independiente, asi pues, todas las siguientes estructuras serían validas (además de la anterior)
- Código: Seleccionar todo
if( condicion ){
//1
//2
}else
//1
- Código: Seleccionar todo
if( condicion )
//1
else{
//1
//2
}
- Código: Seleccionar todo
if( condicion )
//1
else
//1
Cuando usamos las llaves "{ }" podemos no poner ningún código en su interior sin hacer ningún cambio especial, pero si los omitimos, se dan condiciones especiales.
- Código: Seleccionar todo
if( condicion );
Ahora os recomendaría, para variar, que practicarais, puesto que para entender lo siguiente es necesario haber entendido y practicado lo anterior.
Si ya habéis practicado, continuad leyendo:
Que pasa si nosotros queremos hacer algo del tipo, "Si la variable NO es 1".
Una solución chapucera sería la siguiente:
- Código: Seleccionar todo
if(var == 1);
else{
//Puesto que ELSE solo se ejecuta si no se cumple la condición, y por tanto solo llegamos aquí si var NO es 1.
}
Pero, resulta que C, y C++ y muchos otros lenguajes, disponen de un carácter especial, "!", el signo de exclamación. Este significa NEGAR.
Así pues, podemos hacer una comparación negativa, usando esto: !=
- Código: Seleccionar todo
if(var != 1){
//Si llegamos aquí, significa que var NO (!=) es igual a 1
}
Hay otra forma, pero debemos entender muy bien su funcionamiento.
Tenemos var == 1, en el primer caso. Si esto fuera verdad, nos devolvería que es verdad (que redundante pensaréis), pero resulta que decir que algo es verdad, siempre que hablemos de programación, equivale a un 1. Pensemos un momento en binario, eso que solo hay 000111010101. ¿Cuál seria el contrario de 1 en binario? Pues 0, tampoco hay más elección posible. Dejo esto al aire, puesto que me interesa y mucho para más tarde.
Dicho de otra forma más fácil, ¿cuál es el contrario de verdadero? FALSO.
Así pues, podríamos decir que si se da que, !VERDADERO, es decir, FALSO, se ejecute algo.
VERDADERO, ¿a que equivale en nuestro ejemplo anterior? A "var == 1", verdad? Entonces quedaría así:
- Código: Seleccionar todo
!(var == 1); //!VERDADERO
Fijaros en los paréntesis, queremos negar la condición entera, hacer "!var == 1" no serviría de nada.
Tenemos pues que,
- Código: Seleccionar todo
if( var != 1 ){}
Es lo mismo que:
- Código: Seleccionar todo
if( !(var == 1) ) {}
Nota: El uso de != es válido, pero no lo es el de !> o cualquier parecido, por lo que, si nuestra condición es:
- Código: Seleccionar todo
if(var > 1){} // Siempre que var sea mayor estricto a 1 será verdadero
Y queremos negarlo, debemos hacer lo siguiente:
- Código: Seleccionar todo
if( !(var > 1) ){} //No mayor ni igual a 1 = verdadero
Aunque si pensamos un poco, veremos que el contrario de > es <=, por lo que podemos simplemente escribir:
- Código: Seleccionar todo
if ( var <= 1 ) //Menor o igual a 1 = verdadero
En fin, aprovecho para decir que probéis de hacer condicionales con las estructuras "==", "!=", ">", "<", ">=", "<=".
Y por hoy, creo que ya he escrito más que suficiente. Mañana más y mejor, puesto que el "if" aun no se ha acabado, tiene cuerda para rato.
Practicad todos estos conceptos nuevos del "if" por favor!
10.1 Entrando en detalle en las condiciones
Pensemos una cosa, que pasa si queremos comprobar mas de una condicion. Del plan:
Si pasa esto X, pero si no pasa esto y pasa Y, o no pasa ni X ni Y y pasa Z... etc
Podriamos hacer algo del tipo:
- Código: Seleccionar todo
if(X){
}else{
if(Y){
}else{
if(Z){
}
}
}
Claramente esto funcionaria, pero, ¿que pasa si queremos comprobar 20 condiciones de esta manera? Simplemente, que al final no sabremos ni lo que hacemos. Es por esto, que podemos utilizar un formato distinto pero que hace exactamente lo mismo:
- Código: Seleccionar todo
if(X){
}else if(Y){
}else if(Z){
}else{
//Si ninguna se ha cumplierto
}
¿Mucho mejor no? Código claro, estructurado y nos permite comprobar varias condiciones con el simple uso de "else if".
Ahora quiero que os imaginéis otra situación muy parecida a la anterior. También queremos comprobar distintas condiciones, pero todas ellas recaen sobre la misma variable de tipo numério (es decir, todos los tipos menos cadenas del tipo "abc", recordad que comparaciones de cadenas (char *), como cadena no puntero, lo trataremos más tarde).
Tenemos una variable que llamaremos "test", la cual queremos comprobar si vale 1, 2, 3, 4, 5, 6, 7, 8, 9 o 10. Efectivamente una solución bastante lógica, por lo que sabemos ahora, sería el uso de los multiples "else if", quedando asi:
- Código: Seleccionar todo
if(test == 1);
else if(test == 2);
else if(test == 3);
else if(test == 4);
else if(test == 5);
else if(test == 6);
else if(test == 7);
else if(test == 8);
else if(test == 9);
else if(test == 10);
Mmmm... sí, vale, está bien, pero, realmente, ¿vamos a escribir 10 veces la variable "test"? ¿El día que queramos hacer 100 comprobaciones, lo escribiremos 100 veces? NO, la respuesta es NO.
Para esto existe otro tipo de condicional, el llamado "switch". ¡Abrimos nuevo subapartado!
10.2 Switch
La sintaxis de este condicional, que por cierto su nombre significar "cambiar" en castellano, es la que sigue:
switch(NOMBRE_VARIABLE){
case VALOR1:
break;
case VALOR2:
break;
}
Realmente solo hay una cosa que debería extrañarnos, que es la palabrita "break". Por lo demás, lo que hacemos es como un "else if". Cada "case X:" equivale a un "else if". Hagamoslo con el ejemplo anterior.
- Código: Seleccionar todo
switch(test){
case 1: break;
case 2: break;
case 3: break;
case 4: break;
case 5: break;
case 6: break;
case 7: break;
case 8: break;
case 9: break;
case 10: break;
}
Mucho, pero que mucho, mejor. NOTA: El condicional switch solo sirve para IGUALDADES (==), en caso de querer otras cosas, podríamos recurrir a lo que explicaré a continuación.
La palabra break la veremos con más detalle en el capítulo 11, al final. Pero de todas formas, para poder entender si función, diremos que lo que hace es evitar que el codigo que haya después se ejecute. Veamos un ejemplo en que omitiremos el uso de break:
- Código: Seleccionar todo
switch(test){
case 1: printf("A"); break;
case 2: printf("A"); break;
case 3: printf("B"); break;
}
Fijaros, ¿no es verdad que tanto el caso 1 como el 2 hacen exactamente lo mismo? Hemos dicho que el break evitaba que se ejecutara lo que había a continuación, pero, ¿y si queremos simplificarlo?
- Código: Seleccionar todo
switch(test){
case 1:
case 2: printf("A"); break;
case 3: printf("B"); break;
}
Interesante, ¿no?. Esto podríamos interpretarlo de la siguiente forma: "Si test == 1 o test == 2 -> printf("A");"
Si test vale 1, se ejecutará lo mismo que si test vale 2, pero como luego hay un break, no se ejecutará nada más.
Ahora las cosas van a, no complicarse pero sí enredarse un poco, asi que, es mi consejo que ¡PRACTIQUÉIS MUCHO ESTO!
10.3. Más sobre condiciones
Anteriormente hemos hablado de que hacer cuando queremos evaluar 2 (o más) condiciones por separado, el else if, pero, y si lo que queremos es evaluar en un mismo if más de una condición a la vez, estilo si I < 10 i M > 8, etc. En nuestro idioma hablado, sea el que sea, dispones de las conjunciones "y", y "o". Así pues decimos si eso y aquello, si eso o lo otro. En C tenemos unos equivalentes llamados && (y) y || (o). Nótese que son 2 carácteres, no me escribáis uno solo porque entonces lo que hacemos son operaciones con bits (que más adelante explicaremos).
Veamos un ejemplo:
- Código: Seleccionar todo
int a = 1;
int b = 2;
if(a == 1 && b == 2)
printf("a = 1 y b = 2");
Probadlo, y luego cambiad el valor de cualquier de estas dos variables, veréis como no se muestra el resultado en pantalla. Una vez hecho, probemos cambiando el && por un ||
- Código: Seleccionar todo
int a = 1;
int b = 2;
if(a == 1 || b == 2)
printf("a = 1 y b = 2");
Basta que con una de las igualdades, o a == 1 o b == 2, para que se ejecute el condicional.
Esto es facilito, pero luego podemos hacer combinaciones de && y ||. Y nótese que podemos usar paréntesis para ello
- Código: Seleccionar todo
if((a == 1 && b == 2) || c == 3)
NO es lo mismo que
- Código: Seleccionar todo
if(a == 1 && (b == 2 || c == 3))
En el primer caso primero se comprueba si a == 1 Y b == 2, si eso se cumple, tenemos la primera condicion verdadera. Ahora bien, luego viene un ||, es decir, que o todo el primer bloque es cierto, o lo es la 2 condicion, con una basta.
Miremos el segundo caso. Primero tenemos a == 1, ¿es cierto? Si lo es, ya tenemos un bloque verdadero. Luego nos exige mediante un && que se cumpla que b sea 2 o, recalco o, que c sea 3.
Si habéis leído atentamente, las diferencias y usos están más claros.
Ahora, tanto si lo tenéis claro como si no, ¡PRACTICAD!
11. Bucles
Un bucle es cualquier construcción de programa (código) que repite una sentencia o conjunto de sentencias de forma repetida.
En todo bucle encontramos un cuerpo, las sentencias que se repiten, y iteraciones, que son cada una de las repeticiones del bucle.
Encontramos diferentes tipos de bucles, analizamos primero el while
11.1 - Bucle While
Un bucle while se ejecuta según una condición. Antes de cada iteración (repetición), se comprueba que la condición sea válida. Si lo es, se ejecuta el cuerpo, si no, no. En otras palabras, un bucle while se ejecutará de 0 a más veces, según la condición siga siendo válida.
Veamos la sintaxis básica de un bucle while:
- Código: Seleccionar todo
//Caso 1:
while(condicion)
sentencia;
//Caso 2:
while(condicion){
sentencia_1;
sentencia_2;
sentencia_3;
...
sentencia_n;
}
//También sería valido en el caso 1 poner {}, pero simplemente los omitimos:
while(condicion){
sentencia;
}
Pongamos un ejemplo:
- Código: Seleccionar todo
int n = 0;
while (n < 10)
n++;
Primero de todo, "n++". Siempre que veáis un ++ después de una variable numérica, significa lo mismo que, "n += 1", y esto significa lo mismo que "n = n+1". Por poner un ejemplo: "7++" = "7 += 1" = "resultado = 7 + 1 = 8".
Luego, la condición aquí es n < 10. Para todos aquellos que hagáis hecho un poco de matemáticas, sabréis que "<" significa "menor que", y si no, ahora ya lo sabéis. Aprovecho para recordar:
variable < numero (menor que)
variable > numero (mayor que)
variable <= numero (menor o igual que)
variable >= numero (mayor o igual que)
Es decir, la condición es que "n", nuestra variable, sea menor que 10. Si es menor que 10, le sumamos 1. En caso de que nuestra variable llegue a 10, la condición ya no será verdadera, puesto que 10 no es menor que 10, es igual.
Veamos otro ejemplo. Os acordáis que en capítulos anteriores había usado un == en vez de = ? En C, y C++, cuando comparamos dos valores (o variables) para saber si son iguales debemos usar == , a diferencia de cuando le guardamos un valor que usamos =.
Este ejemplo es un poco tonto:
- Código: Seleccionar todo
int n = 5;
while(n == 5)
n++;
Si os fijáis solo se va a ejecutar una vez, pues, a la primera pasada 5 == 5, así que se ejecuta el cuerpo, n para a ser 6. En la segunda pasada 6 == 5, y eso no es verdad, así que, ya no se ejecuta.
11.2 - Bucle for
SI habéis entendido el anterior, este es muy parecido:
- Código: Seleccionar todo
for( declaraciones_o_inicializaciones ; condicion ; accion){
cuerpo
//recordad que si el cuerpo es 1 sola instrucción podemos eludir los {}
}
Primer, que son las declaraciones o inicializaciones. De la misma forma que con el while, poníamos justo antes "int n = 5", aquí también tenemos que hacerlo, solo que podemos hacerlo dentro.
A diferencia de el bucle while, el primer "apartado", declaraciones (me refiero a los 3 que se separan por
, solo se ejecuta a la primera pasada, o iteración. Es decir, solo se ejecutará 1 vez, la primera de todas y antes de todo.Veamos un par de ejemplos:
- Código: Seleccionar todo
for(int n = 0; ... ; ...);
Como n no existe, la declaramos en el primer "apartado".
Nota: los "..." los pongo para indicar que allí va algo.
- Código: Seleccionar todo
int n;
for(n = 0; ... ; ...);
"n" ya está declarada, pero no tiene ningún valor, así que, la inicializamos a 0, "n=0".
- Código: Seleccionar todo
int n = 0;
for( ; ... ; ...);
Puesto que "n" ya existe y ya tiene un valor (0) no hace falta hacer nada. No es obligatorio poner nada en el primer apartado, de hecho, en ninguno de ellos es obligatorio.
Siguiente apartado, condición. Este es exactamente lo mismo que en el while, una condición que se comprueba a cada iteración.
Y el último, acción. El bucle for, a diferencia del while, ejecuta una ultima acción antes de pasar a la siguiente ronda, veamos un esquema de lo que hace el for:
- Código: Seleccionar todo
-- RONDA 0
--- declaraciones_inicializaciones
--- condición verdadera?
----- cuerpo
----- acción
-- RONDA 1
--- condición verdadera?
----- cuerpo
----- acción
... etc.
¿Entendéis pues lo que os quiero decir? Justo al acabar el cuerpo, ejecutará algo que tu hayas puesto. Veamos un ejemplo práctico:
- Código: Seleccionar todo
for(int n = 0; n < 10; n++)
printf("%d\n", n);
Esto hará lo siguiente:
- Código: Seleccionar todo
-- RONDA 0
--- int n = 0;
--- (n < 10), (0 < 10), verdad
----- printf(...)
----- n++, n +=1, n = n + 1 => n = 1
-- RONDA 1
--- (n < 10), (1 < 10), verdad
----- printf(...)
----- n++, n +=1, n = n + 1 => n = 2
-- RONDA 2
--- (n < 10), (2 < 10), verdad
----- printf(...)
----- n++, n +=1, n = n + 1 => n = 3
-- RONDA 3
--- (n < 10), (3 < 10), verdad
----- printf(...)
----- n++, n +=1, n = n + 1 => n = 4
-- RONDA 4
--- (n < 10), (4 < 10), verdad
----- printf(...)
----- n++, n +=1, n = n + 1 => n = 5
-- RONDA 5
--- (n < 10), (5 < 10), verdad
----- printf(...)
----- n++, n +=1, n = n + 1 => n = 6
-- RONDA 6
--- (n < 10), (6 < 10), verdad
----- printf(...)
----- n++, n +=1, n = n + 1 => n = 7
-- RONDA 8
--- (n < 10), (8 < 10), verdad
----- printf(...)
----- n++, n +=1, n = n + 1 => n = 9
-- RONDA 9
--- (n < 10), (9 < 10), verdad
----- printf(...)
----- n++, n +=1, n = n + 1 => n = 10
-- RONDA 10
--- (n < 10), (10 < 10), falso
11.3 - Bucle do-while
Para entender este bucle, debemos tener claro el concepto del bucle while. El funcionamiento es prácticamente el mismo, solo que en while, si recordamos bien, primero se comprobaba la condición y si era verdadera se ejecutaba el cuerpo. En el do-while, en cambio, primero se ejecuta el cuerpo y luego se comprueba la condición, si esta es verdadera, se vuelve a ejecutar el código.
Ejemplo:
- Código: Seleccionar todo
do{
//Cuerpo
}while(condicion); //Notese el ;
Si sabemos un poco de inglés, la cosa se "facilita", traducido, dice esto:
- Código: Seleccionar todo
HACER {
//cuerpo
}MIENTRAS(condición);
Creo que es fácil de entender, y es por esto que no daré mas detalles sobre ello. SI tenéis dudas, PRACTICAD! Y si las tenéis siguiendo, PRACTICAD MÁS! Y si aun tenéis, pues preguntar aquí.
Eso si, os dejo un ejemplo, que no es mío, pero que ilustra perfectamente el uso de do-while:
- Código: Seleccionar todo
char c;
do {
printf("\n\nIntroduzca una letra: ");
c=getche(); /*devuelve el carácter leido desde la consola, y lo visualiza.*/
c=toupper(c); /* devuelve el carácter c en mayúscula, y si no lo hay, devuelve el mismo*/
printf("\nSu mayúscula correspondiente es: %c",c);
} while (c!='X');
int j=100; /* sentencia que ejecutamos cuando deje de cumplirse la condición del do-while. */
Palabritas mágicas
¿Buen título eh? Resulta que mientras estamos en un bucle disponemos de dos palabras reservadas, dicho de otra forma, que tienen una función especial. Se trata de "break" y de "continue".
Planteemos una primera situación. Que pasa si nosotros llegamos a un punto en que queremos salir del bucle. Podriamos pensar las mil y una para añadir una condición más al bucle, pero, ¿para que complicarnos tanto la vida? La palabra "break" (romper en castellano) nos permite salir de un bucle. Veamos un ejemplo:
- Código: Seleccionar todo
for(int i = 0; i < 10; i++){
if(i == 5) break;
printf("%d\n", i);
}
El resultado será este:
1
2
3
4
El 5 (y posteriores) nunca lo llegaremos a ver, puesto que al llegar a este número, salimos del bucle sin ejecutar el código restante ni las siguientes pasadas.
Luego existe otra situación, ¿que pasa si queremos que, llegado a una condición X, no se ejecute el código, pero que en vez de salir por completo, como en el caso del "break", continue con la siguiente pasada? La respuesta es "continue". Vamos un ejemplo:
- Código: Seleccionar todo
for(int i = 0; i < 10; i++){
if(i == 5) continue;
printf("%d\n", i);
}
El resultado será este:
1
2
3
4
6
7
8
9
El 5 no lo veremos, puesto que cuando lleguemos al 5, se saltará directamente a la siguiente pasada sin ejecutar el código que haya a continuación.
12. Puesta en común y visión global
Sinceramente, ahora se trataría de coger TODO, absolutamente todo lo que hemos aprendido y realizar un proyectillo. Nada muy grande, pero tampoco nada muy pequeño.
Os recomiendo que os paséis por mi pequeño foro (http://shallowcoding.ne t76.ne t)(sin los espacios), donde subo todo mi código y tutoriales, y os hagáis una pasada por el hilo de "[TUTORIAL] Haciendo un ahorcado en C", en la sección C / C++". Donde tal y como se dice se crea un ahorcado y se explica paso a paso.
De todas formas ya iré poniendo ejemplos aquí también.
Seguiré escribiendo cuando pueda, espero lo entendáis. No quiero ver ningún post de que el tutorial está incompleto por favor.
Del mismo modo, también espero no ver ningún comentario diciendo que esto no sirve para hacer homebrew, y que quiere aprender algo rápido y ya. Si fuerais a hacer esto, leeros la introducción.
pedazo tuto, que currado se entiende a la perfeccion para gente que no entendia del tema, como yo.

