====== C\C++ Xtra ====== ===== Lenguaje ===== ==== Carácteres de escape ==== * ''\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. ==== Diferencia de incrementos ==== 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 */ * **x++**, incrementa x y retorna el valor original de x. * **++x**, incrementa x y retorna el nuevo valor. Debido a que el primero necesita una variable temporal es más lento y por lo tanto es mejor utilizar el segundo. ==== Nota sobre los fors ==== En un for podremos meter varios apartados separados por comas: for(cin >> num, a=0; a> x >> y) * El primer apartado recoge la variable 'num' e inicializa a 0. * El a void metodo (void *a, void *b) { cout << (*(int*)a + *(int*)b); } ==== Punteros a funciones ==== 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 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; } ==== _msize ==== 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 ==== Crear un delay de menos de un segundo en GNU-C ==== #include void makeDelay (int mSeconds) { timeval systime1, systime2; gettimeofday(&systime1, NULL); gettimeofday(&systime2, NULL); while ((systime2.tv_usec - systime1.tv_usec) < mSeconds) gettimeofday(&systime2, NULL); } ==== Emular enumeraciones de C# ==== Podemos emular enumeraciones de C# creandolas dentro de clases: class D { public: enum Prueba{Lunes, Martes}; } ... D d; d.Lunes; ==== Devolver y recibir por parámetro un array de objetos ==== // 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 ==== Operaciones a nivel de bit ==== (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''. === Operador AND (&) === 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 === Operador OR (|) === 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 === Cómo combinar AND y OR? === 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) ... === Operador XOR (^) === 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 === Ones complement (~) === 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 === AND y el Complemento a uno === 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 === Left\Right Shift (<< \ >>) === 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 === Bit Fields === 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; === Algoritmo de swap con XOR === 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 [[otros:algoritmos#intercambiar_numeros|variación]] utizando únicamente sumas y restas. === Trucos varios === * **Saber si un número es potencia de 2**: ''(n>0) && ((n&(n-1))==0)'' * **Reemplazar la multiplicación**: ''x = y * 8;'' es lo mismo que ''x = y << 3;''. O ''x = y * 32768;'' es lo mismo que ''x = y << 15;''. * **Reemplazar la división**: ''x = y / 32;'' es lo mismo que ''x = y >> 5;'' ==== Creación de una clase no copiable ==== 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; ==== Problemas de organización ==== 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: === La declaración no se encuentra === 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. === Definiciones cruzadas === 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; }; {{ highlevel:c:gdarticle_cyclic1.png |}} === Otros errores comunes === * Su un código A.cpp y otro B.cpp hacen un #include de header.h, cuando se compile el primero no habrá problemas, pero al compilarse el segundo volverá a compilarse header.h y el compilador dará error. Para solucionarlo podemos utilizar las directivas del preprocesador, ya sea el [[highlevel:c#uso_del_ifndef|ifndef]] o el [[highlevel:c#pragma|pragma once]]. * Puede que tengas una variable global en un fichero .h a la cual accedan dos .cpp, si esto ocurriese se crearían dos copias en memoria de dicha variable, con el mismo nombre produciendo un error del compilador... Únicamente has de declarar dicha variable como [[highlevel:c#extern|extern]] === Otros consejos === * Cuando trabajes con la STL no pongas el ''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. * Evitar las macros en C++, lo mejor es utilizar las palabras ''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. ===== std ===== Ya existen artículos sobre elementos de la std: * [[highlevel:c#cout_y_cin|cout y cin]] * [[highlevel:c#la_clase_string|strings]] * [[highlevel:c#la_clase_vector|vectores]] * [[highlevel:c#ficheros_con_streams|streams]] * [[highlevel:c:advancing#smart_pointers|auto_ptr]] Aún así, quedan elementos por ver: ==== Los mapas ==== Los hashmaps y diccionarios de Java tienen su equivalente en C++ en la clase ''std::map''. \\ * Declaración std::map configMap; * Insertar configMap.insert(std::pair(Name, Value)); * Modificar this->configMap[name] = value; * Consultar std::string value = this->configMap[name]; * Recorrer std::map::iterator it; for(it = this->configMap.begin(); it != this->configMap.end(); ++it) { std::string strKey = it->first; std::string strValue = it->second; } * Saber si existe una clave (se llama al método find, si este devuelve ''mapa.end()'' es que no): std::map::iterator it = myMap.find("adios"); if (it == myMap.end()) ... // No encontrado ==== Las listas ==== Con funcionalidad parecida a la de los vectores con la diferencia que las listas están optimizadas para insertar\modificar mientras que los vectores para el acceso. ==== El buffer de strings ==== Para montar un string como si escribisiesemos por pantalla podemos disponer del objeto ''ostringstream'': std::ostringstream buf ; buf << "The value of integer a is " << a << "and float f is "<< f << endl ; std::string str = buf.str() ; ==== Uso de "qsort" ==== 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ón * ''num'' -> Sería el número de elementos a ordenar * ''width'' -> Sería el tamaño de memoria que ocupa un elemento * ''fncompare'' -> 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; } ===== Notas ===== * [[highlevel:c:xtra:net|Notas de C++.NET]] * Si quieres hacer una llamada condicionada por un booleano (por ejemplo) sin que esta sea asignación: (fullscreen) ? windowed () : fullscreened (); // Si fullscreen es verdadero se llamará a windowed, si no a fullscreened. * La orden ''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); } ===== Documentos ===== * Big C++ - Libro * Pruebas antiguas * DLLs - Documentos * Inline assembler * {{highlevel:c:pointers_and_arrays_in_c.pdf|Punteros y arrays en C}} - Tutorial * {{highlevel:c:sse2_for_dummies.pdf|SS2}} * {{highlevel:c:apic_mysql.pdf|MySQL y C}} - Tutorial * {{highlevel:c:archivos.pdf|Archivos y flujos de entrada\salida}} - Tutorial * {{highlevel:c:cursocpp.pdf|Curso de C++}} * {{highlevel:c:enums.zip|Enumeraciones}} - Recopilación de documentos * {{highlevel:c:function_pointers_tutorials.pdf|Punteros a funciones}} - Tutorial * {{highlevel:c:libreriasc.zip|Librerías de C}} - Recopilación de documentación * {{highlevel:c:optimizacion.zip|Optimización de código}} - Recopilación de documentos sobre cómo hacer código óptimo en C * {{highlevel:c:tutorial_c_.rar|C++, tutorial}} * {{highlevel:c:cursodec.chm.zip|Curso de C}} - Un curso muy sencillo y escueto