Etiquetas

C (31) Cpp (28) Linux (14) asm (8) Telegram (5) bot (5) libreria (5) Algoritmo (3) Errores comunes (3) python (3) Opengl (2) kali (2) Android (1) Snippet (1) nano (1) recursividad (1)

domingo, 21 de junio de 2026

[C]Bruteforce Recursivo vs Iterativo


/*Me encontré este post en elhacker y lo comparto con uds... no es de mi autoría Hay casos en que es más conveniente usar la recursividad para solucionar un problema. Hay que cambiar system("PAUSE") y los scanf"*/

//////////////////////////////////////
/*BruteForce Iterativo en Lenguaje C*/
//////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]){
int contadores[50]={0}, len=0, cant=0, i=0;
char caracteres[200]={0}, pass[50]={0};
printf ("Ingrese los caracteres a usar para la contrasena: \n");
scanf ("%s", caracteres);
printf ("Ingrese la longitud de la contraseña: \n");
scanf ("%i", &len);
cant = strlen (caracteres);
/*contadores = (int *) malloc (sizeof (int) * (len+1) );
pass = (char *) malloc (len+1);*/
for (i=0 ; i<len;i++){
pass[i] = caracteres[0];contadores[i] = 0;}
contadores[i] = 0;pass [i] = '\0';
while (!contadores[len]){
printf ("%s\n", pass);
for (i=0;contadores[i]==cant-1;i++)
pass[i] = caracteres [contadores[i] = 0];
pass[i] = caracteres [++contadores[i]];}
/*free (contadores);free (pass);*/
system("PAUSE");return 0;} //end of main
vs Recursivo

//////////////////////////////////////
/*BruteForce Recursivo en Lenguaje C
*///////////////////////////////////////
#include <stdio.h>
#include <string.h>
void brute_force (char caracteres[], int cant, int pos, char *password){
if(pos==-1){printf ("%s\n", password);return;}
for(int i=0;i password[pos]=caracteres[i];
brute_force (caracteres, cant, pos-1, password);}
return;}

