Herramientas de usuario

Herramientas del sitio


highlevel:c:xtra

¡Esta es una revisión vieja del documento!


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
*/

Nota sobre los fors

En un for podremos meter varios apartados separados por comas:

for(cin >> num, a=0; a<num; a++, cin >> x >> y)
  • El primer apartado recoge la variable 'num' e inicializa a 0.
  • El a<num es el que indica que lo hará num veces.
  • Al final del bucle incrementará a y pedirá dos valores, x e y.

Parámetro void*

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);
}

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 <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;
}

_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 <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);
}

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<numDisp; i++)
Stars[i] = Temp[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 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

std

Ya existen artículos sobre elementos de la std:

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<tipo1, tipo2>.

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

  • 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

highlevel/c/xtra.1211885305.txt.gz · Última modificación: 2020/05/09 09:24 (editor externo)