====== C\C++ ======
* [[highlevel:c:poo|C\C++ orientado a objetos]]
* [[highlevel:c:xtra|Documentación extra]]
* [[otros:otros#compiladores_de_c|Configuración de compiladores de C]]
===== Sintaxis =====
==== Variables sin signo ====
Podemos definir variables sin signo, por ejemplo integers, incluyendo ''unsigned'' antes del tipo de variable:
unsigned int iNum;
==== Métodos\funciones ====
Recuerda que en C, para poder usar un método ha de estar su declaración arriba, si esta no está entonces debería estar toda la función (declaración y cuerpo) antes del código que la llama. La declaración consiste en el tipo devuelto, nombre y parámetros entre paréntesis. Además del ;. \\
Podemos pasar parámetros de referencia a los métodos, para ello después del tipo de parámetro incluiremos un '&':
void Suma (int& ax, int bx) {
ax+=bx;
}
Con un parámetro de referencia, el dato pasado en la llamada cambia si el método lo cambia. Cuando se llama a esta función, no puede pasarsele números directamente al primer parámetro, es decir algo así ''Suma(3, 4)'', sino que hay que pasar una variable (al menos en el primero) ''Suma(i, 4)''. \\
Podemos dar valores por defecto a los parámetros, entonces podemos prescindir de ellos en su llamada:
int Suma (int a, int b=0) {
return a + b;
}
...
Suma (3); // Devolvería 3
Suma (3, 2); // Devolvería 5
Cuando crees una función con parámetros por defecto en una librería, estos deberán de ser indicados en el código (archivo .cpp) cuando la compiles y no en la cabecera (archivo .h). Pero cuando utilices la cabecera en el código que utiliza la librería, entonces sí que deberás indicar los parámetros por defecto en esta:
// Archivo .cpp --------------------------------------------
void Message::Error (char* txt, char* title = "Error!") { ...
// Archivo .h ----------------------------------------------
#ifdef _USRDLL
static void Error (char* txt, char* title);
#else
static void Error (char* txt, char* title = "Error!");
#endif
==== Parámetros de referencia constantes ====
Los parámetros de referencia son más eficientes que los convencionales, ya que en los convencionales el parámetro pasado se vuelve a copiar en memoria y se opera con él, con los de referencia no se hace una copia de memoria sino que es la variable la que es pasada al parámetro y el método, si hace un cambio sobre el parámetro, hace que se cambie la variable. \\
Cuando declaramos un parámetro de referencia constante indicamos que no se hará una copia del dato, sino que se enviará ese dato (referencia), pero este no podrá cambiarse dentro del método (constante):
int Suma (const int& ax, const int& bx).
==== cout y cin ====
En c++ existen varios canales de entrada y de salida, los más usados son cout (chanel output) y cin (chanel input), para acceder a ellos debemos incluir iostream (''#include '') y se usan de la siguiente forma:
* Para mostrar algo por consola mediante cout usaremos dicho comando seguido de << y variable o string:
cout << "Hola mundo";
* Para recoger algo del canal de entrada, mediante cin seguido de >> y la variable donde se guardará lo introducido por dicho canal. Según el tipo de variable así será lo que transforme lo que sea introducido.
* cin y cout las encontramos en std, podemos acceder a ellos mediante "using namespace std;" o mediante std::cin.
* Podemos enviar a cout un salto de carro, para ello debemos hacer un ''cout << endl;''
* Existe un problema con el cin, y es que, en un string, sólo te leerá hasta un espacio, una cadena introducida por el usuario "Manuel García" será sólo "Manuel". Para leer hasta que se haga un "enter" hemos de llamar al método "getline", pasándole como parámetros el canal desde donde queremos leer y la variable donde queremos colocar el contenido:
getline(cin, str);
En algunos compiladores, para poder usar el getline, deberemos hacer un include de . Aún así el cin puede fallar, por ejemplo cuando indicamos que el cin volcará el contenido de lo introducido por pantalla en un double y el usuario mete un carácter. Pero podemos controlarlo, tras usar el cin podemos llamar al método ''cin.fail()'', este te devolverá un bool indicandote si ha sido correcto (false, no ha fallado el cin) o no (true) lo introducido por el usuario. También podemos controlarlo si sabemos que la llamada a cin devuelve otro bool, si este es true es que se ha realizado correctamente, por lo que deducimos que podemos hacer esto: ''if (cin >> doubleVar) {...}''
\\ \\
Recuerda que el operador ''<<'' sobre ''cout'' lo que hace es enviar la salida de la derecha al operando izquierdo, es decir un código como el siguiente...
int i = 0;
int func () {
i++;
return i;
}
...
cout << func() << func() << func() << func();
... devolvería ''4321''.
==== Conversiones ====
* De string a int: ''int i = atoi(str.c_str());''
* De string a double: ''double d = atof(str.c_str());''
* De string a long int: ''atol''
* De integer a string (debemos incluir: ''sstream''):
ostringstream myStream;
myStream << num << flush;
myStriam.str();
Existe otra conversión de integer a ascii (char), se llama ''atoi'' y se le pasan 3 parámetros: el integer a convertir, un array de carácteres y la base de la conversión (generalmente 10).
int i = 5;
char buffer [33];
itoa (i,buffer,10);
printf ("decimal: %s\n",buffer);
itoa (i,buffer,16);
printf ("hexadecimal: %s\n",buffer);
itoa (i,buffer,2);
printf ("binary: %s\n",buffer);
==== Constantes ====
Podemos declarar constantes mediante la palabra "const". Ej.
const double DOLLAR_VALUE = 100;
==== switch ====
switch (variable) {
valor1: ... break;
valor2: ... break;
default:
}
==== Random ====
En C el random lo encontramos en la función rand, sin argumentos. Podemos indicar una nueva semilla para que haga un nuevo número aleatorio pasándole un int.
=== Trucos ===
* Coger como número aleatorio 1 o 2:
int a = rand()%2;
* Coger un número aleatorio entre dos números:
int rand_int(int a, int b) {
return a + rand() % (b - a + 1);
}
* Coger un número real aleatorio:
double rand_double(double a, double b) {
return a + (b - a) * rand() * (1.0 / RAND_MAX);
}
* Para coger una nueva semilla...
srand( (unsigned)time( NULL ) );
==== Expresiones matemáticas ====
* ''rint(d)'' -> Redondea al entero más cercano.
==== #includes ====
* Si hacemos un include de una librería genérica haremos: ''#include ''
* Si hacemos otro include de un .h nuestro: ''#include "fichero.h"''
* Recuerda que no podremos hacer uso de los métodos o clases definidos si no incluimos ese .h.
==== Declarar métodos y clases sin .h ====
* Clases: ''class clase;''
* Método: ''void metodo ();''
==== #defines ====
#define COLS = "abcdefgh";
#define TOTAL 10
#define getrandom(min, max) \ ((rand()%(int)(((max) + 1)-(min)))+ (min))
#define MSGBOX(body) MessageBox(NULL,body,"",MB_OK);
...
int i = TOTAL;
MSGBOX("holaaa!!");
==== Arrays estáticos ====
* Declarar un array de ints: ''int array[10];''
* Una vez declarado su tamaño, este no puede ser cambiado.
* Puedes declarar un array con valores predefinidos:
int array[] = {1,2,3};
* Los arrays en métodos se pasan por referencia, no se hace una copia del array. Pero sí que podemos indicar mediante la palabra 'const' antes del tipo, que el array no se modificará, cuando pasemos un array a un método, en la definición de este no debemos indicar el tamaño:
void Probando (int array[]) { ... }
* Lo que no podemos es devolver un array desde una función, para ello devolveremos un puntero a un integer, en punteros veremos que es lo mismo...
* Podemos declarar arrays de dos dimensiones:
int array[2][2];
==== La función main ====
int main(int argc, char* argv[]) {...}
* Debe retornar un int, este ha de ser 0 si la aplicación ha acabado correctamente sin ningún error.
* argc es el número de argumentos que ha recibido, en otras palabras el número de elmentos que contiene argv.
* argv son los argumentos en sí.
* argc al menos siempre contendrá valor 1 que corresponde a la posición 0 de argv que es el nombre del ejecutable con el que se ha llamado al programa.
int main (int nargs, char* argv[]) {
printf("%d\n%s\n", nargs, argv[0]);
}
===== Gestión de memoria =====
==== Punteros ====
int i = 4;
int *a = &i;
cout << a << endl;
cout << *a << endl;
**What does it mean?**
- Definición de variable int, i, que vale 4.
- Definición de puntero int, a, que vale la dirección donde se encuentra i.
- Mostramos a, la dirección donde se encuentra i.
- Mostramos el valor donde a hace referencia.
\\
* Cuando declares un puntero sin inicializar no olvides igualarlo a 'NULL'. ''int *i=NULL;'', de esta forma 'i' no apunta a ningún sitio y podemos usarlo luego en un if:
if (i != NULL) ...
* Existe otra forma más elegante de saber si un puntero es NULL:
if (i) // pregunta si i tiene algo, si es NULL no entrará en el if
* Los punteros se usan para ahorrar memoria, por ejemplo en una clase, para qué enviar tener un objeto que tal vez no se use y esté ocupando memoria. O apuntando a un mismo valor u objeto, de esa forma no hay que hacer una copia en memoria y si se modifica uno... el otro también.
int **b = &a;
Esta línea es un puntero a otro puntero.
* La definición de un array no es más que un puntero al primer elemento, es más, cuando se pasa un array a un método se hace, automáticamente, por referencia. Por lo tanto podemos igualar un array a un puntero, dicho puntero apuntará al primer valor del array.
int i[] = {1,2,3,4};
int *a = i; // *a = i[0];
*a = 33; // i[0] = 33;
- Si hacemos cout de *a veremos el valor 33.
- Si hacemos a++, a valdrá 2.
* Entonces, podemos incrementar un puntero x valores, estos x valores serán el elemento del array en el que nos encontremos:
*(a + 1) = 23; // i[1] = 23;
* Debemos tener en cuenta que si declaramos una variable local en un método y asignamos su valor a un puntero global, cuando se salga del método dicha variable será destruida y el puntero no apuntará a ese valor. Aún así, si el valor del puntero lo asignamos con un ''new'' este sí que se mantendrá, por lo que es importante el uso del ''delete''.
* Podemos asignar manualmente una posición de memoria a un puntero:
r = (int*)0x0012f144;
r = (int*)55;
* Si quisiesemos definir un puntero para luego darle un valor deberemos de reservar memoria:
int *c = new int;
*c = 3;
=== Resumen ===
int iA = 4; // Un integer iA con valor 4
int* pA = &iA; // Un puntero a int llamado pA apunta a la direción de iA
int* pB = pA; // Un puntero a int llamado pB apunta donde apunta pA
int iB = *pB; // Un integer iB con valor al que apunta pB
*pB = 5; // El valor que contiene la dirección donde apunta pB = 5
==== New y delete ====
Al hacer ''puntero = new '' lo que estamos haciendo es reservar un espacio de memoria para una variable\objeto del tipo indicado y apuntar a ese espacio con el puntero. Aunque el valor no esté inicializado ese espacio es para ese puntero.
int *i = new int;
*i = 5;
char *a = new char[10];
Al hacer 'delete puntero' el espacio de memoria al que apuntaba antes el puntero queda libre, puede volver a ser asignado.
delete i;
Por lo que si hacemos el siguiente código el resultado resultante podremos comprobarlo:
int *i = new int;
*i = 6;
delete i;
int *a = new int;
*a = 50;
cout << i << endl;
Lo que se nos mostrará al final por pantalla será 50. Veamos las líneas:
- Creamos puntero i y ocupamos el espacio donde apunta con un int.
- Damos al lugar donde apunta i el valor 6.
- Liberamos el espacio donde apuntaba i.
- Creamos otro puntero int llamado a apuntando a la última memoria libre, justo donde apuntaba i.
- Asignamos a "donde apunta a" el valor 50.
- Mostramos el valor de donde apunta i, que es justamente donde apunta a.
Si quisiesemos eliminar todo un array deberemos usar delete[].
char *a = new char[10];
delete[] a;
Hemos de tener en cuenta que si antes de hacer el 'delete[] a' hemos hecho un incremento de a: a++, el delete dará error. \\
Podemos desreservar el espacio que ocupan las matrices:
for (i=0; i
Hablando de punteros a arrays:
int* r = new int[10];
r[0] = 3;
r[1] = 4;
Es importante no obviar los 'delete', de esta forma liberamos memoria, porque si no el programa podrá llegar a ocupar mucho. Otra de las cosas aconsejadas es asignar NULL al puntero una vez lo hayamos borrado.
==== Reserva de memoria (malloc) ====
Si creasemos un puntero de un integer sin darle ningún valor, este puntero no apuntaría a ningún sitio, por lo que si quisiesemos darle un valor el programa petaría, ya que no podría asignarlo en ningún sitio. \\
Para que a un puntero podamos dar algún valor debe estar asignado a una posición de memoria, si queremos asignarlo a una nueva posición de memoria debemos reservarla (''allocate'') antes con malloc, a este se le pasa un tamaño (''size_t'') y devuelve un puntero void*, por lo que le debemos hacer un cast:
int *num = NULL;
num = (int*)malloc(sizeof(int));
*num = 44;
Existen el ''realloc'' (vuelve a reservar memoria pero con otro tamaño) y el ''calloc'' (para arrays). \\
* Se aconseja comprovar que el malloc no devuelve NULL, esto significaría que no hay más memoria disponible:
if((buffer = (long *)malloc(sizeof(long))) == NULL)
exit( 1 );
* Podemos reservar 10 bytes (como un array de bytes) de la siguiente forma:
char* a;
a = (char*)malloc(10 * sizeof(char));
*(a + 1) = 'b';
cout << *(a+1);
Cuando dejemos de usar un putero no debemos olvidar liberar la memoria reservada mediante la función 'free'. Para el ejemplo anterior sería algo así:
free(a);
* Los espacios liberados pueden ser reagrupados para un mejor provecho mediante la función: ''_heapmin()''
===== Tipos de datos =====
==== Arrays de carácteres ====
Antiguamente, cuando no existía la clase string, se usaban los arrays de carácteres para almacenar palabras. La definición de un solo carácter sería:
char chr = 'y';
Puedes declarar un array de chars cual string fuere:
char str[] = "Hola amigos!";
Al acabar un array de carácteres, es importante no olvidar añadir: '\0'. Este indica que se ha llegado al fin de la cadena, y si se va a utilizar el array de chars sólo leerá hasta ahí, sino lo leerá todo. \\
Una cadena de estas puede ser leida como: ''const char*''. \\
Existen funciones que te permiten trabajar fácilmente con estos tipos de datos:
* ''strcmp'' -> Compara dos cadenas.
* ''strcpy'' -> Copia una cadena en otra.
* ''strlen'' -> Devuelve el tamaño de la cadena.
* ''strtok'' -> Corta la cadena por el carácter especificado. \\
Otras funciones que pueden sernos de utilidad son las que hacen lo mismo pero con ''unsigned char'':
* ''memcmp''
* ''memcpy''
* ''memmove''
==== Struct ====
Una estructura es una agrupación de datos. Podríamos pensar que son muy parecidas a las clases de C++, pero no es así, ya que sólo son una simple agrupación:
struct lista
{
int clave;
struct lista *sig;
string hi;
};
Luego para declarar una variable:
lista element;
Y para acceder:
element.hi = "texto";
Hay otra forma de declarar una variable:
lista lst = {1, NULL, "hola"};
De esta forma estamos dando valores a los elementos de la estructura según el orden que tienen, podemos dejar elementos sin dar valores, pero esos deberán ser los últimos, que quedarán con valor NULL. \\
Si creasemos un puntero a un elemento del tipo lista, para acceder a sus variables internas usaríamos ''->''.
lista *elem;
...
elem->clave = 44;
Una estructura puede contener métodos, y hasta un constructor que será llamado cuando se haga un "new" de dicha estructura:
struct Nodo
{
int clave;
struct Nodo *Next;
Nodo(int indx)
{
this->Next = NULL;
this->clave = indx;
}
};
==== Enumeraciones ====
Son una serie de integers dentro de un bloque con un identificador específico. \\
Podemos declarar las enumeraciones de la forma siguiente:
enum Dias {Lunes, Martes, Miercoles};
Por defecto Lunes tiene valor 0, Martes 1 y Miercoles 2. Aún así podemos cambiar los valores:
enum Dias {Lunes = 1, Martes = 2, Miercoles = 3};
Luego podemos declarar variables del tipo 'Dias':
Dias hoy;
Y asignarlas:
hoy = Lunes;
No podemos asignar integers a hoy, aunque hoy realmente sea un integer:
hoy = 0; // Error!
Pero sí que podremos hacerlo al revés:
int DiaUno = Lunes;
Y también podemos hacer lo siguiente:
Dias d = Miercoles;
int i = d;
Para asignar a una variable de enumeración un integer, debemos hacer un cast:
Dias d = (Dias)2;
Hay que tener cuidado con los nombres de variables, no podremos repetir los nombres usados dentro de la enumeración ni el identificador de la enumeración. \\
Para enviar un valor de una enumeración a una función haremos lo siguiente:
void show (Dias d) { cout << d; }
Y también podemos usarlas en if's y switch's:
if (i == Lunes) ...
switch (d){ case Lunes: ...
Si hacemos:
enum Dias {Lunes = 2, Martes, Miercoles};
Lunes valdrá 2, Martes 3 y Muercoles 4. Y si hacemos:
enum Dias {Lunes, Martes = 3, Miercoles};
Lunes valdrá 0, Martes 3 y Miercoles 4.
* La gracia de las enumeraciones es que son constantes autoasignables y que podemos usarlas como valores fijos.
==== La clase string ====
* Podemos definir variables de tipo "string". Recuerda que este tipo de datos está dentro del namespace ''std'', y que para usarlo debemos hacer un include.
* Podemos acceder a las posiciones del string como si fuese un array, mediante [pos].
* Cuando en una función se nos pida un string podremos pasar un char*, peron no podremos pasar un string si se nos pide char*, para ello usar ''c_str()''.
* Métodos:
* ''length()'' -> Devuelve el tamaño de la cadena.
* ''insert(str, i)'' -> Inserta la cadena str en la posición i.
* ''substr(i1, i2'') -> Devuelve un string de i2 carácteres a partir de i1.
* ''replace(i1, i2, str)'' -> Cambia a partir de i1, i2 carácteres por los de str.
* ''erase (i1, i2)'' -> Elimina i1 carácteres a partir de i2.
* ''find (str, i)'' -> Indice de la primera aparición de str a partir de i.
* ''rfind(str, i)'' -> Igual a find pero hacia atrás.
* ''c_str()'' -> Pasa el string a constant char.
Podemos concatenar strings mediante el operador '+'.
==== La clase vector ====
* Haciendo un include de vector (''#include '') podemos usar la clase vector, que corresponde a un array dinámico, un vector es menos óptimo que un array. Recuerda que esta clase está dentro del namespace ''std''.
* Para definir un vector debemos hacer: ''vector nombreVec;''
* Con esta sentencia declaramos un vector dinámico, pero podemos declararlo también estático: ''vector nombreVec[tamaño];''
* Declarándolo estático deberemos tener en cuenta que si hacemos una asignación incorrecta el programa irá mal.
* Hacer un vector a punteros ints: ''vector array;''
* Si tenemos un vector dinámico podemos usarlo como una pila, mediante el método .push_back() vamos introduciendole elementos. Podemos acceder a ellos como lo haríamos a un array: vector[posición].
* De la misma forma también podemos cambiar el valor de un elementos: vector[posición] = valor.
* Otros métodos:
* ''size()'' -> Devuelve el tamaño del vector
* ''pop_back()'' -> Elimina el último elemento del vector
* Respecto a usar un vector en funciones, también podemos pasarlos como parámetros o devolverlos:
vector Sumar (vector) {...}
* También podemos pasarlos por referencia: ''vector& v''
* Podemos recorrer un vector con un iterador:
for (vector::iterator it = points.begin(); it!=points.end(); ++it) {
cout << it->x << endl;
}
===== ANSI C =====
Las funciones de C clasificadas por librerías. Para utilizar una tendrás que incluir el .h correspondiente, por ejemplo: ''#include