¡Esta es una revisión vieja del documento!
\n, nueva línea.\t, tabulación.\0, fin de línea.%d, insertar un int pasado por parámetro a la función donde se utiliza.%f, insertar el double\float pasado por parámetro en la función donde se utiliza.%1.xf, (siendo x un número) insertar el double\float pasado por parámetro en la función donde se utiliza mostrándolo con x decimales.\“, comillas.\\, barra.for (int i=0; i<10: i++) cout << ++i << endl; /* out: 1 3 5 7 9 */
for (int i=0; i<10: i++) cout << i++ << endl; /* out: 0 2 4 6 8 */
En un for podremos meter varios apartados separados por comas:
for(cin >> num, a=0; a<num; a++, cin >> x >> y)
Al enviar una referencia de este tipo queda como si fuese un tipo indeterminado. Luego podremos usar los valores que contiene de la siguiente forma:
void metodo (void *a, void *b) { cout << (*(int*)a + *(int*)b); }
La idea es poder almacenar la dirección de memoria de una función en un puntero, luego este puntero puede ser utilizado como si de una función se tratase. Para que esto pueda llevarse a cabo correctamente, la función asignada debe devolver y recibir lo indicado en el puntero.
typedef void (*funcPointer) (int); void myFunc (int x) { printf( "%d\n", x ); } int main() { funcPointer fp = &myFunc; fp(3); return 0; }
En el código anterior se crea un tipo nuevo llamado funcPointer, corresponde a la dirección de memoria de una función cualquiera que devuelva void y reciba por parámetro un int. En el main se asigna la dirección de myFunc, una función que cumple los requisitos, a una variable funcPointer, luego, esta puede ser utilizada como si fuese una función.
#include <stdio.h> void my_int_func(int x) { printf( "%d\n", x ); } int main() { void (*fp)(int); fp = &my_int_func; fp( 2 ); // equivale a: (*fp)( 2 ); return 0; }
Devuelve el valor en bytes (como unsigned int) que tiene reservado un putero:
int i = 4; int *c = i; cout << _msize(c) << endl; // Devolvería 4 en tipo size_t
#include <sys/time.h> void makeDelay (int mSeconds) { timeval systime1, systime2; gettimeofday(&systime1, NULL); gettimeofday(&systime2, NULL); while ((systime2.tv_usec - systime1.tv_usec) < mSeconds) gettimeofday(&systime2, NULL); }
Podemos emular enumeraciones de C# creandolas dentro de clases:
class D { public: enum Prueba{Lunes, Martes}; } ... D d; d.Lunes;
// Declaración struct sDisp {...} sDisp Stars[numDisp]; // Envio por parámetro sDisp* Temp; Temp = ConfDisp(Stars); // Cabecera de la función sDisp* ConfDisp (sDisp *Strellas) { ... return Strellas; } // Recorrido del array devuelto for (int i = 0; i<numDisp; i++) Stars[i] = Temp[i];
(También llamadas Bitwise Operations)
C no nos permite trabajar directamente con bits, pero sí que contiene operadores para manipular este tipo de datos. Cuando necesitemos trabajar en binario podemos declarar “flags”, estos pueden ser representados como BYTE, WORD o DWORD que contienen internamente estos 1 y 0.
Una DWORD corresponde a una Double WORD, es decir 4 bytes; internamente los datos se almacenan en bytes (8 bits), una WORD son 2 bytes. En C existen estos tipos de datos: BYTE, WORD y DWORD.
Generalmente, cuando se trabaja con bits los valores los expresamos en hexadecimal, valores en base 16 (en C esto es muy sencillo, por ejemplo a un int puedes asignarle un valor hexadecimal (int i = 0x43a;)). De esa forma se simplifica muchísimo la tarea, por ejemplo fíjate en la siguiente tabla:
0000 0 0001 1 0010 2 0011 3 0100 4 0101 5 0110 6 0111 7 1000 8 1001 9 1010 A 1011 B 1100 C 1101 D 1110 E 1111 F
A la izquierda están los posibles valores de cuatro bits, a la derecha, con un sólo “carácter”, cómo podemos expresarlo en hexadecimal. Ahora el truco consiste en dividir el byte en dos grupos de 4 bits. Por ejemplo el número 114, en 8 bits es 01110010; en la tabla: 0111=7 y 0010=2, por lo tanto el valor en hexadecimal es 0x72.
Al comparar dos valores, en una posición si los dos bits son 1, el resultado será 1; si no 0.
1 & 1 == 1 1 & 0 == 0 0 & 1 == 0 0 & 0 == 0
De los valores comparados el resultado será 1 si alguno de los dos tiene 1. Si, en cambio, los dos son 0 el resultado será 0.
1 | 1 == 1 1 | 0 == 1 0 | 1 == 1 0 | 0 == 0
Imaginemos que tenemos definido las siguientes posiciones:
#define POS_ARRIBA 0x1 // 0001 #define POS_ABAJO 0x2 // 0010 #define POS_DERECHA 0x4 // 0100 #define POS_IZQUIERDA 0x8 // 1000
Si luego seleccionamos una posición con una combinación de las anteriores:
BYTE posicion_seleccionada = POS_ARRIBA | POS_IZQUIERDA;
Luego podremos comprobar qué combinación ha sido la seleccionada:
if (posicion_seleccionada & POS_ARRIBA) ... if (posicion_seleccionada & POS_ABAJO) ... if (posicion_seleccionada & POS_DERECHA) ... if (posicion_seleccionada & POS_IZQUIERDA) ...
O uno o otro, eso es lo que significa. Al operar dos bits asignará en el resultado 1 si uno de los dos, sólo uno contiene 1, si no 0.
1 ^ 1 == 0 1 ^ 0 == 1 0 ^ 1 == 1 0 ^ 0 == 0
Invierte el valor, es decir, convierte los 1 en 0 y los 0 en 1.
BYTE:
00000011 = 0x03 = 3
11111100 = ~0x03 = 252
WORD:
0000000000000011 = 0x03 = 3
1111111111111100 = ~0x03 = 65532
Podemos asegurarnos que un bit tiene un valor concreto mediante la combinación de AND y del Complemento a uno.
Por ejemplo queremos hacer que el último bit de b sea 0 en r:
BYTE b = 0x03; BYTE c = 0x01; BYTE r = b & ~c;
Lo que se ha hecho:
00000011 = 0x03 = b
00000001 = 0x01 = c
11111110 = ~0x01 = ~c
00000011 = b
AND 11111110 = ~c
-------------
00000010 = r = b & ~c
Desplazan a la derecha o a la izquierda el número indicado de bits.
00011100 = c = 0x1c = 28 00111000 = c << 1 = 0x38 = 56 00000111 = c >> 2 = 0x07 = 7 00000011 = c >> 3 = 0x03 = 3
No podemos definir definir valores de menos de un byte, sin embargo, utilizando estructuras podemos asignar porciones de un byte asignandolas por bits:
struct date_struct { BYTE day : 5, // Indica que son los 5 últimos bits, pudiendo asignar valores menores que 31 month : 4, // Indica que son los 4 siguientes bits, pudiendo asignar los valores menores que 12 year : 14; // Indica que se cogerán los siguientes 14 bits, permitiendo un valores menores que 9999 } date;
El byte, pues, estaría repartido de la siguiente forma:
|0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0| | | | | +------ year ---------------+ month +-- day --+
Y ahora podemos utilizar la estructura en nuestro código:
date.day = 12; dateptr = &date; dateptr->year = 1852;
El operador XOR permite intercambiar los valores de dos variables con sólo tres expresiones. Por ejemplo, teniendo x = 1 e y = 5:
x = x ^ y; // x = 1 XOR 5 -> x = 4 y = y ^ x; // y = 5 XOR 4 -> y = 1 x = x ^ y; // x = 4 XOR 1 -> x = 5
Estas expresiones se resumen en la siguiente:
x^=y^=x^=y;
Existe una variación utizando únicamente sumas y restas.
(n>0) && ((n&(n-1))==0)x = y * 8; es lo mismo que x = y << 3;. O x = y * 32768; es lo mismo que x = y << 15;.x = y / 32; es lo mismo que x = y >> 5;A veces es necesario impedir que se copien objetos de una clase, el ejemplo más claro es el de la clase que contiene una conexión de red. Las clases no-copiables son aquellas que tienen privados el contructor de copia y el operador de asignación.
class MyClass { public: int data; MyClass () { data = 0; } private: MyClass (const MyClass &); MyClass & operator = (const MyClass &); };
Ahora el siguiente código dará error:
MyClass b; MyClass c = b;
Cuando trabajamos en C\C++ y mantenemos un código organizado en distintos ficheros (definiciones en .h y código en .cpp independientes por clases) podemos encontrarnos con pequeños errores que pueden traernos grandes dolores de cabeza, aquí los más comunes:
El error más básico, símplemente es necesario añadir un #include al archivo .h donde se encuentra la declaración. Aún así pueden aparecer nuevos problemas como definiciones de distintos tipos pero con igual nombre en distintos .h, lo mejor en este caso es revisar el orden de los #include.
Otro error es cuando un .h incluye otro .h, entonces los .cpp que incluyan al primero tienen incluido por defecto el segundo, si por alguna razón se decide borrar la inclusión del segundo .h en el primero el fichero .cpp tendrá grandes posivilidades de no compilar, la clave es incluir explícitamente los .h que necesites para cada fichero en cada uno de los .cpp necesarios.
Imaginemos el caso de dos clases, una padre que ha de almacenar la clase hijo y una hijo que ha de almacenar la clase padre:
/* Parent.h */ #include "child.h" class Parent { Child* theChild; }; /* Child.h */ #include "parent.h" class Child { Parent* theParent; };
El padre necesita de la declaración del hijo y el hijo de la del padre, pero cuando se está compilando el hijo se hace una inclusión de la cabecera del padre que a la vez incluye la del hijo y el compilador acaba haciendose la picha'un lío. Si eliminas alguno de los include el compilador saltará indicando que el identificador que se necesita no se ha declarado.
Es entonces cuando necesitamos hacer uso de una “declaración posterior” (fordward declaration):
/* Parent.h */ class Child; /* Forward declaration of Child; */ class Parent { Child* theChild; };
using namespace en el archivo .h ya que reduce la efectividad de estos, lo mejor es utilizar el using en los ficheros .cpp y colocar el nombre completo en los .h.const o inline en sus respectivos casos necesarios. Pero sobretodo evita añadir las macros a los .h, en vez de eso, si no puedes esquivarlas, definelas en los mismos .cpp.Ya existen artículos sobre elementos de la std:
Aún así, quedan elementos por ver:
Los hashmaps y diccionarios de Java tienen su equivalente en C++ en la clase std::map<tipo1, tipo2>.
En 'stdlib.h' encontramos esta función que ordena un array usando el argoritmo “quicksort”, una vez llamada te deja el array ordenadito.
Su definición es la siguiente:
void qsort ( void * base, size_t num, size_t width, int (*fncompare)(const void *, const void *) );
Por lo que se le han de pasar los siguientes parámetros:
base → Sería el array en cuestiónnum → Sería el número de elementos a ordenarwidth → Sería el tamaño de memoria que ocupa un elementofncompare → Es una función que debemos codificar que compare dos elementos del array y ha de ser con la declaración siguiente:int fncompare (const void * elem1, const void * elem2 );
Esta función debe devolver <0 si elem1 va antes que elem2, 0 si son iguales y >0 si elem1 va después que elem2.
Ejemplo:
int values[] = { 40, 10, 100, 90, 20, 25 }; int compare (const void * a, const void * b) { return ( *(int*)a - *(int*)b ); } int main () { int * pItem; int n; qsort (values, 6, sizeof(int), compare); for (n=0; n<6; n++) { printf ("%d ",values[n]); } return 0; }
(fullscreen) ? windowed () : fullscreened (); // Si fullscreen es verdadero se llamará a windowed, si no a fullscreened.
continue corta la iteración actual del bucle y continua con la siguiente. El código siguiente imprime los números del 0 al 9 saltándose el 5:for (i=0; i<10; i++) { if (i == 5) continue; printf("%d",i); }