int main(int argc, char *argv[]){
char caracteres[256]={0}, *password=NULL;int longitud=0, cantidad=0;
double tiempo=0;time_t t1, t2;
printf ("Ingrese los caracteres a usar: ");
scanf ("%s", caracteres);
cantidad = strlen(caracteres); printf ("Ingrese la longitud maxima de la contraseña: "); scanf ("%i", &longitud); password = (char*) malloc (longitud*sizeof(char)); time (&t1);password[longitud]= '\0'; brute_force (caracteres, cantidad, longitud-1, password); time (&t2);tiempo = difftime (t2, t1); printf ("\tTiempo: %7.2f\n\n", tiempo); free (password); //importante:liberar memoria system("PAUSE");return 0;}

martes, 11 de marzo de 2025

Intro al Reversing

Ingeniería Inversa Definición por pancake aka trufae
 La ingeniería inversa sirve para obtener información de un producto, cómo ha sido construido o cómo funciona por dentro. 
 El objetivo de la ingeniería inversa es obtener información o un diseño a partir de un producto accesible al público, con el fin de determinar de qué está hecho, qué lo hace funcionar y cómo fue fabricado. 
Hoy en día los productos más comúnmente sometidos a ingeniería inversa son los programas. Sirve para: 
 Clasificar Malware 
Liberar drivers/hardware 
Analizar vulnerabilidades 
Hacker crackmes/ctf (Capture the Flag) 
Contabilidad con software Espionaje industrial 
 ¿Qué tipos de ejecutables existen? 
¿Todos se depuran de la misma forma? 
¿Por qué los ejecutables tienen un encabezado? 
¿Qué información hay en ese encabezado (header en inglés)? 
¿Cómo se analiza un virus y qué modifica? 
¿Cómo se modifica un programa? 
¿Cómo puedo depurar mi programa para buscar errores? 
Una vez que he aprendido a depurar, ¿cómo puedo mejorar la protección de mi software? Del Código Máquina al Lenguaje Ensamblador Cuando nos encontramos frente a una variante de algún código malicioso en un sistema y nos disponemos a analizarlo tenemos el archivo binario y debemos utilizar un desensamblador para generar el código en assembler con el objetivo de analizarlo. 
Ensamblador (assembler) es en realidad una clase de lenguaje de programación. Cada variante de ensamblador corresponde a una familia particular de microprocesadores tales como x86, x64, SPARC, PowerPC, MIPS o ARM. Dentro de todas estas familias la más habitual dentro las arquitecturas de procesadores es la x86, aunque con el pasar de los años vemos más y más procesadores x64. 

 Data: La sección de datos de un programa hace referencia a una región específica de memoria. Contiene lo que se conoce como las variables estáticas que no cambian con la ejecución del programa. También en esta sección se encuentran las variables globales, que están disponibles desde cualquier parte del programa. 

Code: En esta región de memoria se almacena el código que se ejecuta del programa donde se alojan todas las instrucciones que se van a ejecutar. 

Heap: El heap es una región de memoria que se utiliza para alocar nuevos valores durante la ejecución del programa como así también para eliminarlos una vez que se dejaron de utilizar. El heap es una memoría dinámica y su contenido varía a medida que se ejecuta el programa 

Stack (Pila): La pila se utiliza para alojar las variables locales, parámetros y valores de retorno de una función como así también contiene las direcciones de retorno entre una llamada a una función y otra, siendo muy útil para controlar el flujo de ejecución del programa. Cualquier software se ejecuta de forma secuencial, es decir una instrucción detrás de otra, la siguiente instrucción a ejecutar se almacena en un registro llamado IP, mediante programación se puede desviar esa ejecución hacia funciones que realicen tareas concretas, el programa en un principio ejecutará el flujo normal hasta que llega a una llamada a una función, en ese momento guarda en RAM el registro IP, ejecuta la función y retorna al flujo principal del programa porque fué capaz de leer el valor almacenado en RAM del registro IP.
Registros
Un registro es, de forma simplificada, un pequeño almacén en la CPU y es, evidentemente, la forma más rápida que tiene la CPU de acceder a datos.
En la arquitectura x86 de Intel existen ocho registros de propósito general: EAX, EDX, ECX, ESI, EDI, EBP, ESP y EBX (cambiar E por R en arquitecturas de 64 bits).
Son utilizados para facilitar la tarea en el procesado de las instrucciones, cómo para almacenar datos que se utilizaran posteriormente por las mismas.
El registro EAX (RAX)
El registro EDX (RDX)
El registro ECX (RCX)
Los registros ESI (XSI) y EDI (RDI)
Los registros ESP (RSP) y EBP (RBP)
El registro EBX (RBX)
Registro especial EIP (RIP)
Estos son alguno de los registros básicos que existen:
-EAX (Accumulator register): Utilizado tanto para realizar cálculos, cómo para el almacenamiento de valores de retorno en "calls".
-EDX (Data register): Extensión de EAX, utilizada para el almacenamiento de datos en cálculos más complejos.
-ECX (Count register): Utilizado en funciones que necesiten de contadores, como por ejemplo bucles.
-EBX (Base register): Se suele utilizar para apuntar a datos situados en la memoria.
-ESI (Source index): Utilizado para la lectura de datos.
-EDI (Destination index): Utilizado para la escritura de datos.
-ESP (Stack pointer): Apunta a la cima de la pila “stack”.
-EBP (Base pointer: Apunta a la base de la pila “stack”.

Instrucciones -Son acciones predefinidas en el lenguaje ensamblador. Algunas de las más habituales de ver son:
-PUSH: Guarda el valor en la pila.
-POP: Recupera valor de la pila.
-MOV (dst, src): Copia el operador “src” en el operador “dst”.
-LEA (reg, src): Copia una dirección de memoria en el registro destino (ej: EAX).
-ADD (o1, o2): Suma los dos operadores y los almacena en el operador uno.
-SUB (o1, o2): Resta el valor del segundo operador sobre el primero y lo almacena en el primer operador.
-INC: Incrementa en 1 el valor del operador indicado.
-DEC: Decrementa en 1 el valor del operador indicado.
-AND: El resultado es 1 si los dos operadores son iguales, y 0 en cualquier otro caso.
-OR: El resultado es 1 si uno o los dos operadores es 1, y 0 en cualquier otro caso.
-CMP: Compara dos operadores.
-JMP Salta a la dirección indicada.
-CALL: Llama/Salta a la dirección/función indicada.
-NOP: Not Operation.

Estructura de la pila (Stack)
La pila es una estructura FILO (First In, Last Out) donde los argumentos son apilados en la cima de la misma cuando se invoca una función y retirados cuando la función finaliza. El registro ESP se usa para seguir la pista a la parte más alta del marco de la pila y el registro EBP para seguirle la pista a la parte baja (aunque ya vimos que ciertos compiladores pueden decidir no usar ebp para eso). 
 La PILA o Stack es un conjunto de direcciones de memoria encargadas de almacena información de llamadas a funciones, variables locales, direcciones de retorno a funciones anteriores, entre otras tareas. La PILA es dinámica, por lo que va cambiando su tamaño dependiendo de la función a la cual se encuentre asociada, dispone de una estructura “First In, Last Out”, por lo que lo último que entra será lo primero en salir, delimitada siempre por su cima (ESP) y por su base (EBP).
Puntos de Interrupción (Breakpoints)
La habilidad de parar un proceso que está siendo depurado se consigue mediante el fijado de puntos de interrupción o breakpoints. Al parar el proceso podemos inspeccionar el valor de las variables, los argumentos de la pila, y las direcciones de memoria sin que el proceso modifique estos valores hasta que no se lo indicas de esa forma al depurador. Los puntos de interrupción son la herramienta principal proporcionada por los depuradores. Los puntos de interrupción le permiten interrumpir la ejecución del programa en un lugar específico. Hay dos tipos de puntos de interrupción:
Puntos de interrupción de software
Puntos de interrupción de hardware
Es muy difícil realizar ingeniería inversa de software sin puntos de interrupción. Las tácticas populares de ingeniería anti-reversa se basan en la detección de puntos de interrupción, proporcionando una serie de métodos anti-depuración correspondientes.
Cómo omitir una verificación de punto de interrupción de software
No existe un enfoque universal para evitar una verificación de punto de interrupción de software. Para evitar esta protección, debes encontrar el código que calcula la suma de verificación y sustituir el valor devuelto con una constante, así como los valores de todas las variables que almacenan sumas de verificación de funciones.
Puntos de interrupción de hardware
DR0-DR3 – breakpoint registers -“Debug Address Registers” or “Address-Breakpoint Registers”
DR4 - DR5 – reserved - “Reserved Debug Registers”
DR6 – debug status - "Debug Status Register”
DR7 – debug control -“Debug Control Register”

SEH (Structured Exception Handling)
El manejo estructurado de excepciones es un mecanismo proporcionado por el sistema operativo a una aplicación que le permite recibir notificaciones sobre situaciones excepcionales como la división por cero, la referencia a un puntero inexistente o la ejecución de una instrucción restringida. Este mecanismo le permite manejar excepciones dentro de una aplicación, sin la participación del sistema operativo. Si no se maneja una excepción, dará como resultado la finalización anormal del programa. Los desarrolladores suelen ubicar punteros a SEH en la pila, que se denominan marcos SEH. La dirección de trama SEH actual se encuentra en el desplazamiento 0 en relación con el selector FS (o el selector GS para los sistemas x64).
VEH (Vectored Exception Handler)
VEH se introdujo en Windows XP y es una variación de SEH. VEH y SEH no dependen el uno del otro y funcionan simultáneamente. Cuando se agrega un nuevo "manejador" VEH, la cadena SEH no se ve afectada ya que la lista de manejadores VEH se almacena en la variable no exportada ntdll! LdrpVectorHandlerList. Los mecanismos VEH y SEH son bastante similares, la única diferencia es que las funciones documentadas se utilizan para configurar y eliminar un controlador VEH.
Exploiting sobre Linux-x86
La porción de RAM utilizada por un programa cuando se llama a una función es comunmente llamada pila o stack, debemos tener en cuenta que la pila crece de arriba a abajo, como podemos ver hay una parte de la ram que almacena la copia del registro IP(EIP) en el momento de realizar la llamada a la función, en el programa no se tiene en cuenta que el dato introducido sea de una longitud concreta, se realiza la copia a ciegas a la variable nombre, aprovechando este descuido podemos hacer crecer la variable nombre hasta llegar a ocupar la dirección de retorno EIP haciendo así que el software termine retornando a otra posición de memoria y no la que se guardó al realizar la llamada a la función func.
¿Qué es una Shellcode?
Una shellcode no es mas que el conjunto de opcodes (instrucciones en hexadecimal) que ejecutará el procesador para realizar un acción en concreto, las shellcodes suelen estar escritas en ensamblador ya que nos permite un control total sobre el proceso de ejecución además de un tamaño inferior de la shellcode.

Definición Buffer
Un espacio de memoria de cierto tamaño que se reserva para guardar datos, y manejar los mismos.
Un ejemplo básico es una lata de 20 litros que tengo vacía para guardar allí un contenido, el mismo podrá ser menor o igual a 20 litros, el cual es el tamaño máximo que puedo guardar en este buffer de 20 litros, si quisiera guardar más en un solo depósito debería buscar la forma de tener un buffer más grande, sino, al tratar de guardar por ejemplo 40 litros en una lata de 20 litros se desbordaría. Un buffer overflow ocurre cuando un programa informático excede el uso de cantidad de memoria reservado para ello , escribiendo en el bloque de memoria contiguo.
En verdad, un buffer overflow se produce en una aplicación informática cuando no cuenta con los chequeos de seguridad necesarios en su código de programación, como por ejemplo medir la cantidad de datos que se copiara a un buffer y que no exceda el tamaño del mismo.
Los tipos más comunes de buffer overflows son los stack buffer overflows y los heap buffer overflows. Bueno aqui vemos la definición de buffer overflow, y en nuestro ejemplo anterior si trato de guardar 40 litros en una lata de 20 litros se desbordara como vimos, ese desborde que se produce es el buffer overflow, o sea el desbordamiento de mi depósito al sobrepasar la máxima capacidad del mismo.
Stack y heap overflow
Ahora una idea acerca de la diferencia entre stack y heap: STACK : El stack se utiliza para guardar las variables locales de una función que sólo necesitan durar tanto como la ejecución de la función. En la mayoría de los lenguajes de programación es fundamental que sepamos en tiempo de compilación qué tan grande es una variable si queremos almacenarla en el stack. HEAP: El heap se utiliza para reservar memoria dinámica, cuya vida útil no se sabe muy bien por adelantado, pero se espera que duren un tiempo. Si no sabemos su tamaño o el mismo se decide en tiempo de ejecución se deberá calcular y reservar en el heap.
Stack Buffer Overflow
In software, a stack buffer overflow occurs when a program writes to a memory address on the program's call stack outside of the intended data structure; usually a fixed length buffer.
Es decir, la vulnerabilidad Stack Buffer Overflow ocurre cuando una aplicación no controla correctamente el número de bytes que son almacenados en una dirección de memoria previamente reservada, de forma que la cantidad de bytes que se van a almacenar son superiores a los reservados. Nuestro principal objetivo es llegar a sobrescribir la dirección de retorno (almacenada en la PILA) con un valor que apunte a nuestra shellcode.
-Registro EIP (Extended Instruction Pointer): Este registro apunta a la siguiente dirección de memoria que el procesador va a ejecutar.
-Instrucción RETN: Es la instrucción encargada de recoger el valor de ESP y almacenarlo en el registro EIP, de este modo el valor de ESP será la próxima dirección de memoria que el procesador va a ejecutar.
-Dirección de retorno: Es el valor exacto que nos indica la dirección de memoria donde habíamos dejado la aplicación ante de entrar en una subfunción, para así cuando esta termine volver a la posición exacta donde nos quedamos. Este valor se encontrará almacenado en la siguiente dirección de memoria del EBP de la subfunción.

Anti-sandboxing y Anti-debugging
Debuggers como OllyDbg, WindDbg, Radare o IDA ya sabréis que se requieren conocimientos sobre lenguaje ensamblador y estructuras internas de ficheros.
Una técnica anti-debugging muy fácil de implementar, pero también muy fácil de evadir, es la función “IsDebuggerPresent”.
PEB (Process Environment Block)
Los códigos de espagueti y basura hacen que las herramientas de análisis comunes sean ineficaces
El primer problema de ofuscación que requiere una solución es la eliminación de las instrucciones basura y el "código de espagueti", que es una técnica que pretende confundir los programas de desamblado El código de espagueti hace que el programa fluya de forma difícil de leer al agregar saltos continuos de código, de ahí el nombre.
Este problema no es nuevo, y en situaciones comunes se conocen complementos de reversing que pueden ayudar en esta tarea. Sin embargo, a veces no podemos encontrar un buen complemento de desensamblador interactivo (IDA) que pueda normalizar el flujo de código.

VM detection
hardware IDs are VmBus in case of HyperV VEN_15AD in case of VMware Debugger detection IsDebuggerPresent ThreadHideFromDebugger ProcessDebugPort ProcessDebugObjectHandle ProcessDebugFlags CheckRemoteDebuggerPresent NtQueryInformationProcess ProcessBasicInformation Fuente:elhacker.net

miércoles, 5 de junio de 2024

No encontrar las librerías .h en linux

Las librerías que usa gcc y g++ (y creo que clang) están en /usr/include/ ...a veces el comando locate no encuentra las librerías.

viernes, 31 de mayo de 2024

Los 11 fallos más comunes en Lenguaje C

(1)Mezclar enteros signed y unsigned.
(2)Sobrepasando límites de un arreglo.
(3)Perdiendo la condición base de una funcion recursiva.
(4)Usando constantes de caracteres en vez de literales de cadena y viceversa.
(5)Literales de tipo float son por defecto de tipo double.
(6)Olvidando liberar la memoria.
(7)Añadiendo un semicolon a #define.
(8)No ser cuidadoso con el semicolon.
(9)Erroneamente escribir = o ==.
(10)Copiando demasiado.
(11)Las macros son solo reeemplazo de cadenas.
1.- Mezclar enteros signed y unsigned en operaciones aritmeticas
#include stdio.h
int main(void){
unsigned int a = 1000;signed int b = -1;
if (a > b) puts("a is more than b");
else puts("a is less or equal than b");
return 0;}
Como 1000 es mayor que -1 uno esperaría que a es mayor que b. Antes de hacer la comparación, b es convertida a unsigned int. Cuando es convertido a unsigned int toma el valor máximo de unsigned int... el cual es mayor que 1000. Por esto se puede apreciar que a > b es una falso.
2.- Sobrepasando los límites de un arreglo.

Los arreglos siempre empiezan en 0 y terminan en la longitud del arreglo - 1.
#include stdio.h
int main(){
int x = 0;int myArray[5] = {1,2,3,4,5};
for(x=1; x<=5; x++){printf("%d\t",myArray[x]);}
printf("\n");return 0;} //Output: 2 3 4 5 GarbageValue
La forma correcta:
#include stdio.h
int main(){
int x = 0;int myArray[5] = {1,2,3,4,5};
for(x=0; x<5; x++){printf("%d\t",myArray[x]);}
printf("\n");return 0;} //Output: 1 2 3 4 5
Entonces, hay que conocer el límite de nuestros arreglos porque osino podemos corromper el buffer o provocar un fallo de segmentacion por acceder a un área de memoria distinta.
3.- Perdiendo la condición base en una función recursiva.
Calcular la factorización de un número es un ejemplo clásico de recursividad:
#include stdio.h
int factorial(int n){return n * factorial(n - 1);}

int main(){
printf("Factorial %d = %d\n", 3, factorial(3));return 0;}
//Typical output: Segmentation fault
El problema con esta función es que va a estar en un ciclo infinito, lo que causará fallo de segmentación. Necesita una condición base para detener la recursividad. La forma correcta:
#include stdio.h
int factorial(int n){
// Base Condition, very crucial in designing the recursive functions.
if (n == 1){return 1;}else{return n * factorial(n - 1);}}

int main(){
printf("Factorial %d = %d\n", 3, factorial(3));return 0;}
//Esta función va a terminar tan pronto alcance 1.
//Output : Factorial 3 = 6
Reglas a seguir:
1 Iniciar el algoritmo.
Las funciones recursivas necesitan con frecuencia un valor inicial con el que empezar. Esto es acompañado sea por un parametro de la función o una función puerta que no es recursiva pero pone los valores iniciales para la recursión.

2 Revisar para ver si los valores actuales que se están procesando coinciden con el caso base.Si es asi, entonces procesa y devuelve un valor


3 Redefine la respuesta en terminos de un problema pequeño o simple subproblema o subproblemas.

4 Ejecuta el algoritmo en un subproblema.
5 Combina los resultados en la formulación de la respuesta.
6 Retorna los resultados.

4.- Usando constantes de caracteres en vez de literales de cadena y viceversa.

En lenguaje C, las cadenas de caracteres y literales de cadena son cosas distintas. 'a' ..esto es una cadena de caracter. Una cadena de caracter es de tipo entero que tiene asignado un número para ese caracter. "asdf"... es un literal de caracteres. Un literal de caracteres un arreglo inmodificable cuyos elementos son de tipo char. "asdf" tiene 5 caracteres, porque el caracter final es un \0. Este caracter final es conocido como carácter nulo. {a,s,d,f,\0}.
//ejemplo 1: una cadena de caracteres es usada donde debería ir una literal de cadena. Esto da un comportamiento indefinido.
#include stdio.h
int main(void){
const char *hello = 'hello, world'; /* bad */puts(hello);return 0;}
//ejemplo 2:
un literal de cadena se usa donde se debería usar una cadena de caracteres. El resultado es una cosa sin sentido.
#include stdio.h
int main(void) { char c = "a"; /* bad */ printf("%c\n", c); return 0;}
En ambos casos el compilador se va a quejar de la mezcla. Si no pasa esto, necesita usar más advertencias para la compilación, o derechamente usar un mejor compilador.

5.- Literales de tipo float son por defecto de tipo double.

Hay que tener cuidado al inicializar una variable float a valores literales o compararlas con estos. Esto es debido a que literales float de valor 0.1 por lo regular son de tipo double. Este tipo de cosas nos puede conducir a sorpresas:

#include stdio.h

int main(){
float n = 0.1; if (n > 0.1) printf("Wierd\n");return 0;}
// Prints "Wierd" when n is float
n fue inicializada y redondeada por precisión, resultando en 0.10000000149011612. Entonces, n es vuelto a convertir en double para ser comparado con el valor literal 0.1 (lo cual es igual a 0.10000000000000001) lo que da una discordancia. Mezclar variables float con literales dobles puede resultar en pobre desempeño en plataformas donde no hay soporte de hardware para doble precisión.

6.- Olvidando liberar la memoria.

Uno siempre debe recordar liberar la memoria alojada, sea una función hecha por tí o por una función de librería llamada por tu función.
#include stdlib.h #include stdio.h

int main(void){
char *line = NULL;size_t size = 0;
/* memory implicitly allocated in getline */
getline(&line, &size, stdin);
/* uncomment the line below to correct the code */
/* free(line); */return 0;}
Es un error inocente en este ejemplo específico,porque cuando un proceso termina,la mayoría de los sistemas operativos libera la memoria alojada en vez de tí.
7.- Añadiendo un semicolon a #define

Muchas veces me pasó a mí!!! Es fácil confundirse con el preprocesador de C, y tratarlo como parte del lenguaje. Pero es un error, porque el preprocesador es sólo un mecanismo de reemplazo de texto.
// WRONG
#define MAX 100; int arr[MAX]={0};
//lo cual se traduce como
int arr[100]={0}; //Que se traduce como error de sintaxis.

8.- Ser cuidadoso con el semicolon
//Esto: if (x > a); a = x;
//Significa esto:
if (x > a) {} a = x;
Aveces, perder un semicolon puede provocar problemas inesperados:
if (i < 0) return day = date[0]; hour = date[1]; minute = date[2];
/*El semicolon antes de return está perdido, por lo que day =date[0]; va a ser regresado. El compilador lee hasta que encuentra ; como el fin de línea.*/

9.- Erroneamente escribir = en vez de ==.
El = es para asignar.
El == es para COMPARAR.
A veces hacemos:
/* assign y to x */ if (x = y) {/* logic */}
//cuando lo que uno queria era:
/* compare if x is equal to y */ if (x == y) {/* logic */}

//lo cual es equivalente a:
/* compare if x is equal to y */ if (x == y) != 0{/* logic */}

10.- Copiando demasiado.
char buf[8]={0}; /* tiny buffer, easy to overflow */
printf("What is your name?\n");
scanf("%s", buf); /* WRONG */
scanf("%7s", buf); /* RIGHT */
Si uno pone más caracteres que los requeridos por scanf, van a empezar a sobreescribirse zonas de memorias aledañas al buffer. Esto puede derivar en comportamiento indefinido. Los hackers maliciosos con frecuencia usan esto para sobreeescribir la dirección de return, y cambiar la dirección por la del código malicioso creado por este.
11.- Las Macros son solo reemplazo de cadenas
#include stdio.h
#define SQUARE(x) x*x
//este es el error.
int main(void){
printf("%d\n", SQUARE(1+2));return 0;}
Esperarías que este código devolviese 9, pero devolverá 5 porque la macro será expandida a 1+2*1+2. Para evadir este problema debe encerrar entre () las fichas para evadir este problema.
#include stdio.h
#define SQUARE(x) ((x)*(x))
int main(void){
printf("%d\n", SQUARE(1+2));return 0;}

TextView and EditText en Android

crear un archivo .xml con esto: LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> /LinearLayout> ........Encerrar LinearLayout entre <> (no me deja poner el código xml) y escribir esto en un archivo .xml: LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> /LinearLayout> TextView and EditText in Android

Diferencias entre const int y int const

Una respuesta simple: leer hacia atrás. Los punteros como sabréis son variables especiales que apuntan a otras variables, y las constantes son variables que no cambian durante la ejecución de un programa. int * ptr = ptr es un puntero hacia int. int const * ptr = ptr es un puntero hacia constante int. int * const ptr = ptr es un puntero constante hacia int. const int * const ptr = ptr es una constante puntero a una constante int. const int * ptr es igual a int const * ptr const int * const ptr es igual a int const * const ptr véase que lo que cambia es lo que esta antes de *ptr o *const ptr. int ** ptr = is a pointer to pointer(p2p) to int. Un puntero que apunta hacia otro puntero. int ** const ptr = ptr es un puntero constante de un puntero hacia int. int * const * ptr = ptr es un puntero a una constante puntero hacia int. int const **ptr = ptr es un puntero de un puntero hacia una constante int. int * const * const ptr = ptr es un puntero constante de otro puntero constante hacia int. ¿Como saber si const se refiere al puntero o lo apuntado? Si está del lado derecho del asterico, entonces se refiere a lo apuntado. Si está del lado izquierdo del asterico, del puntero. Fuente: What is the difference between const int and int const.

miércoles, 22 de mayo de 2024

Colores c.nanorc

## Syntax highlighting for C and C++ files. syntax c "\.([ch](pp|xx)?|C|cc|c\+\+|cu|H|hh|ii?)$" header "-\*-.*\" # Labels. color brightmagenta "^[[:blank:]]*[A-Z_a-z][0-9A-Z_a-z]*:[[:blank:]]*$" color normal ":[[:blank:]]*$" # Types and related keywords. color green "\<(auto|bool|char|const|double|enum|extern|float|inline|int|long|restrict|short|signed|sizeof|static|struct|typedef|union|unsigned|void)\>" color green "\<([[:lower:]][[:lower:]_]*|(u_?)?int(8|16|32|64))_t\>" color green "\<(_(Alignas|Alignof|Atomic|Bool|Complex|Generic|Imaginary|Noreturn|Static_assert|Thread_local))\>" color green "\<(class|explicit|friend|mutable|namespace|override|private|protected|public|register|template|this|typename|using|virtual|volatile)\>" # Flow control. color brightyellow "\<(if|else|for|while|do|switch|case|default)\>" color brightyellow "\<(try|throw|catch|operator|new|delete)\>" color magenta "\<(break|continue|goto|return)\>" # Single-quoted stuff (characters, backslash escapes, hex and octal byte codes). color brightmagenta "'([^'\]|\\(["'\abfnrtv]|x[[:xdigit:]]{1,2}|[0-3]?[0-7]{1,2}))'" # GCC builtins. color cyan "__attribute__[[:blank:]]*\(\([^)]*\)\)|__(aligned|asm|builtin|hidden|inline|packed|restrict|section|typeof|weak)__" # Strings and names of included files. color brightyellow ""([^"]|\\")*"|#[[:blank:]]*include[[:blank:]]*<[^>]+>" # Preprocessor directives. color brightcyan start="^[[:blank:]]*#[[:blank:]]*(if(n?def)?|elif|warning|error|pragma)\>" end="(\`|[^\])$" color brightcyan "^[[:blank:]]*#[[:blank:]]*((define|else|endif|include(_next)?|line|undef)\>|$)" # Comments. color brightblue "//.*" color brightblue start="/\*" end="\*/" # Reminders. color brightwhite,yellow "\<(FIXME|TODO|XXX)\>" # Trailing whitespace. color ,green "[[:space:]]+$"

viernes, 12 de abril de 2024

ELF Basics Internal: Elf Basics.

/*Este material no lo hice yo. Source: oxhat.blogspot.com*/ In this post I will share details on ELF binary basics.

So let is begin with a very simple hello world program in C

#include stdio.h

int main(){printf("\nHello World\n");return 0;}

As I am on a 64 bit Linux system I will compile the binary for both 32bit and 64bit mode.

We will compile this code with gcc by issuing the command
for 64 bit -> gcc hello.c -o hello64
for 32 bit -> gcc hello.c -m32 -o hello32

, ( in case we get error we can install gcc multilib by issuing command sudo apt-get install gcc-multilib ) If we issue file command on the binary we created we would see the following output

pentest@ubuntu:~/Desktop$ file hello64 hello64: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=797fa6ea8a92b773eb5106c822a76788441ceac1, not stripped
pentest@ubuntu:~/Desktop$ file hello32 hello32: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=ba188ad09ee9ff9ac774833b8a7c87d8afbc443a, not stripped

So let us try to understand what all these mean (we will analyze the result of 64 bit binary)
hello64: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=797fa6ea8a92b773eb5106c822a76788441ceac1, not stripped

hello64: This is the filename of the binary on which we are executing the file command
ELF - Executable and Linking Format or Executable and Linkable Format - This mean that the binary type is associated with mainly UNIX type operating system , like Linux, Solaris but also supports certain non UNIX operating system.
64bit - This gives us information tells about the architecture of the binary that it is 64 bit. So if it is a 32 bit binary it will be shown as 32 If we are in a 64bit machine and if we want to create a 32 bit binary we can pass the -m32 option
LSB - Least Significant Byte - It means the binary is in little endian format. In Intel architecture you will often find this as LSB. However in architectures like PowerPC , SPARC and so on it is possible to get this in big endian format i.e MSB ( Most Significant Byte )
Shared object - This result can either be Shared Object or Relocatable or Executable.
Let us see how these are different from each other and how we can generate them using gcc. The two terms which we are going to use here are PIC ( Position Independent Code ) and PIE ( Position Independent Executable) .
When we are planning to create a library that can be called by many process, we need to make it a PIC so that they can be loaded in the memory at any virtual address and just because they are position independent it can be accessed with relative offsets without worrying about the clashes of fixed locations in memory. We can create a PIE when Shared Object - By default the gcc compiler compiles the source code with -fPIC which makes address of the sections in the program relative to each other.
Executable - This mean this is not a PIE application. This loads with absolute address and thus we can find no reference of .plt.got sections here as the program is loaded in memory with fixed address .We can disable PIE with -no-pie option in gcc and thus we will get a executable object file.
Relocatable - This means this is just an object code without any linking of libraries or files that are necessary for the execution.
There are some steps involved when we make a program that can be executable ( Please Note: The term executable here means here is to make it run or execute and should not be confused with the above executable object type ).
To make an executable from source program the following process is involved.
Preprocessing -> Compilation -> Object File Creation -> Linking.

Normally in gcc we do in one step like gcc hello.c -o hello.out
but however we can do in 2 steps like gcc -c hello.c ; this will create an object file called hello.o This is how the disassembly of main looks like in object code.
0000000000000000 <main>:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 48 8d 3d 00 00 00 00 lea rdi,[rip+0x0] # b <main+0xb>
b: e8 00 00 00 00 call 10 <main+0x10>
10: 90 nop
11: 5d pop rbp
12: c3 ret

This program cannot run or do anything because the object code doesn't have the necessary linked objects or libraries required for execution. We can generate an executable binary from object code using the command
gcc hello.o -o hello-executable Now if we run objdump on the binary we can see lots of sections getting created with the location to the linkers.
This is how the disassembly of main looks like after linking
000000000000063a <main>:
63a: 55 push rbp
63b: 48 89 e5 mov rbp,rsp
63e: 48 8d 3d 8f 00 00 00 lea rdi,[rip+0x8f] # 6d4
<_IO_stdin_used+0x4>
645: e8 c6 fe ff ff call 510
<puts@plt>
64a: 90 nop
64b: 5d pop rbp
64c: c3 ret
64d: 0f 1f 00 nop DWORD PTR [rax]

Nota: Lo marcado con fondo rojo va al final de la línea anterior.
version 1 (SYSV) - This means that it uses version 1 and the target operating system for the binary is SYSTEM V. There can be other possible values for this for example FreeBSD, HP-UX , etc,. I didn't get enough resource from where I can find more details on the version 1 result and how it can affect something.

Dynamically linked, interpreter /lib/ld-linux.so.2, - It means that the binary uses some dynamically linked libraries. There is 2 possible values possible for this.Dynamically linked and Statically Linked.

Dynamically Linked - It means the linker actually uses a reference to load dynamically linked libraries in memory during execution of the program from the location /lib/ld-linux.so.2
We can verify it by running the ldd on the binary
pentest@ubuntu:~/Desktop$ ldd hello64
linux-vdso.so.1 (0x00007fff96bc2000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f425eb2a000)
/lib64/ld-linux-x86-64.so.2 (0x00007f425f11d000)

Statically Linked - It means that the binary has been packed along with the libraries. So there is no dynamically linked libraries inside it. So if a binary is compiled with -shared option in gcc it will create a statically linked binary. So if we run ldd command on the binary it will tell that there is not a dynamic executable.
pentest@ubuntu:~/Desktop$ ldd helloStatic
not a dynamic executable
There is a huge difference in sizes of the binary when compiled with -shared option ( i.e statically )
-rwxrwxr-x 1 pentest pentest 8296 Feb 6 08:55 hello64
-rwxrwxr-x 1 pentest pentest 844704 Feb 7 09:28 helloStatic
At this point you might feel confused between the relocation of the binary that we discussed before and the linking which we are discussing now. Well when we talk about shared object or executable or relocatable object type, then we are actually dealing how the program will be loaded in memory but when we talk about linking, then it is all about how the external libraries will be linked to binaries - either dynamically via some shared resources or statically by packing it with the actual binary. So we can make this statement , an executable object type may have dynamic linked libraries. Than means even if we disable PIE we can still get an executable with dynamically linked libraries.
pentest@ubuntu:~/Desktop$ file helloNOPIE
helloNOPIE: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=2d986bca273f541af7a48ffb51f4d5fd22177c22, not stripped

pentest@ubuntu:~/Desktop$ ldd helloNOPIE
linux-vdso.so.1 (0x00007ffffe990000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcb5e8c0000) /lib64/ld-linux-x86-64.so.2 (0x00007fcb5ecb1000)
for GNU/Linux 3.2.0 - The minimum kernel version required to execute the binary BuildID[sha1]=ba188ad09ee9ff9ac774833b8a7c87d8afbc443a - This ID is assigned to the binary during the build phase. Possibly during the linking phase as this is not visible in object code after compilation.
not stripped - This means that the certain but not all debugging information are available. It can also have a value stripped in case we remove the debug symbols. A stripped binary is smaller in size than an not stripped binary. When we strip a binary we remove some extra sections from a binary that is not relevant or required for execution but was added for making debugging easy. We can verify using gdb and we can keep debug symbols in a binary by compiling with -g option
pentest@ubuntu:~/Desktop$ gcc hello.c -g -o helloDebugSymbols pentest@ubuntu:~/Desktop$ gdb -q ./helloDebugSymbols
Reading symbols from ./helloDebugSymbols...done.
(gdb) info functions
All defined functions: File hello.c:
void main();
Non-debugging symbols:
0x00000000000004e8 _init
0x0000000000000510 puts@plt
0x0000000000000520 __cxa_finalize@plt
0x0000000000000530 _start
0x0000000000000560 deregister_tm_clones
0x00000000000005a0 register_tm_clones
0x00000000000005f0 __do_global_dtors_aux
0x0000000000000630 frame_dummy
0x0000000000000650 __libc_csu_init
0x00000000000006c0 __libc_csu_fini
0x00000000000006c4 _fini
Now we will try the same with Stripped Binary and we can see that as there there is no debug symbols there is no reference to the function void main() as per source code. However there are still certain debug information available. For example I can find the address of main function.
pentest@ubuntu:~/Desktop$ gcc hello.c -o helloNoDebugSymbols pentest@ubuntu:~/Desktop$ gdb -q ./helloNoDebugSymbols
Reading symbols from ./helloNoDebugSymbols...(no debugging symbols found)...done.
(gdb) info functions
All defined functions:
Non-debugging symbols:
0x00000000000004e8 _init
0x0000000000000510 puts@plt
0x0000000000000520 __cxa_finalize@plt
0x0000000000000530 _start
0x0000000000000560 deregister_tm_clones
0x00000000000005a0 register_tm_clones
0x00000000000005f0 __do_global_dtors_aux
0x0000000000000630 frame_dummy
0x000000000000063a main
0x0000000000000650 __libc_csu_init
0x00000000000006c0 __libc_csu_fini
0x00000000000006c4 _fini
We can strip it down further using strip function
pentest@ubuntu:~/Desktop$ strip -s helloNoDebugSymbols -o helloNoDebugSymbolsStripped
pentest@ubuntu:~/Desktop$ gdb -q ./helloNoDebugSymbolsStripped
Reading symbols from ./helloNoDebugSymbolsStripped...(no debugging symbols found)...done.
(gdb) info functions
All defined functions:
Non-debugging symbols:
0x0000000000000510 puts@plt
0x0000000000000520 __cxa_finalize@plt
So that's all for this blog post. In my further posts I will talk in more details about each of the part of elf binary in more details

shellcode a ASM y al reves

/*Este manual no es de mi autoría. Salió de https://oxhat.blogspot.com*/ As i have started a journey into vulnerability research and exploitation, i thought of sharing some topics which I found very confusing initially. So i will try to detail as much information as possible. So we will begin by writing a simple helloworld assembly code. The code will do the following Print HelloWorld and Exit Now you may wonder why do I have to write a code that exits ? If such is the case then you might probably have written good amount of code in high level language. The compilers of high level languages takes care of it i.e writing the extra code in the object file like the exit code. Internally every operations like read , write , exit and so on requires some low level calls to kernel. These calls are called SysCalls. So if you are programming using high level language like C and C++ , then you don't need to write codes to make the syscalls because due to the abstraction layer that hides the excessive code that is required to code. The compiler takes care to generate the object code which has essential exit code in it. To trigger these syscalls we need to use interrupt. Now these interrupt is maintained using an interrupt table. The diagram below shows the workflow of the syscalls and the interrupt
Now that i have given you a brief idea on why we need to write an exit code in asm, we will program a helloworld code global _start section .text _start: ;/usr/include/i386-linux-gnu/asm/unistd_32.h ;#ifndef _ASM_X86_UNISTD_32_H ;#define _ASM_X86_UNISTD_32_H 1 ;#define __NR_restart_syscall 0 ;#define __NR_exit 1 ;#define __NR_fork 2 ;#define __NR_read 3 ;#define __NR_write 4 ;#define __NR_open 5 ;#define __NR_close 6 ;ssize_t write(int fd, const void *buf, size_t count); mov eax,0x4 ; syscall for write => 4 mov ebx,0x1 ; fd => stdout mov ecx,someString ; *buf => someString mov edx,strlen ; size_t count => strlen int 0x80 ; call interrupt ;void exit(int status); mov eax,0x1 ; syscall for exit => 1 mov ebx,0x2 ; status => 2 int 0x80 ; call innterupt section .data someString: db "Hello World NASM" strlen equ $-someString
Now that I have a working , elf binary , my next target is to generate the shellcode from it. I will use the objdump utility to view the disassembled contents of the binary along with the opcodes.
There is a nice one liner at ( http://www.commandlinefu.com/commands/view/6051/get-all-shellcode-on-binary-file-from-objdump ) which we can use to get the shellcode from the binary. objdump -d ./PROGRAM|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g' Using this technique, we can get a nice shell code from it which we dont need to extract manually from the disassembled code objdump -d ./helloworld|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g' "\xb8\x04\x00\x00\x00\xbb\x01\x00\x00\x00\xb9\xa4\x90\x04\x08\xba\x10\x00\x00\x00\xcd\x80\xb8\x01\x00\x00\x00\xbb\x02\x00\x00\x00\xcd\x80" Sweet! Now our 1st part of the tutorial is over , moving to the next , ShellCode to Assembly. Now if I present you with the following shellcode, how will you get back to a working elf executable. "\xb8\x04\x00\x00\x00\xbb\x01\x00\x00\x00\xb9\xa4\x90\x04\x08\xba\x10\x00\x00\x00\xcd\x80\xb8\x01\x00\x00\x00\xbb\x02\x00\x00\x00\xcd\x80"' Lets copy the shellcode and save the contents inside a file. Please note we are going to save the shellcode as raw hex file and not as text. To do it we need help of perl Syntax : perl -e 'print "YOUR SHELL CODE"' > outputFile perl -e 'print "\xb8\x04\x00\x00\x00\xbb\x01\x00\x00\x00\xb9\xa4\x90\x04\x08\xba\x10\x00\x00\x00\xcd\x80\xb8\x01\x00\x00\x00\xbb\x02\x00\x00\x00\xcd\x80"' > hexraw Now we will use the ndisasm utility to get the disassembled code from the file. So what ndisasm is doing here is converting the hex opcodes into equivalent asm instructions. Syntax : ndisasm -b 32 hexraw
Now you can see , we almost have the same code that we wrote, except there is an hardcoded address 0x80490a4 at line 3 and hardcoded value at line 4. The problem is we got the disassembled code of the .text section and not the .data section. Let us fix the code by modifying the code a little. global _start section .text _start: mov eax,0x4 mov ebx,0x1 mov ecx,someString mov edx,strlen int 0x80 mov eax,0x1 mov ebx,0x2 int 0x80 section .data someString: db "Hello World ASM" strlen equ $-someString Finally we are able to get back our ASM code and make it execute successfully

lunes, 25 de marzo de 2024

conversion explicita (casting)

C++, conversión explícita o cast Cuando necesitamos convertir una variable perteneciente a un tipo de dato (cadena, numérico, fecha, etc.) a otro tipo diferente del suyo original, debemos decirle al programa explícitamente que tipo de conversión deseamos.

La conversión explícita en vez de realizarla el compilador automáticamente se indica de forma explícita, en C++ utilizamos la forma (nombre_de_tipo)expresión. No es exclusiva de C++ pues también se puede emplear en otros programas aunque cambie ligeramente la implementación.

Notación cast Si tenemos una función previamente definida que espera un tipo determinado, por ejemplo la función raíz cuadrada (sqrt) espera como argumento un tipo double, para evitar una salida inesperada en el caso de pasar un argumento de otro tipo, podemos escribir: sqrt ( ( double ) ( n + 2) ); De este modo forzamos para que el resultado de ( n + 2 ) sea siempre un tipo double y se lo pase a la función sqrt.

En C++ es posible expresar también una construcción cast de la forma siguiente: nombre_de_tipo(expresión) esta recibe el nombre de notación funcional, y no se puede utilizar con tipos que tengan un nombre simple.

Notación funcional Para convertir un valor a un tipo puntero utilizando la notación cast escribiríamos: int *p = ( int *)0x1F5; Pero utilizando la notación funcional escribiremo: typedef int *pint; int *p = pint(0x1F5);

Una variable de un determinado tipo no siempre puede ser convertida explícitamente a otro tipo. En este caso: struct { unsigned int a : 3; // bits 0 a 2 unsigned int b : 1; // bit 3 unsigned int c : 3; // bits 4 a 6 unsigned int d : 1; // bit 7 } atributo; La variable atributo tiene una longitud de ocho bits. Pero si intentamnos copiar la variable atributo a una variable atrib de tipo char y escribimos. char atrib = char atributo; // error Da un error, ya que C++ no permite convertir una estructura a un tipo como char aunque las longitudes de ambos sean iguales.

Conversión de Punteros Utilizando conversiones explícitas de tipo sobre punteros, es posible convertir el valor de una variable de un determinado tipo a otro cualquiera. El formato general para hacer esto es: cualquier_tipo *p = ( cualquier_tipo *)&variable. Aplicando esto al caso anterior, obtendríamos: char * atrib = ( char *)&atributo; Con lo que char a = * atrib; define la variable a de tipo char, cuyo contenido es el mismo que el de la estructura atributo. Constructores y operadores de conversión Cuando trabajamos con clases, nosotros mismos tenemos que construir las conversiones que deseamos que realice el compilador cuando utilice un objeto de una clase. Estas conversiones pueden ser o entre una clase y un tipo predefinido. Para ello podemos utilizar dos mecanismos; constructores y operadores de conversión. Constructores Podemos definir una conversión a través de un constructor que tome un argumento de un determinado tipo como entrada y lo convierta en un objeto de una determinada clase. En la definición de una clase podemos definir el siguiente constructor: nombre_de_clase (int r) { real = ( double )r; imag = 0; } Que sirve para construir un número complejo. Este constructor además de inicializar un objeto complejo utilizando solamente un valor también permite asignar directamente un entero int a un objeto complejo como se muestra a continuación. complejo c(3); // construye el complejo (3,0) c = 6; // equivale a c = complejo(6) Operador de conversión Ahora se desea que se realice también de una forma implícita o explícita, si existe ambigüedad, la conversión de un tipo definido por el usuario a un tipo básico: double d; CRacional r(1,2); d = r ; // r tiene que convertirse a double para que la instrucción d = r se ejecute correctamente, es necesario realizar una conversión implícita de CRacional a double. Este tipo de conversión no está permitido con un constructor, ya que no podemos definir un constructor de un tipo base. Cuando necesitamos convertir objetos de un tipo de clase a otro tipo, tenemos que utilizar un operador de conversión. La sintaxis para este operador es: C::operator T(); Donde T es el nombre de un tipo. La conversión que se realiza es de C a T. Para convertir de CRacional a double. class CRacional { //....... operador double(); }; inline CRacional::operator double() { return ( double )numerador/( double )denominador; } Un operador de conversión no puede tener argumentos ni tipo del valor que se retorna. Un operador de conversión se puede llamar de a través de las formas siguientes: double d; CRacional r(1,2); d = r.operator double();/llamada explícita a la función d = double(r);//conversión explícita (notación funcional ) d = ( double )r; //conversión explícita cast d = r ; // conversión implícita Un operador de conversión puede llamarse explícitamente pero su principal utilidad es que sea llamado automáticamente por el compilador cuando la evaluación de una expresión requiere el tipo de conversión realizado por él. También puede definirse un operador de conversión que convierta un objeto de una clase a otro objeto de otra clase. Conversión del tipo void* El tipo void se puede utilizar para definir un puntero a un elemento genérico. Como podemos ver la función C malloc se define como: void *malloc(size_t n); En C, la conversión del tipo void* a otro tipo podía realizarse de forma implícita de este modo. char *p = malloc(longitud + 1); Pero en C++ esta conversión tiene que realizarse de forma explícita. Esto es. char *p = ( char * ) malloc ( longitud + 1 );

punteros,templates(plantillas)

Operaciones con punteros. Tipos genéricos. Plantillas. ( Templates ). Typedef. El operador sizeof. Constructores. Objetos de la clase. Operaciones con punteros El lenguaje C++ ofrece cinco operaciones básicas con punteros. 1- Asignación: Consiste en asignar una dirección a un puntero. Normalmente se empleará el nombre de un array o con el operador dirección (&). En el siguiente ejemplo, se asigna a punt1 la dirección del inicio de un array llamado arr. En la variable punt2 colocamos la dirección del tercer y último elemento, arr[2]. static int arr[] = { 10,20,30}; int *punt1, *punt2; punt1 = arr; // asigna una dirección al puntero punt2 = & arr[2];
2- Valor guardado en una dirección: El operador * nos da el valor almacenado en la posición de memoria apuntada. *punt1 =arr[1]; //obtenemos en *punt1 el valor que hay en arr[1]. 3- Dirección de un puntero: Como cualquier variable, los punteros tienen una dirección y un valor. El operador & nos dice dónde está almacenado el puntero. int direcc; direcc = &punt1; // en direc metemos la dirección en la que se encuentra punt1. 4- Incremento de un puntero: Es posible realizar esta tarea como una suma normal o a través del operador de incremento. Si incrementamos un puntero, éste apuntará al siguiente elemento del array. De este modo, punt1++ incrementa el valor numérico de punt1 en 2 ( un int ocupa 2 bytes ) y hace que punt1 señale a arr[1]. Hay que tener en cuenta que la dirección de punt1 sigue siendo la misma. Pues una variable no se mueve de su sitio sólo por el hecho de cambiar su valor. También es posible decrementar el puntero pero hay que tener en cuenta algunos puntos peligrosos. El ordenador no le sigue la pista a un puntero, por tanto no puede saber si está apuntando dentro de un array o producirá un error. La operación ++punt1 hace que punt1 se mueva otros dos bytes. Otra cuestión a tener en cuenta es que sólo se pueden incrementar variables, y no constantes, así que una constante que sea puntero no puede cambiar su valor ( esto puede parecer una perogrullada, pero ++arr es una expresión muy atractiva). Sin embargo, sí puede utilizarse en esta suma normal. Válido No válido punt1++; arr++; x++; 3++; punt2 = punt1 + 4; punt2 = arr++; punt2 = arr + 1; x = y + 3++; 5- Resta: Dos punteros pueden restarse. Se utilizará con dos punteros que señalen elementos dentro de un mismo array, para saber cuántos elementos les separan. El resultado no son bytes, sino las mismas unidades del tamaño del array. Punteros y arrays En el siguiente ejemplo veremos cómo se relacionan los puntero y los arrays, vamos a ver primero la notación de un array y luego la notación de un puntero. Versión de array: // imprime los valores del array main( ) { static int nums[ ] = { 90, 70, 69, 58 }; int indice; for (indice = 0; indice < 4; indice ++) cout << nums[indice]; } Esta forma de programar es más directa, pues utiliza la notación de array para acceder a los elementos del array mediante la expresión nums[indice]. Versión de punteros: // utiliza punteros para imprimir los valores del array main( ) { static int nums[ ] = { 90, 70, 69, 58 }; int indice; for (indice = 0; indice < 5; indice ++) cout << *(nums + indice); } Esta versión es idéntica a la primera, excepto por la expresión *(nums + indice). Cuyo efecto es exactamente el mismo que el de nums[indice] en el programa anterior, es decir: *( array + indice) es lo mismo que array[indice] Una tira de caracteres es una cadena de bytes, finalizada por el carácter especial '\0'; En este caso para inicializar la cadena, emplearemos: char *saludo = "Hola "; En vez de static char saludo[ ] = "Hola "; Estos dos formatos parecen que tienen el mismo efecto en el programa. Pero hay una pequeña diferencia. La segunda línea reserva espacio para un array con el suficiente número de bytes ( En este caso 10 ) para almacenar la palabra más un byte correspondiente al carácter '\0' ( nulo ). La dirección del primer carácter del array se obtiene dando el nombre del array, saludo. En la versión puntero (la primera línea) se reserva espacio para el array de la misma forma, pero también se reserva espacio para una variable de puntero, este puntero es el que recibe el nombre saludo. Tipos genéricos. Plantillas. ( Templates ) Los tipos genéricos o tipos parametrizados, también denominados plantillas, permiten construir una familia de funciones o clases relacionadas. Las funciones o clases se diseñan para trabajar con un tipo específico de datos. Esta característica tiene poco interés para el diseñador de la función o de la clase, pero tiene importancia para el usuario de la función o clase, ya que le permitiría elegir el tipo de datos que necesite en cada momento. Las funciones genéricas o plantillas de función admiten argumentos de cualquier tipo. Es decir podemos definir una plantilla que admita el tipo como un parámetro más. #include template < class Plantilla > Plantilla min(Plantilla a, Plantilla b) { return ( a < b ) ? a : b; } void main( ) { int m = 11, n = 28; float x = 17.5, y = 36.2; cout << min ( m, n) << endl; cout << min (x, y) << endl; } Una plantilla no es una definición de función, sino un patrón a partir del cual el compilador puede generar una función. El prefijo template < class Plantilla > indicará al compilador que Plantilla se proporcionará en el momento de la expansión de la plantilla a una definición real de la función, en ese momento un tipo definido sustituirá a Plantilla. Si necesitamos que a sea de un tipo cualquiera y b de otro tipo cualquiera. Entonces escribimos: #include < iostream.h> template < class Plantilla1, class Plantilla2 > Plantilla2 min (Plantilla 2 a, Plantilla1 b) { return ( a < b ) ? a : b; } void main( ) { int m = 11, n = 28; float x = 17.5, y = 36.2; cout << min ( m, n) << endl; cout << min ( x, n) << endl; cout << min ( m, y) << endl; cout << min ( x, y) << endl; } El prefijo template < class Plantilla1, class Plantilla2 > indica que la plantilla en el momento de la expansión sustituirá Plantilla1 y Plantilla2 por los argumentos correspondientes a los tipos en la llamada a min, El compilador expandirá la plantilla automáticamente cuando sea necesario. Por ejemplo, si detecta una llamada min con dos argumentos enteros, automáticamente amplía una versión int de la plantilla. Clases genéricas Aunque las plantillas son útiles, son aún más importantes las plantillas de clase. Las clases genéricas o plantillas de clase permitirán definir un patrón para definiciones de clases. Las clases contenedor o clases de contenido; esto es, una clase que contiene objetos del mismo tipo (listas, arrays, etc). Son buenos ejemplos de plantillas. Si tenemos un vector de enteros o de cualquier tipo, las operaciones básicas que podremos realizar con él, serán las mismas (insertar, borrar, leer, etc.). Lo que permitirá al usuario elegir el tipo de elementos del array, para ellos, definiremos una clase genérica. Una plantilla de clase se diferencia de una plantilla de función por que se expande, dando lugar a una definición completa de clase, cuyos tipos internos son proporcionados por el usuario. Las funciones que son miembros de la plantilla de clase, también son plantillas de función, y por tanto, también tendrán que expandirse cada vez que se expanda la clase asociada. La solución a este problema, y por tanto la forma correcta de utilizar las plantillas, consiste en reunir en una sola clase base común todo el código que podría ser idéntico en las diversas ampliaciones de la plantilla, y derivar de ella la plantilla de clase. Typedef La palabra clave typedef crea un tipo al que se le asigna un nombre arbitrario dado por el programador. Se parece a #define, pero tiene tres diferencias: 1. typedef está limitado a asignar nombres simbólicos a tipos de datos únicamente, al contrario de como hace #define 2. La función typedef se ejecuta desde el compilador y desde el preprocesador. 3. Dentro de sus límites, typedef es más flexible que #define. typedef float real; ó typedef float REAL; permite declaraciones del tipo: real x; ó REAL *numero; También es posible el uso de typedef con estructuras: typedef struct DAT_PER{ char nombre[25]; union datos_conf{ char matricula[20]; int cuenta; char nif; } conf; DAT_PER *sig; }personal[MAX_NUM_PERSONAS]; lo cual permite declaraciones más intelegibles: DAT_PER *sig; o DAT_PER personal[MAX_NUM_PERSONAS] Operador sizeof El operador sizeof no se representa mediante un símbolo como el resto de operadores. Es un operador unario, no una función. Devuelve el tamaño en bytes de un objeto determinado. La forma de utilizar sizeof es: sizeof expresión; Donde expresión es el nombre de una variable o de un tipo de datos. El objetivo de sizeof es determinar el tamaño de varios objetos dependientes de la máquina. Es muy útil para escribir programas que puedan ejecutarse en varios tipos de ordenadores. Pues estos pueden tener tamaños de almacenamiento diferentes. //muestra el tamaño en bytes de varios tipos de datos main( ) { cout << sizeof(char); cout << sizeof(short); cout << sizeof(int); cout << sizeof(long); cout << sizeof(float); cout << sizeof(double); } Este ejemplo devuelve la memoria se se usa para cada dato de tipo primario. Si se ejecuta en un sistema de 16 bits. El programa le dirá que un entero ocupa 4 bytes. En uno de 64 bits le dirá que ocupa 8 bytes. Un programa que necesite este tipo de información se puede obtener fácilmente utilizando sizeof. Constructores. Objetos de la clase En primer lugar definimos una clase: class Punto{ int x; // miembros dato int y; char ch; public: void mostrar( ); void ocultar( ); }; void Punto::mostrar( ) { gotoxy( x, y); cout << ch; } void Punto::ocultar( ) { gotoxy( x, y); cout << " "; } Una vez definida la clase, para usarla se ha de definir un objeto. Se define una variable de la clase Punto, exactamente igual que se define una variable de un tipo predefinido ( int, float, etc,) o cualquier otro tipo definido por el usuario. Los objetos usan la misma notación que cualquier tipo de variable, y su alcance se extiende desde la línea donde se ha declarado hasta el final del bloque. Para crear un objeto de una clase se llama a una función denominada constructor. El constructor se llama de forma automática cuando se crea un objeto. El constructor tiene el mismo nombre que la clase. Lo específico del constructor es que no tiene tipo de retorno. Punto::Punto(argumentos) -Declaración del constructor class Punto { //... public: Punto ( char ch1, int x1, int y1); }; - El constructor inicializa los miembros dato en la definición del constructor. Punto::Punto ( char ch1, int x1, int y1) { ch = ch1; x = x1; y = y1; } -Llamamos al constructor. Para crear un objeto pt1 de la clase Punto. Punto pt1('*',20,33); En el objeto, el miembro dato ch guardará el carácter *, el miembro dato x, el número entero 20, y el miembro y, el entero 33. -Se llama a las funciones miembro desde el objeto pt1 pt1.mostrar( ); pt1.ocultar( ); Es posible tener más de un constructor, A continuación definimos uno que fije el carácter pero que permita cambiar las coordenadas del punto. -Declaración de constructor class Punto{ //... public: Punto( int x1, int y1); //... }; -Definición del constructor: se fija el carácter, y se le pasan las coordenadas del punto Punto::Punto( int x1, int y1 ) { x = x1; y = y1; ch = '*'; } Se llama al constructor para crear un objeto pt2 Punto pt2( 20, 33 ); En el objeto, el miembro dato ch almacenará el carácter *, el miembro dato x, el número entero 20, y el miembro y el entero 33. -Se llama a las funciones miembro desde el objeto pt2 pt2.mostrar( ); pt2.ocultar( ); Una clase puede tener más de un constructor. Si un constructor no tiene argumentos, es el constructor por defecto. -Declaración del constructor por defecto de la clase class Punto{ //... public: Punto( ); //... }; -Definición del constructor por defecto: los miembros dato se inicializan en el bloque de dicho constructor Punto::Punto( ) { ch = '*'; x = 20; y = 33; } Para llamar al constructor por defecto escribimos: Punto def; Si escribimos Punto def( ); // error Da un error. Para llamar a las funciones miembro desde el objeto def. def.mostrar( ); def.ocultar( );

puntero implicito this, keyword friend

El puntero implícito this Saltar el sistema de protección. ( Friend ) Miembros static de una clase Funciones virtuales El puntero implícito this Cada objeto de una determinada clase mantiene su propia copia de los datos miembro de la clase, pero no de las funciones miembro, de estas sólo existe una copia para todos los objetos de la clase; es decir, cada objeto tiene su propia estructura de datos, pero todos comparten el mismo código para cada función miembro. De este modo si necesitamos que una función miembro conozca la identidad de cada objeto para el que ha sido llamada, en C++ debemos invocar un puntero al objeto denominado this. Así por ejemplo, si declaramos un objeto. Empleado.set_empleado( ); C++ define el puntero this para apuntar al objeto fecha1 de la forma: CEmpleado *const this = &Empleado1; y si realizamos la misma operación con otro objeto empleado2, empleado2.set_empleado( ); C++ define el puntero this, para apuntar al objeto empleado2, de la forma: CEmpleado *const this = &Empleado2; Según lo expuesto, la función set_empleado puede ser definida de la forma: void CEmpleado::set_empleado( ) { cout << "nombre, ## : "; cstr >> this->nombre; cout << "apellido1, ## : "; cstr >> this ->apellido1; cout << "apellido2, #### :"; cstr >> this -> apellido2; } Un ejemplo de esto. do empleado.set_empleado( ); while ( !empleado.ok_empleado( )); En este caso, la función miembro ok_empleado conoce con exactitud a que objeto ha de ser aplicada, puesto que se ha expresado explícitamente. Pero ¿Que pasa con la función miembro Jefe_de, que se encuentra sin referencia directa alguna en el cuerpo de la función miembro ok_empleado? int Cempleado::ok_empleado( ) { //... if (Jefe_de (apellido1)) //... } En este otro caso la llamada no es explícita como en el anterior. Lo que sucede realmente, es que todas las llamadas a los datos y funciones miembro son referenciadas con un puntero implícito this, que contiene, como ya se ha dicho, la dirección del objeto por medio del cual se ha producido la llamada. Según esto también podríamos escribir. if ( this -> Jefe_de(apellido1 )); Normalmente no será necesario utilizar este puntero para acceder a los miembros de la clase, pero es útil cuando si se trabaja con estructuras dinámicas, también se puede utilizar la expresión (*this) miembro. Saltar el sistema de protección. (Friend) Los datos miembro de una clase declarados en la parte privada pueden manipularse únicamente por las funciones miembro de la clase, este es el modo de garantizar su integridad. El resultado es como una caja negra que realiza cierta función y que es reutilizable en cualquier otro programa. En el caso de que una función no miembro de una determinada clase necesite acceder a los datos miembro privados de la misma, hay que declarar a dicha función amiga de la clase. Para esto se declara la función anteponiendo al nombre de la misma la palabra reservada friend. Una función declarada friend de una clase CClase es una función no miembro que puede acceder a los miembros privados de la clase CClase. Por ejemplo la función VisualizarVector de CClase no puede acceder a la clase CVector. Según lo expuesto este problema puede resolverse haciendo que la función VisualizarVector sea friend de la clase CVector esto es: class CClase { void VisualizarVector( CVector &objvector); }; class CVector { friend void VisualizarVector( CVector &objvector); //... }; De este modo la definición de la función VisualizarVector puede utilizar datos miembro declarados como private en la clase CVector. Una función friend se puede declarar en cualquier sección de la clase. class C1; // declaración class C2 { public: int GetDato ( C1 &obj); //... }; class C1 { friend int C2::GetDato( C1 &); //... }; Algunas veces puede ser también necesario que una clase C1 tenga acceso directo a los datos miembro privados de otra clase C2. class C1 { friend class C2; //... }; Jerarquía de clases con Friend Una función friend de una clase base puede acceder solamente a los miembros de la clase derivada que fueron heredados de la clase base. Es decir, la amistad no se hereda. Si la función es friend de la clase derivada, esta puede acceder a los miembros públicos y protegidos de la clase base. Una clase friend de una clase base tiene acceso, además de a los miembros públicos, a los miembros privados y protegidos de la clase base, pero no tiene acceso a los miembros privados y protegidos de la clase derivada. Sin embargo, una clase friend de una clase derivada de una clase base, no tiene acceso a los miembros privados y protegidos de dicha clase base. Esto indica que no hay una relación entre ambas clases friend. Miembros static de una clase Una clase es un tipo, no un objeto (Un objeto es un ejemplar de una clase determinada) cada objeto de una determinada clase tiene su propia copia de los datos miembro de la clase a la que pertenece, pero no de las funciones miembro. Si declaramos un dato miembro de una clase como static, sólo existirá una copia de ese miembro, la cual será compartida por todos los objetos de esa clase y existirá aunque no existan objetos de esa clase. Un dato miembro de una clase declarado como static es una variable asociada con la clase, no con el objeto. Un dato miembro static existirá aunque no se hayan declarado objetos de la clase. Un dato miembro static debe ser declarado a nivel global. No se debe colocar la inicialización en un lugar donde se pueda producir más de una vez; por ejemplo, en un fichero de cabecera, ya que este puede ser cargado más de una vez. La inicialización de un dato static normalmente se colocará en el fichero fuente que contiene las definiciones de las funciones miembros de la clase. class CCuenta { //... static float Saldo; }; // inicializar variables estáticas float CCuenta::Saldo = 15,34; //... Una función miembro declarada como static podrá acceder solamente a miembros static de su propia clase. Este tipo de funciones no estarán asociadas con un objeto específico. Por lo que no tiene puntero this, y esto impide que puedan acceder a un miembro normal de su clase. Estas funciones se utilizan normalmente para actuar globalmente sobre todos los objetos de una clase. class CCuenta { //... static void SetSaldo ( float NuevoSaldo) { Saldo = NuevoSaldo;} //... static float Saldo; }; // inicializar variables estáticas float CCuenta::Saldo = 15,34; void main ( ) { //... CCuenta::SetSaldo (20); //... } Una función miembro static también puede llamarse utilizando la siguiente sintaxis: nombre_objeto.SetSaldo(20); Pero resulta engañosa ya que su acción no se ejecutará sobre un objeto en particular, sino sobre todos los objetos de la clase. En definitiva, un miembro static de una clase no está asociado con un objeto individual de la clase, sino que lo está con la propia clase. Funciones virtuales Si se llama a una función miembro que está definida en la clase base y también está definida en la clase derivada, la función que realmente es invocada dependerá del tipo de objeto o del puntero que se utilice para invocar a la misma. Si llamamos a una función que se define con el mismo nombre en varias clases, necesitamos que la función llamada pertenezca a la misma clase que el objeto referenciado. Cuando se llama a una función miembro que está definida en la clase base y en la clase derivada, la función invocada depende del tipo de puntero. El propio sistema será el que se encargue de identificar en tiempo de ejecución la clase de los objetos apuntados. El mecanismo que utiliza C++ para esto es la función virtual. Una función virtual es pues una función miembro pública o protegida de una clase base que puede ser redefinida en cada una de las clases derivadas. Una vez redefinida se accede a ella a través de un puntero o una referencia a su clase base. Para declarar una función virtual hay que colocar la palabra clave virtual antes de la declaración de la función miembro de la clase base. Las redefiniciones que realicemos de esta función en las clases derivadas no necesitan incorporar en su declaración la palabra clave virtual; serán declaradas implícitamente. class Albaran { //... virtual void Visualizar( ); //... }; La declaración de la función visualizar en la clase base le precede la palabra clave virtual. De este modo, tanto la función Visualizar de la clase base como la de las clases derivadas serán funciones virtuales. Una función global o static no podrá ser declarada virtual, ya que una función virtual sólo será llamada para objetos de su clase. Pero, una función virtual si podrá ser declarada friend de otra clase. Llamar a una función virtual La sintaxis para llamar a una función virtual es la misma que se utiliza para llamar a una función miembro normal. Una función virtual será invocada mediante una referencia o puntero a su clase base. La llamada a través de un puntero será automáticamente manipulada dependiendo del tipo del objeto apuntado. Si la llamada a la función virtual se hace a través de un objeto de una clase, el compilador resolverá la función a invocar basándose en el tipo de objeto. Como regla general una llamada a una función virtual se resolverá en función del tipo de objeto para el que es invocada, mientras que una llamada a una función no virtual se resolverá en base al tipo de puntero o de la referencia. Redefinición de una función virtual Una función virtual en una clase base continúa siendo virtual cuando es heredada. Una clase derivada puede disponer de su propia versión de la función virtual o asumirla tal cual. Cuando la clase derivada provee de su propia versión, no será necesario volver a especificar la palabra clave virtual. Una clase derivada también puede contener sus propias funciones virtuales; es decir, funciones virtuales propias y no heredadas de sus clases base. Para redefinir una función virtual en una clase derivada, dicha función debe tener el mismo nombre, número de tipos de argumentos y mismo tipo del valor retornado que la definida en la clase base. Si la redefinición difiere en el tipo del valor retornado, se producirá un error. Por otra parte, si difiere en los argumentos, la función será tratada como una función sobrecargada. Cuando en una clase base derivada se redefine una función de una clase base, se oculta la función de la clase base y todas las sobrecargadas de la misma en la clase base. Constructores y destructores virtuales C++ no admite constructores virtuales por tanto si un constructor llama a una función virtual, ésta corresponderá a la clase base, ya que todavía no se ha creado el objeto de la clase derivada. Si una clase base y sus derivadas tienen definidos sus destructores. Cuando durante la ejecución se sale fuera del ámbito del objeto de una clase derivada, se llamará primero al destructor de esa clase derivada y después al destructor de su clase base. Pero si el objeto ha sido creado dinámicamente y está referenciado por un puntero a la clase base, surge un problema, pues cuando se aplique el operador delete, el compilador llamará al destructor de la base aunque el objeto referenciado sea de una clase derivada. Para solucionar esto se debe declarar un destructor virtual, pero esto hace que todos los destructores de todas las clases derivadas sean virtuales, aunque no compartan el mismo nombre que el destructor de la clase base. De esta forma, cuando se aplica delete a un puntero de la clase base, este llamará al destructor perteneciente a la clase del objeto apuntado. Una buena práctica será colocar un destructor virtual a una clase que posea funciones virtuales, aunque este destructor no haga nada. El motivo de esto es que una clase derivada puede requerir un destructor; por ejemplo, si se deriva una clase de una clase base que define un destructor para ella. Colocando un destructor virtual en la clase base, nos aseguramos de que el destructor de la clase derivada será llamado cuando se necesite. Cómo se implementan las funciones virtuales El compilador no puede identificar durante la compilación la función que va a ser llamada por una sentencia como volumen[i]->Visualizar( ), ya que podría ser cualquiera de varias funciones diferentes. Esto pasa si la función Visualizar se declara virtual en la clase, esto hace que también sea virtual en todas las clases derivadas donde esté definida. El compilador evalúa la sentencia en tiempo de ejecución; es decir, cuando puede indicarle a qué tipo de objeto apunta Volumen[i]. Esto se conoce como asociación dinámica o asociación retrasada. En OOP decimos que un mensaje dirigido a un objeto se asocia con un método. Cuando la asociación se hace durante la compilación, se denomina asociación estática. Y cuando se hace durante la ejecución se llama asociación dinámica. La asociación dinámica sucede si el método que se asocia está definido como virtual. El compilador utilizará la asociación dinámica cuando no se puede utilizar una asociación estática, como ocurre a continuación: void CBiblioteca::VisualizarVol( ) { if (NVols > 0) for ( int i = 0; i < NVols; i ++) { Volumen[i] -> Visualizar( ); cout << endl; } } La asociación dinámica en C++ se implementa a través de una tabla de funciones virtuales o v-table. Dicha tabla está formada por un array de punteros a funciones que el compilador asocia a cada una de las clases que contienen una o más funciones virtuales. La v-table contiene un puntero a una función por cada una de las funciones virtuales de la clase. Clases abstractas y funciones virtuales puras En este blog ya se habló de clases abstractas en VB.Net como se dijo, una clase abstracta es una clase que puede utilizarse solamente como clase base para otras clases. Se trata de disponer de un mecanismo que soporte la implementación de un concepto general, como figura, del cual utilizaremos variantes concretas, cómo círculo, cuadrado y triángulo o como se explicó en .net con las piezas de ajedrez. Pensando en estas ideas,abstractas no se pueden crear objetos de una clase abstracta; lo que crearemos serán objetos de sus clase derivadas. Una clase para ser abstracta, necesita tener al menos una función virtual pura. Por ejemplo. class CPieza // clase abstracta { //... virtual void Visualizar( ) const = 0; //función virtual pura //... }; Una función virtual pura no requiere definición; es decir, no es necesario escribir el cuerpo de la función. El propósito de una declaración en la clase base es proveer a las clases derivadas de una interfaz polimórfica. Una clase derivada debe proveer una redefinición de la función virtual. Si no se hace, heredará la función virtual pura y se convertirá en una clase abstracta, pero esto no permitirá declarar objetos de la misma. Aunque visualizar se ha declarado constante (const) esto no quiere decir que sea necesario. Si se añade const, a las declaraciones y definiciones en las clases derivadas se tiene que añadir también const. No se pueden crear objetos de una clase abstracta. Si se intenta, el compilador muestra un error. Esta restricción es para prevenir cualquier llamada de un objeto a la función virtual pura. CPieza obj; // error, clase abstracta. Es posible declarar punteros y referencias a una clase abstracta, pero una clase abstracta no se puede utilizar como tipo para un argumento ni como tipo retornado por una función por una función, ni en una conversión cast. Por ejemplo. CPieza *p; // correcto CPieza &fn( CPieza &); // correcto Clases virtuales La jerarquía de clases ocasiona un problema por la derivación múltiple, la clase derivada heredará dos veces los miembros de la clase base. Por tanto cuando se crea un objeto de la clase derivada, éste contendrá dos sub objetos de la clase base. Tener dos sub objetos además de un derroche de espacio, hace que el compilador no sepa cual utilizar y genera un error. Para asegurarnos de que sólo se utiliza un sub objeto de la "clase base indirecta". Es necesario declarar la clase base común virtual cuando se está derivando en las clases base directas. class CBase // clase base { // lista de miembros }; class CDerivada1 : virtual public CBase { // lista de miembros }; class CDerivara2 : virtual public CBase { // lista de miembros }; class CDerivada12 : public CDerivada1, public CDerivada2 { // lista de miembros }; La palabra clave virtual, cuando se están derivando las clases bases directas CDerivada1 y CDerivada2, asegura que el compilador solamente pasará a la clase CDerivada12 una copia de CBase. Los constructores de una clase base virtual serán siempre llamados antes que los constructores de las clases no virtuales, y los destructores son siempre serán llamados en el orden inverso.

funciones sobrecargadas, referencias

/*Este texto no es mío. Viene de analisisyprogramacionoop.blogspot.com*/ Funciones sobrecargadasPaso de parámetros por referenciaReferencia como valor retornadoClases con miembros que son punterosArrays de objetos y de punteros a objetosPunteros a miembros de una clasePunteros como argumentos de funciones Funciones sobrecargadas Se dice que una función está sobrecargada cuando se declara una función previamente declarada con distinto número y/o tipo de parámetros pero con el mismo nombre. Es un concepto de programación orientada a objetos llamado polimorfismo. (Muchas declaraciones de una misma función). Cada función suele tener un nombre que la distingue de las demás. Pero se pueden presentar casos en los que varias funciones ejecuten la misma tarea sobre objetos de diferentes tipos, y puede ser interesante que dichas funciones tengan el mismo nombre. Por ejemplo podemos definir una función Mover para cada tipo de movimiento de una pieza distinta de ajedrez y llamar a todas las funciones con el nombre mover pero distinguirlas por el número y/o el tipo de los parámetros sean diferentes. Otro ejemplo: una función pot que calcule xy siendo x e y enteros o reales podemos hacer. int pot (int, int); double pot (double, double); double pot (int, double); double pot (double, int); El compilador buscará la función adecuada en función del tipo de parámetros que le enviamos, en caso de no encontrar una función con los mismos tipos de argumentos, realizaría las conversiones permitidas sobre los parámetros actuales, buscando así una función. No se pueden declarar dos funciones que difieran solamente en el resultado de salida. En el siguiente código se utilizan una u otra de estas funciones para calcular xy en función de los argumentos pasados en la llamada. #include #include "pot.h" void main( ) { double ad = 2.5 , bd = 1.5; int ai = 3, bi = 2; cout << pot ( ai, bi ) << endl; cout << pot ( ad, bd ) << endl; cout << pot (ai, bd ) << endl; cout << pot (ad, bi) << endl; } Paso de parámetros por referencia Cuando pasamos parámetros por valor se hace una copia de los parámetros en sus correspondientes parámetros formales. Esta operación se hace automáticamente cuando se llama a una función, esto impide que se modifiquen los parámetros actuales. Pasar parámetros por referencia, significa que no se transfieren los valores sino las direcciones de las variables donde están contenidos esos valores, de este modo los parámetros actuales se verán modificados en el mismo valor que lo hagan sus correspondientes parámetros formales. Cuando se llama a una función, los argumentos especificados en la llamada son pasados por valor, excepto si se trata de arrays, donde se pasan por referencia, ya que el nombre del array es un puntero a dicho array. Por valor se pueden traspasar constantes, variables y expresiones, y utilizando la forma de pasar parámetros por referencia solamente se permite transferir las direcciones de variables de cualquier tipo, arrays y funciones. Para pasar una variable por referencia, se pueden utilizar las dos formas explicadas a continuación: 1. Pasar la dirección del parámetro actual a su correspondiente parámetro el cual necesariamente tiene que ser un puntero. void permutar(int *, int *); void main( ) { int a = 10, b = 15; permutar(&a, &b);// se pasan las direcciones de //a y b cout << "a = "<< a << "b = " << b << endl; } // utilizando punteros void permutar( int *x, int *y) { int z = *x; *x = *y; *y = z; } 2. Definir el parámetro como una referencia al parámetro actual que se quiere pasar por referencia. Para ello, es necesario anteponer el operador & al nombre del parámetro. #include void permutar (int &, int &); void main( ) { int a = 10, b = 15; permutar (a, b); cout << " a = " << a << " b= " << b << endl; } // Utilizando referencias x e y son referencias a sus // correspondientes parámetros actuales a y b. void permutar(int &x, int &y) { int z = x; x = y; y = z; } Los resultados son los mismos que en el programa anterior. En el programa con punteros, cualquier asignación que se haga a *x afecta a la variable a. En el programa con referencias, la referencia x también tiene esa propiedad, pero sin necesitar el operador de indirección *. Hay que tener en cuenta que las referencias no pueden manipularse igual que los punteros. Con un puntero se puede distinguir el puntero de la variable apuntada utilizando el operador de indirección *. Es decir, x describe el puntero, mientras que *x describe el dato apuntado. Pero con una referencia sólo podemos referirnos al dato. Cualquier operación sobre la propia referencia se realizará sobre el dato referenciado pero nunca sobre la referencia. Referencia como valor retornado También es posible definir una función en la que el valor devuelto venga dado por una referencia a ese valor. #include int &fnx( ); int n; void main( ) { int c; fnx( ) = 25; // n = 15 c = fnx( ); // c = 15 fnx( )++ ; //n = 16 fnx( ) = fnx( ) + 3; // n =19 } //El tipo del resultado es una referencia a un int int &fnx( ) { // otro código return n; } En este código, el valor retornado por la función fnx es una referencia inicializada con la variable global n. Lo que sucede es que fnx actúa como un nombre alternativo para n. Por tanto una llamada a la función puede aparecer a la izquierda o a la derecha de un operador de asignación. Clases con miembros que son punteros Los operadores new y delete se pueden utilizar en la definición de las funciones miembro de una clase. En el código mostrado debajo, nuestro objetivo es crear un array de enteros con un número cualquiera de elementos. En este caso es inapropiado definir como miembro privado de la clase CVector un array con un número fijo de elementos. Para ello es mejor definir un puntero, pVector, que apunte a un entero para después reservar dinámicamente la cantidad de memoria necesaria para el array. class CVector { public: CVector( ); //crea un array con un nº de elementos //por defecto CVector(int ne); // crea un array de ne elementos CVector(CVector &); // inicialización con un //vector v CVector(int [ ], int ); // inicialización con un //array a // otras funciones miembro private: int *pVector; // puntero base int n_elementos; // numero de elementos }; Una clase cuyos miembros sean de tipo puntero como es CVector puede tener problemas. En el caso de que en el programa anterior no se defina un constructor copia y que la función main sea como esta: void main( ) { CVector vector1(x,7); fnEscribir(vector1); // escribe 1 2 3 4 5 6 7 // el siguiente bloque define vector2 { CVector vector2 = vector1; fnEscribir(vector2); //escribe1 2 3 4 5 6 7 } // vector2 ha sido destruido int *pi = new int[5]; for (int i = 0; i < 5; i++) pi[i] = i * 10; // pi apunta a un array de valores 0 10 20 30 40 fnEscribir(vector1); //escribe 0 10 20 30 40 50 } En el caso de que en el programa anterior no definamos un constructor copia, el constructor copia definido por defecto será de la forma. CVector::CVector(CVector &v) // constructor copia { n_elementos = v.n_elementos; pVector = v.pVector; } Ahora v.pVector es un puntero. El resultado de esta asignación es que pVector y v.pVector apuntan a la misma dirección de memoria. Por tanto cuando ejecutemos el código: CVector vector2 = vector1; // llama al constructor copia El miembro pVector de vector1 y el miembro pVector de vector2 apuntarán al mismo array de enteros. Por lo que cualquier modificación en uno de los objetos afecta a ambos. Si salimos fuera del ámbito de vector2 se llama al destructor de la clase CVector y se liberan los dos bloques de memoria correspondientes a ese objeto ( su estructura interna y el array de objetos ). De este modo cualquier operación posterior con el objeto vector1 podría dar resultados inesperados, ya que pVector de vector1 apuntaba a la misma localización de memoria y ésta ha sido liberada. Por otra parte cuando se sale fuera del ámbito de vector1 se invoca de nuevo al destructor, y este intentará liberar de nuevo el bloque de memoria ocupado por el array de enteros, esto provocará resultados inesperados. En la función main anterior al destruir el vector2, la siguiente instrucción asigna memoria para un array de enteros irreferenciado por pi. Se da la circunstancia de que la memoria asignada ha coincidido con la liberada. Pero en el vector1, además de no tener memoria asignada para su array de enteros, tampoco devuelve los resultados esperados. Esto pone de manifiesto que la memoria se libera, pero los punteros conservan sus valores. Si nuestra intención no fuera esta, sino crear un nuevo array para cada objeto, el constructor debería ser como el que sigue a continuación. CVector::CVector(CVector &v) //constructor copia { n_elementos = v.n_elementos; pVector = new int [n_elementos]; for ( int i = 0; i*. El operador .* liga su segundo operando a su primer operando, el cual debe ser un puntero a un miembro, el cual que debe ser un objeto. En este caso tenemos: objeto.*puntero_a_miembro Es decir. CCalificacion Alumno; float n = Alumno.*pmCalificacion; Si el primer operando es un puntero a un objeto, entonces utilizaremos el operador ->*. La sintaxis será: puntero_a_objeto ->* puntero_a_miembro De este modo temenos. CCalificacion *pAlumno; //... (pAlumno ->*pmfnSetCalificacion)(n); La precedencia de los paréntesis ( ) es mayor que la de .* y ->*, por eso son necesarios. No es posible definir un puntero a un miembro declarado como static, ya que este pertenece a la clase misma y no a un objeto en particular de la clase. Punteros como argumentos de funciones Un buen motivo para pasar un puntero a una función en vez de pasarle una copia del valor de la variable es ahorrar tiempo y espacio. Al pasar una copia de una variable grande, como un array o una estructura de datos grande, requiere una gran cantidad de espacio en la pila y de tiempo. Se puede mejorar el rendimiento de un programa pasando la dirección de esa variable y permitir que la función acceda a sus elementos a través de un puntero. Otro motivo para usar punteros como parámetros es para que una función devuelva más de un sólo valor. Pues una función sólo puede devolver un valor por medio de su sentencia return. Pero si se utilizan parámetros de tipo puntero, se puede dar a la función acceso (y de este modo poder cambiar) los valores de una variable. Para ello, la función llamada tendrá que parar argumentos como una dirección en lugar de como un valor.