Bloque de Colores

Usando linked lists para trabajar con polinomios

Icono de Post Agosto 13 2009 :: Estructuras de datos, c ::

Uno de las cosas fascinantes del Mathematica y ( ahora del wolfram alpha ) es como resuelve y además simplifica expresiones matemáticas (por ejemplo: derivando funciones y dando cada uno de los pasos). Se usa de esta manera la computadora no para hacer cálculos numéricos ( que arrojen como resultado un número ) sino para computación simbólica ( cálculos que arrojan como resultado una expresión matemática ).

La pregunta es como una maquina que guarda resultados en memoria en forma de expresiones binarias ( esencialmente números, aunque pueden representar otra cosa ) para hacer esto. La respuesta es que la representación de formulas matemáticas puede ser realizada a través de diferentes estructuras de datos y es manipulando estas estructuras que se pueden realizar estos cómputos.

Como un ejemplo básico de esto acá va un ejemplo sacado de C & Data Structures sobre como se pueden hacer operaciones sobre polinomios.

Una estructura de datos para guardar un polinomio y las funciones para llenarlo y mostrarlo.



# include <stdio.h>

# include <stdlib.h>

struct pnode

   {

      int exp;

      int coeff;

      struct pnode *link;

   };

struct pnode *insert(struct pnode *p, int e,int c) {

   struct pnode *temp;

   temp = p;

   if(p==NULL) {

      p=(struct pnode *)malloc(sizeof(struct pnode));

      if(p==NULL) {

         printf("Error de alocación de memoria\n");

         exit(0);

      }

      p-> exp = e;

      p->coeff = c;

      p-> link = NULL;

   } else {

        //Si tienen el mismo exponente se suman los coeficientes en vez de agregar un nodo.

       if(p->exp == e) {

            p->coeff = p->coeff + c;

       } else {

            p->link = insert(p->link, e, c);

       }

   }

   return (p);

}



void printpoly ( struct pnode *p ) {

      printf("The polynomial is\n");

      while (p!= NULL) {

        if(p->exp != 1) {

            printf("%dx^%d\t", p->coeff, p-> exp);

        } else {

            printf("%dx\t", p->coeff);

        }

        p = p-> link;

      }

      printf("\n");

}

Nota: en la función de insertar si un nuevo termino tiene el mismo exponente que uno anterior se lo suma en vez de agregarlo. Esto es una de las modificaciones al código del libro.

Ordenando el polinomio



/* a function to sort a list */

struct pnode *sortpoly(struct pnode *p)

{

   struct pnode *temp1,*temp2,*max,*prev,*q;

   q = NULL;

   while(p != NULL) {

      prev = NULL;

      max = temp1 = p;

      temp2 = p -> link;

      while ( temp2 != NULL ) {

         if(max -> exp < temp2 -> exp) {

            max = temp2;

            prev = temp1;

         }

         temp1 = temp2;

         temp2 = temp2-> link;

      }

      if(prev == NULL) {

            p = max -> link;

       }  else {

            prev -> link = max -> link;

       }

       max -> link = NULL;

        if( q == NULL) {

            q = max;

        } else {

            temp1 = q;

            while( temp1 -> link != NULL) {

                temp1 = temp1 -> link;

            }

            temp1 -> link = max;

        }

    }

    return (q);

}

Una Función para sumar 2 polinomios



/* A function to add two polynomials */

   struct pnode *polyadd(struct pnode *p, struct pnode *q)

   {

      struct pnode *r = NULL;

      int e;

      int c;

      while((p!=NULL) && (q != NULL)) {

            //p mayor exponente que q

            if(p->exp > q->exp) {

                r = insert(r,p->exp,p->coeff);

                p = p->link;



            //q mayor exponenete que p

            } else if(p->exp < q->exp) {

                r = insert(r,q->exp,q->coeff);

                q = q->link;



            //mismo exponente

            } else {

                c = p->coeff + q->coeff;

                e = q->exp;

                r = insert( r, e,c);

                p = p->link;

                q = q->link;

            }

      }

   //Lo que quede de ambos polinomios

    while(p != NULL) {

        r = insert( r, p->exp,p->coeff);

        p = p->link;

    }

    while(q!=NULL) {

        r = insert( r, q->exp,q->coeff);

        q = q->link;

    }

   return(r);

}

Una función para multiplicar polinomios.

Teniendo una función insertar que sume los términos del mismo exponente y una función de suma es muy fácil agregar una función que multiplique polinomios.



struct pnode *polymult(struct pnode *p, struct pnode *q) {

    struct pnode *r, *r2, *temp;

    r = NULL;

    temp = q;

    while(p != NULL) {

        temp = q;

        while(temp != NULL) {

            r = insert(r, (temp->exp + p->exp), (temp->coeff * p->coeff));

            temp = temp->link;

        }

        p = p->link;

    }

    return r;

}

Probando el programa



int main()

   {

         int e;

         int n,i, c;

         struct pnode *poly1 = NULL ;

         struct pnode *poly2=NULL;

         struct pnode *result;

         printf("Enter the terms in the polynomial1 \n");

         scanf("%d",&n);

         i=1;

         while ( n-- > 0 )

         {

      printf( "Enter the exponent and coefficient of the term number %d\n",n);

               scanf("%d %d",&e,&c);

               poly1 = insert ( poly1,e,c);

         }

        printf("Enter the terms in the polynomial2 \n");

         scanf("%d",&n);

         i=1;

         while ( n-- > 0 )

         {

      printf( "Enter the exponent and coefficient of the term number %d\n",n);

               scanf("%d %d",&e,&c);

               poly2 = insert ( poly2,e,c);

         }

         poly1 = sortpoly(poly1);

         poly2 = sortpoly(poly2);

         printf("The polynomial 1 is\n");

         printpoly ( poly1 );

         printf("The polynomial 2 is\n");

         printpoly ( poly2 );

         result = polyadd(poly1,poly2);

         printf("The result of addition is\n");

         printpoly ( result );

         result = polymult(poly1, poly2);

         printf("The result of multiplication is\n");

         printpoly ( result );

         return 0;

   }

Resultados


Enter the terms in the polynomial1 
5
Enter the exponent and coefficient of the term number 4
5 5
Enter the exponent and coefficient of the term number 3
4 4
Enter the exponent and coefficient of the term number 2
3 3
Enter the exponent and coefficient of the term number 1
2 2
Enter the exponent and coefficient of the term number 0
1 1
Enter the terms in the polynomial2 
2
Enter the exponent and coefficient of the term number 1
2 2
Enter the exponent and coefficient of the term number 0
1 1
sdfsdfThe polynomial 1 is
The polynomial is
5x^5	4x^4	3x^3	2x^2	1x	
The polynomial 2 is
The polynomial is
2x^2	1x	
The result of addition is
The polynomial is
5x^5	4x^4	3x^3	4x^2	2x	
The result of multiplication is
The polynomial is
10x^7	13x^6	10x^5	7x^4	4x^3	1x^2	

Pensando en todo esto es interesante en como en MathML se usan estructuras arboreas ( MathML esta basado en XML ) para representar expresiones matematicas y las implicaciones que puede tener a futuro para la automatización de determinado tipo de calculos.

Referencias

C & Data Structures, Deshpande, Kakde; Charles River Media, 2004

Analizando los punteros de una linked list

Icono de Post Agosto 10 2009 :: Estructuras de datos, c ::

Uno de los temas que es difícil de entender en C es el tema de los punteros y como se relacionan con las estructuras de datos. Lo difícil ( o por lo menos fue difícil para mi que venia de lenguajes dinámicos ) es entender que los punteros guardan direcciones a memoria y a que a partir de esto se puede hacer el entrelazamiento de diferentes estructuras apuntadas. Voy a usar los ejemplo de linked list de C & Data Structures para mostrar como se da esto.

Primero creamos la estructura y una función para insertar elementos en la misma:

struct node {

   int data;

   struct node *link;

};



/*Insert en versión recursiva */

struct node *insert(struct node *p, int n) {

   if(p==NULL) {

      p=(struct node *)malloc(sizeof(struct node));

      if(p==NULL) {

        printf("Allocation Error\n");

         exit(0);

      }

      p-> data = n;

      p-> link = NULL;

   } else {

       p->link = insert(p->link,n);

    }

      return(p);

}

Nota: Se hace la búsqueda del ultimo nodo para insertar en forma recursiva.

Luego hago una función para mostrar la lista:

void printnode( struct node *p) {

    printf("Memory Space: %p\t Value: %d \t Link: %p\t\n",p, p-> data, p-> link);

}



void printlist ( struct node *p ) {

      printf("The data values in the list are\n");

      while (p!= NULL) {

         printnode(p);

         p = p-> link;

      }

}

La función printnode, muestra primero la dirección de memoria de la estructura, después su valor y finalmente la dirección de memoria a la que apunta, de esta manera podemos ver como se enlazan los nodos dentro de la lista.

Agregando y borrando nodos.

   /* a function which inserts a newly created node after the specified

node */

    //node_no empieza en 0 como en los arrays

   struct node * newinsert ( struct node *p, int node_no, int value ) {

      struct node *temp, * temp1;

      int i;

      temp = p;



      for(i=0; i<node_no && temp->link != NULL; i++) {

            temp = temp->link;

      }

        if(i != node_no ) {

            printf("Se pide insertar en un nodo mayor a la cantidad existente, se agrega tras el ultimo nodo existente.\n");

        }

        temp1 = ( struct node * )malloc ( sizeof ( struct node ));

        if ( temp1 == NULL ) {

            printf( " Cannot allocate \n");

            exit (0);

        }

        temp1->data = value;

        temp1->link = temp->link;

        printf("The newly created node to be inserted is:\n");

        printnode(temp1);

        temp->link = temp1;

        return(p);

   }



   struct node * erasenode( struct node *p, int node_no) {

        struct node *temp, *prev;

        int i;

        temp = p;

        if(node_no == 0) {

            temp = p->link;

            free(p);

            p = temp;

        } else {

            i = 0;

            while(i<node_no && temp->link != NULL) {

                prev = temp;

                temp = temp->link;

                i++;

            }

            if(i != (node_no)) {

                printf("Se pide borrar un nodo que no existe.\n");

                exit(0);

            }

            prev->link = temp->link;

            free(temp);

        }

        return(p);

   }

Corriéndolo en el main:

int main()

{

      int n;

      int x;

      struct node *start = NULL ;

      printf("Enter the nodes to be created \n");

      scanf("%d",&n);

      while ( n-- > 0 )

      {

              printf( "Enter the data values to be placed in a node\n");

         scanf("%d",&x);

         start = insert ( start,x);

      }

      printf("The created list is\n");

      printlist ( start );

      start = newinsert(start, 2, 7);

      printlist (start);

      start = erasenode(start, 2);

      printlist ( start );

      return 0;

}

Resultado en pantalla:

Enter the nodes to be created 
5
Enter the data values to be placed in a node
5
Enter the data values to be placed in a node
4
Enter the data values to be placed in a node
3
Enter the data values to be placed in a node
2
Enter the data values to be placed in a node
1
The created list is
The data values in the list are
Memory Space: 0x81a5008	 Value: 5 	 Link: 0x81a5018	
Memory Space: 0x81a5018	 Value: 4 	 Link: 0x81a5028	
Memory Space: 0x81a5028	 Value: 3 	 Link: 0x81a5038	
Memory Space: 0x81a5038	 Value: 2 	 Link: 0x81a5048	
Memory Space: 0x81a5048	 Value: 1 	 Link: (nil)	
The newly created node to be inserted is:
Memory Space: 0x81a5058	 Value: 7 	 Link: 0x81a5038	
The data values in the list are
Memory Space: 0x81a5008	 Value: 5 	 Link: 0x81a5018	
Memory Space: 0x81a5018	 Value: 4 	 Link: 0x81a5028	
Memory Space: 0x81a5028	 Value: 3 	 Link: 0x81a5058	
Memory Space: 0x81a5058	 Value: 7 	 Link: 0x81a5038	
Memory Space: 0x81a5038	 Value: 2 	 Link: 0x81a5048	
Memory Space: 0x81a5048	 Value: 1 	 Link: (nil)	
The data values in the list are
Memory Space: 0x81a5008	 Value: 5 	 Link: 0x81a5018	
Memory Space: 0x81a5018	 Value: 4 	 Link: 0x81a5058	
Memory Space: 0x81a5058	 Value: 7 	 Link: 0x81a5038	
Memory Space: 0x81a5038	 Value: 2 	 Link: 0x81a5048	
Memory Space: 0x81a5048	 Value: 1 	 Link: (nil)	

Se ve aca como en la primera corrida los nodos estan ubicados uno tras otro en la memoria, pero al agregar un nuevo nodo se salta en el tercer nodo ( en este caso, Memory Space: 0x81a5028 Value: 3 Link: 0x81a5058 ) se salta al agregado ( Memory Space: 0x81a5058 Value: 7 Link: 0x81a5038 ) y del agregado al que antes existia contiguamente ( Memory Space: 0x81a5038 Value: 2 Link: 0x81a5048 ). Algo similar ocurre cuando se borra un nodo.

Revirtiendo y ordenando la lista.

/* a function to sort reverse list */

struct node *reverse(struct node *p) {

   struct node *prev, *curr;

   prev = NULL;

   curr = p;

   while (curr != NULL) {

      p = p-> link;

      curr-> link = prev;

      prev = curr;

      curr = p;

   }

   return(prev);

}



/* a function to sort a list */

struct node *sortlist(struct node *p)

{

   struct node *temp1,*temp2,*min,*prev,*q;

   q = NULL;

   while(p != NULL) {

      //Encontrar el menor de la lista p

      prev = NULL;

      min = temp1 = p;

      temp2 = p -> link;

      while ( temp2 != NULL ) {

              if(min -> data > temp2 -> data) {

                     min = temp2;

                     prev = temp1;

            }

         temp1 = temp2;

         temp2 = temp2-> link;

      }



      //Cambiar el link en la lista p del previo al menor al siguiente del menor

      if(prev == NULL) {

             p = min -> link;

      } else {

             prev -> link = min -> link;

      }



      //Agregar el menor a q

      min -> link = NULL;

      if( q == NULL){

            q = min; /* moves the node with lowest data value in the list pointed to by p to the list pointed to by q as a first node*/

       } else {

            temp1 = q;

            /* traverses the list pointed to by q to get pointer to its last node */

            while( temp1 -> link != NULL) {

                         temp1 = temp1 -> link;

            }

            temp1 -> link = min; /* moves the node with lowest data value in the list pointed to by p to the list pointed to by q at the end of list pointed by q*/

      }

   }

   return (q);

}

Vemos la impresión en pantalla de como quedan ordenados los nodos:

The sorted list is
The data values in the list are
Memory Space: 0x8c03048	 Value: 1 	 Link: 0x8c03038	
Memory Space: 0x8c03038	 Value: 2 	 Link: 0x8c03018	
Memory Space: 0x8c03018	 Value: 4 	 Link: 0x8c03008	
Memory Space: 0x8c03008	 Value: 5 	 Link: 0x8c03058	
Memory Space: 0x8c03058	 Value: 7 	 Link: (nil)	
The reversed list is
The data values in the list are
Memory Space: 0x8c03058	 Value: 7 	 Link: 0x8c03008	
Memory Space: 0x8c03008	 Value: 5 	 Link: 0x8c03018	
Memory Space: 0x8c03018	 Value: 4 	 Link: 0x8c03038	
Memory Space: 0x8c03038	 Value: 2 	 Link: 0x8c03048	
Memory Space: 0x8c03048	 Value: 1 	 Link: (nil)	

Referencias

C & Data Structures, Deshpande, Kakde; Charles River Media, 2004

Implementación de una Cola ( Quee ) sobre un array

Icono de Post Marzo 13 2009 :: Estructuras de datos, c ::

Esta es una variación de la implementación en el libro C & Data Structures de una cola ( FIFO ) construida sobre un array. Me intereso esa estructura de datos por 2 cosas. Primero, el hecho de que la estructura de datos era más que el array donde se guardaban los datos, necesitándose una serie de variables asociados para usarlo y segundo el uso de la aritmética mod( n ) para moverse entre las posiciones del array.

Sin embargo, existen una serie de errores en la implementación del ejemplo y además considerando que se utilizan un conjunto de variables para definir el funcionamiento de la estructura, lo lógico sería agrupar todas estas variables dentro de un struct de C. Así que esta es mi implementación, que además fue un buen ejercicio para repasar la sintaxis de punteros y de structs en C:


#include <stdio.h>
#include <stdlib.h>
#define MAX 10 /* The maximum size of the queue */

struct ca {
	int cola[ MAX ];
	int fin;
	int frente;
	int num_elems;
};
typedef struct ca cola_array;


void insert( cola_array *carr, int value ) {
	if(carr->frente == carr->fin && carr->num_elems > 0) {
		printf(”Cola Llena\n”);
	} else {
		carr->cola[ carr->fin ] = value;
		carr->fin = ( carr->fin + 1) % MAX;
		carr->num_elems ++;
	}
}

int delete( cola_array *carr ) {
	int temp;
	if( carr->num_elems == 0 ) {
		printf(”Cola Vacia\n”);
		return -1;
	} else {
		temp = carr->cola[ carr->frente ];
		carr->frente = ( carr->frente + 1) % MAX;
		carr->num_elems–;
		return temp;
	}
}

void listar( cola_array *carr ) {
	int x;
	if( carr->num_elems == 0 ) {
		printf(”Cola Vacia”);
	} else {
		printf(”Numero de Elementos: %d\t Frente: %d\t Fin: %d\n”, carr->num_elems, carr->frente, carr->fin);
		for( x = 0; x < carr->num_elems; x++) {
			printf(”%d\t”, carr->cola[ ( x + carr->frente ) % MAX  ]);		
		}
	}
	printf(”\n”);
}

int main()
{
	int menu, value;
	cola_array carr;
	carr.fin = carr.frente = carr.num_elems = 0;
	do {
		printf(”Seleccione:\n”);
		printf(”1. Insertar\n”);
		printf(”2. Borrar\n”);
		printf(”3. Listar\n”);
		printf(”4. Salir\n”);
		scanf(”%d”, &menu);
		switch(menu) {
			case 1: 
				printf(”Valor a Insertar (Mayor o Igual que 0): \n”);
				scanf(”%d”, &value);
				insert( &carr, value);
			break;
			case 2: 
				value = delete( &carr );
				if(value >= 0) {
					printf(”Valor borrado: %d \n”, value);
				}
			break;
			case 3:
				listar( &carr );
			break;
			case 4: 
				return(0);
			break;
			default:
				printf(”Opcion no Valida.\n”);
			break;
		}
	} while( menu != 4 );
	return(0);
}

La parte que me gusto hacer es:


		for( x = 0; x < carr->num_elems; x++) {
			printf("%d\t", carr->cola[ ( x + carr->frente ) % MAX  ]);		
		}

Y en particular ( x + carr->frente ) % MAX por el uso de la aritmética mod( n ) que me hizo recordar al concepto de clases de equivalencias de Algebra.

Explicación

Supongamos que la cola tiene 5 elementos (sobre un máximo de 10), pero como la cola ya se estuvo utilizando quedaron ubicados los 2 primeros en las posiciones 8 y 9 del array y los 3 últimos en las posiciones 0 a 2. El loop va a iterar a x de 0 a 4, cuando x este en 0 ( x + carr->frente ) % MAX va a equivaler a 8, cuando este en 1 a 9; pero cuando llegue a 2, ( x + carr->frente ) equivale a 10 y 10 % MAX equivale a 0.

Esto es así porque en la 0/10, 10/10, 20/10.. etc, tienen exactamente el mismo resto, 0; de la misma manera que 1/10, 11/10, 21/10.. etc tienen todos resto 1. ( La operación % establece clases de equivalencia sobre N). Lo que permite moverse circularmente sobre el array.

Resumen de Sintaxis de Estructuras en C

Icono de Post Deciembre 31 2008 :: Apuntes, c ::

Declaración de estructuras y acceso a miembros.


#include <string.h>
#include <stdio.h>

struct ejemplo {
	int miembro1;
	char miembro2[30];
};

main () {

	struct ejemplo var1;

	var1.miembro1 = 10;
	strcpy(var1.miembro2, “Hola que Tal.”);
	printf(”miembro 1: %d \t miembro 2: %s\n”, var1.miembro1, var1.miembro2);
}

Usando Typedef para simplificar la declaración de estructuras.


#include <string.h>
#include <stdio.h>

struct ejemplo {
	int miembro1;
	char miembro2[30];
};
typedef struct ejemplo ejemp;

main () {

	ejemp var2;

	var2.miembro1 = 20;
	strcpy(var2.miembro2, “Otro string.”);
	printf(”miembro 1: %d \t miembro 2: %s\n”, var2.miembro1, var2.miembro2); 

}
Se puede hacer la misma declaración del typedef de la siguiente manera:

typedef struct ejemplo {
	int miembro1;
	char miembro2[30];
} ejemp;

Punteros a estructuras


#include <string.h>
#include <stdio.h>

struct ejemplo {
	int miembro1;
	char miembro2[30];
};

main () {

	struct ejemplo var1, *var2;

	//var2 es un puntero a var1
	var2 = &var1;

	var1.miembro1 = 10;
	strcpy(var1.miembro2, “Hola que Tal.”);

	printf(”miembro 1: %d \t miembro 2: %s\n”, (*var2).miembro1, (*var2).miembro2);
	//O lo que es equivalente y mas usual
	printf(”miembro 1: %d \t miembro 2: %s\n”, var2->miembro1, var2->miembro2); 

}
Nota: (*var2).miembro1 es equivalente a var2->miembro1

Asignar memoria para un puntero a estructura.


#include <string.h>
#include <stdio.h>
#include <stdlib.h>

struct ejemplo {
	int miembro1;
	char miembro2[30];
};

main () {

	struct ejemplo *var1;

	var1 = (struct ejemplo*)malloc( sizeof(struct ejemplo));

	var1->miembro1 = 10;
	strcpy(var1->miembro2, “Hola que Tal.”);

	printf(”miembro 1: %d \t miembro 2: %s\n”, var1->miembro1, var1->miembro2); 		

}

Estructuras dentro de estructuras.



#include <string.h>
#include <stdio.h>
#include <stdlib.h>

struct ejemplo {
	int miembro1;
	char miembro2[30];
};

struct ejemplo2 {
	struct ejemplo ej;
}

main () {

	struct ejemplo2 var1, *var2;

	var1.ej.miembro1 = 10;
	strcpy(var1.ej.miembro2, “Hola que Tal.”);

	printf(”miembro 1: %d \t miembro 2: %s\n”, var1.ej.miembro1, var1.ej.miembro2); 		

	//var2 es puntero a var1
	var2 = &var1;

	printf(”miembro 1: %d \t miembro 2: %s\n”, var2->ej.miembro1, var2->ej.miembro2); 		

}

Espacio de memoria ocupado por una estructura.


#include <string.h>
#include <stdio.h>

struct ejemplo {
	int miembro1;
	int miembro2;
	int miembro3;
	char miembro4[30];
	char miembro5[30];
};

main () {

	struct ejemplo var1;

	var1.miembro1 = 10;
	printf(”Dirección de miembro 1: %u \t Dirección de miembro 2: %u \t Dirección de miembro 3: %u\n”, &var1.miembro1, &var1.miembro2, &var1.miembro3);
	printf(”Dirección de miembro 4: %u \t Dirección de miembro 5: %u\n”, &var1.miembro4, &var1.miembro5);
}
Resultados

Dirección de miembro 1: 3219667864 	 Dirección de miembro 2: 3219667868 	 Dirección de miembro 3: 3219667872
Dirección de miembro 4: 3219667876 	 Dirección de miembro 5: 3219667906
Explicación: Las direcciones utilizadas por una estructura son continuas. En este caso tomo una dirección especifica 3219667864 para el comienzo de la misma, la dirección del primer miembro. Como los ints en mi implementación de C en Ubuntu tienen como tamaño 4 bytes, el segundo, tercer y cuarto miembro tienen las direcciones 3219667868, 3219667872, 3219667876 respectivamente. Como el cuarto miembro es un array de chars y los chars tiene tamaño de 1 byte, el quinto miembro tiene dirección 30 bytes luego del cuarto en 3219667906.

Referencias

The C Programming Language, Segunda Edición; Kernighan, Ritchie; Prentice Hall Hispanoamerica, 1991

C & Data Structures, Deshpande, Kakde; Charles River Media, 2004

Resumen de sintaxis de Punteros en C

Icono de Post Octubre 15 2008 :: Apuntes, c ::
Copiado directamente de K & R.

	int x = 1, y = 2, z[10];
	int *ip; /* ip es un puntero a int */

	//Se usa &var para referenciar a la dirección de la variable a la que se quiere apuntar
	ip = &x; /* ip apunta a x */

	//Se usa *puntero para referenciar el valor de la dirección a la que apunta el puntero
	y = *ip; /* y es ahora 1 ( el valor de x) */

	*ip = 0; /* x es ahora 0 */

	ip = &z[0]; /* ip ahora apunta a z[0] */

Un ejemplo de una función que toma punteros como parámetros


void swap( int *px, int *py) {
	int temp;

	temp = *px;

	*px = *py;

	*py = temp;
}
Llamando a la función y pasando los valores por referencia

	swap(&x, &y);

	printf("\n");
	printf("%d %d\n", x, y);
	//Imprime 1, 0
Que pasa si imprimo un puntero directamente sin usar * ?

....
test1(&x, &y);
....

void test1( int *px, int *py) {
	//Imprime 1 0
	printf("%d %d\n", *px, *py);
	//Imprime la dirección de x y casteada a Int
	printf("%d %d\n", px, py);
}

Arreglos, Punteros y aritmética de punteros


#include 

int main() {
	int a[10], x;
	int *pa;
	//Relleno el array a con [0-9]
	for( x=0; x<10; x++ ) {
		a[x] = x;
		printf(”%d\n”, a[x]);
	}

	pa = &a[0];
	//pa es ahora un puntero que apunta a la primer dirección del arreglo

	printf(”%d %d\n”, a[7], *(pa+7) );
	//Imprime 7 7.. porque luego de pa= &a[0], a[i] es igual a *(pa + i)
}

Punteros y funciones


//Toma como parámetro 2 punteros a char
int strlen( char *s, char *t) {
	while((*s++ = *t++) != '\0');
}
....

//Función que devuelve un apuntador a int
int *f();

Referencias

The C Programming Language, Segunda Edición; Kernighan, Ritchie; Prentice Hall Hispanoamerica, 1991

Macros en C

Icono de Post Septiembre 21 2008 :: c ::
Los Macros (asi como los includes) se manejan en el preprocesador de C que es un paso anterior separado de la compilación. Y permiten basicamente la definición de un termino que será remplazando siempre en el momento de la compilación por la expresión que se defina. El ejemplo típico es la definición de constantes, ej:

#define MAX_SIZE 1000

Macros con Variables

También pueden usarse Macros con variables, ej:

#include <stdio.h>
#define max(A, B) ((A) > (B) ? (A):(B))
int main() {
	int x = 20;
	int y = 10;
	int z;

	//Gets transformed to z = ((x + y) > (y - 10) ? (x + y):(y - 10));
	z = max(x + y, x - 10);
	//prints 30
	printf("%d\n", z);

	//Gets transformed to z = ((++x) > (++y) ? (++x):(++y);
	z = max( ++x, ++y);
	// ++ is doubly evaluated
	//So x and y are doubly incremented
	//prints 22
	printf("%d\n", z);

}
Nota: Hay que tener cuidado con los efectos secundarios que se pueden causar ver ejemplo de ++x

Cuidado con comerse los parentesis y los efectos secundarios que se pueden causar


#include <stdio.h>
#define square(x) x * x
#define square_works(x) (x) * (x);

int main() {
	int x = 20;
	int y = 10;
	int z;

	//Gets transformed to z = x + y * x + y
	//And because of precedence order is z = x + (y * x) + y
	z = square(x + y);
	//prints 230
	printf("%d\n", z);

	//Gets transformed to z = (x + y) * (x + y)
	z = square_works(x + y);
	//prints 900
	printf("%d\n", z);

}
Para desdefinir un Macro se puede usar undef y se puede usar ( también para includes o defines ) estructuras condicionales con if, ej:

#if SYSTEM_BITS == 64
	#define MAX_SIZE = 2^63
#elif
	#define MAX_SIZE = 2^31
#endif

Referencias

The C Programming Language, Segunda Edición; Kernighan, Ritchie; Prentice Hall Hispanoamerica, 1991

Alcance de Variables en C

Icono de Post Septiembre 20 2008 :: c ::
Este post es similar en temática a mis posts de OOP y Javascript Global Variables Weirdness salvo que sobre C.

Variables Automáticas

Se utilizan dentro del contexto de un bloque de código. Se inicializan y destruyen cuando termina el código de ese bloque. El típico ejemplo es las variables dentro de una función.

int main() {
	double i;
	.......
	my_function();
	....
}
int my_function() {
	int i = 15;
	......
}
En este ejemplo, la variable i de la función my_function es independiente de la i de la función main.

Variables Globales

Son accesibles y compartidas por todas las funciones definidas dentro de un archivo. Ej:

#include <stdio.h>
int i = 0;
int main() {
	printf("%d ", i);
	i++;
	my_function();
	printf("%d ", i);
}
int my_function() {
	printf("%d ", i);
	i = 15;
}
Imprime

0 1 15

Variables Externas

Si se quiere utilizar una variable global definida en un archivo externo, es necesario declararla usando extern . Ej:

Archivo vars-1.c


int i = 15;

Archivo vars-2.c


#include <stdio.h>
extern int i; 

int main() {
	printf("%d ", i);
	i++;
	printf("%d ", i);
	return 1;
}
Nota: Para compilar múltiples archivos ver Compiling multiple source files. En este caso se uso: gcc -Wall vars-1.c vars-2.c -o "vars" Se podría haber usado la variable dentro de un bloque de código, por ejemplo una función, para lo que es necesario hacer un archivo de header:

Archivo vars.h

void my_function();

Archivo vars-1.c


int i = 15;
char ch = 'a';

Archivo vars-2.c


#include <stdio.h>
#include "vars.h"
extern int i; 

int main() {
	printf("%d ", i);
	i++;
	printf("%d ", i);
	my_function();
	return 1;
}
void my_function(){
	extern char ch;
	printf("%c ", ch);
}
El archivo de header solamente tiene el prototipo de la función.

Variables Estáticas

Hace que una variable global limita su alcance al archivo en el que esta declarada. Por otro lado usada, dentro de una función, mantiene su valor en las diferentes llamadas a la función. El ejemplo típico es una función recursiva:

#include <stdio.h>
void my_function( int a );
int main() {
	my_function(0);
	printf("\n");
	my_function( 10 );
}
void my_function( int a ) {
	static int i;
	int b = 10;
	i += a;
	printf("| %d - %d |\t", i);
	i++;
	b++;
	if( i < 5 ) my_function( 0 );
}
Devuelve:

| 0 - 10 |	| 1 - 10 |	| 2 - 10 |	| 3 - 10 |	| 4 - 10 |
| 15 - 10 |
Nota: Las variables estáticas se inicializan en 0. Nota: Cada vez que se llama a una función sus variables no estáticas se inicializan de cero , incluso cuando se llama a esta función recursivamente ( se crea un espacio de memoria nuevo para guardar sus variables automáticas para cada invocación de la función ). Esto se ejemplifica claramente por el comportamiento de la variable b.

Algunas conclusiones

Fue muy interesante ver esto, sobre todo al especular sobre como desarrollar un programa a gran escala sobre C. Por ejemplo, se podría considerar a un archivo con sus funciones definidas y sus variables globales propias ( no accesibles para otros archivos ) como un proto-objeto donde sus variables globales son sus atributos y sus funciones son sus métodos. Ahora, como la única forma de acceder a una variable de otro archivo-proto-objeto es a través de extern, se vuelve hyper evidente lo complicado que se transforma desarrollar arquitecturas complejas lidiando con esto. Repentinamente el paradigma OOP se transforma en MARAVILLOSO después de pensar un poco en esto!!!!

Algunos agregados más

Existe la opción register al declarar una variable para guardarla directamente en los registros de la maquina si va a ser muy utilizada. Se puede declarar un array de la siguiente forma!!

int primos[] = { 2 , 3 , 5 , 7, 11 , 13 }

Referencias

The C Programming Language, Segunda Edición; Kernighan, Ritchie; Prentice Hall Hispanoamerica, 1991

Operadores Bitwise ( a nivel del Bit ) en C

Icono de Post Septiembre 12 2008 :: Apuntes, c ::

Una de las cosas nuevas que aprendí en los primeros capítulos de K & R, es la explicación de operaciones Bitwise. Lo cierto es que si bien sabia que existían como nunca había tenido la necesidad de utilizarlas, nunca les preste atención. Este es un pequeño resumen por si las necesito a futuro.

Operaciones & , | , ~ y ^

& ( conjunción Lógica o multiplicación Booleana a nivel del bit )
Sean a, b dos cadenas de bits del mismo tamaño ( por ejemplo integer ). La operación a & b realiza una operación de Conjunción Lógica sobre cada uno de los bits de ambas cadenas, devolviendo una nueva cadena de bits de tamaño equivalente a a,b con un 1 en la posición donde tanto a como b tenian un 1 y un 0 en cualquier otro caso. Ej:

a     = 10010101 
b     = 00110100
a & b = 00010100
| ( disyunción Lógica o suma Booleana a nivel del bit )
La operación a | b devuelve un 1 en la posición donde a o b ( o ambos ) tienen un 1 y un 0 cuando ambas tienen cero. Ej:

a     = 10010101 
b     = 00110100
a | b = 10110101
~ ( negación Lógica a nivel del bit )
Sea a una cadena de bits. La operación ~a invierte los 1s y 0s de a. Ej:


a  = 10010101 
~a = 01101010
^ ( Disyunción exclusiva Lógica a nivel del bit )
La operación a ^ b devuelve un 1 en la posición donde a o b ( pero no ambos ) tienen un 1 y un 0 cuando ambas tienen 0 o ambas tienen 1. Ej:

a     = 10010101 
b     = 00110100
a ^ b = 10100001

Operaciones << y >>

Operación <<
Sea a una cadena de bits, n un entero, a << n, devuelve la misma cádena movida n bits a la izquierda ( perdiendose los primeros n bits de la cadena ) y reemplazando las n posiciones a la derecha por 0. Ej:

a      = 10010101 
a << 2 = 01010100
Operación >>
Sea a una cadena de bits, n un entero, a >> n, devuelve la misma cadena movida n bits a la derecha ( perdiéndose los últimos n bits de la cadena ) y reemplazando las n posiciones a la izquierda por 0. Ej:

a      = 10010101 
a >> 3 = 00010010

Algunos Usos y Ejemplos

Multiplicación por 2n

Por propiedades de los números binarios, se cumple que agregando n 0s a la derecha es equivalente a multiplicar este mismo número por 2n. Ej:


int function multiplicar_por_exponente_de_2( int a, int power ) {
	return a << power;
}

Nota: Hay que cuidar el overflow de los datos considerando el tamaño de ints en nuestro sistema. Pero este no es un post sobre implementaciones de C.

Cociente de dividir por 2n


int function cociente_de_exponente_de_2( int a, int power ) {
	return a >> power;
}

Nota: Desde ya que esta función devuelve el cociente pero no el resto.

Cociente y resto de dividir por 2n


unsigned int a, cociente, resto;
a = 757;
cociente = a >> 3;
resto =  (~((~0) << 3)) & a

Nota: ~0 devuelve un int del tamaño del sistema con todos sus bits pasados a 1, al moverlo 3 posiciones a la izquierda se tiene todos 1s salvo los 3 últimos bits, al negar esto se obtiene todos 0s salvo los 3 últimos bits que nos sirven para obtener los valores que son el resto de la división.

Representación en cadena binaria de un número


void show_as_binary( int a, char str[]) {
	int x, size;
	size = (int)sizeof(b) * 8;
	for(x=0; x<size; x++) {

		//This is necesary for a%2 might return -1 if a < 0. 
		str[x] = ‘0′ + ((a%2) < 0 ? 1: (a%2) );
		a = a >> 1;
	}
	reverse(str);
}

Representación en Octal de un número


void show_as_octal( int a, char str[]) {
	int x, size;
	size = (int)sizeof(b) * 8;
	for(x=0; x<size && a!=0; x++) {
		str[ x ] = ‘0′ + (a%8);
		a = a >> 3;
	}
	str[ x ] = ‘0′;
	reverse(str, (x+1));	

} 

Uso de un int para múltiples flags

Es una práctica común en Unix usar ints para pasar múltiples flags a funciones. Supongamos que necesito 4 flags distintos para una función o para lo que sea. Lo que puedo hacer es lo siguiente:


#define FLAG_A 1;
#define FLAG_B 2;
#define FLAG_C 4;
#define FLAG_D 8;

//Llamar a la función con FLAG_A y FLAG_D
call_function( FLAG_A | FLAG_D );

//Comparar si una variable tiene FLAG_B seteado
if( ( a & FLAG_B ) == FLAG_B ) { ...

//Comparar si una variable no tiene FLAG_C seteado
if( ( ( a | FLAG_C ) != a ) { ... 

Lo interesante de esto es que al abstraernos de los ints y pensarlos como cadenas de bits, se pueden hacer operaciones de algebra de boole, donde cada bit toma un valor verdadero o falso, que nos sirve para evaluar colecciones de flags como colecciones de proposiciones booleanas. Ahorrándonos así de pasar un valor como argumento a la función para cada una. Ej:


//Comparar contra FLAG_A, FLAG_B y no FLAG_D
if ( ( ( a & (FLAG_A | FLAG_B) ) == ( FLAG_A | FLAG_B ) ) && ( ( a | FLAG_D ) != a ) ) { ...

Un ejemplo concreto es el uso de los permisos de archivos en linux.

chmod toma un valor en octal que puede representarse así:



0012 = 18 = x

0102 = 28 = w

1002 = 48 = r

De donde surgen sus relativas combinaciones, por ej:



0112 = 38 = wx

1102 = 68 = rw

1112 = 78 = rwx

Bonús. Ejercicios 2.6, 2.7 y 2.8 de K & R

Ej 2.6

Escriba una función setbits( x,p,n,y ) que regresa x con los n bits que principian en la posición p iguales a los n bits más a la derecha de y, dejando los otros bits sin cambio.


//Ej 2.6
unsigned setbits(unsigned x, int p, int n, int y) {
	unsigned t, z;
	if(y < n) { 
		perror("Error");
	} else {
		//All 0s except for the space that starts at y and has size n
		t = ~(~0<<n)<<(y-n);
		//Get the value of x in this position
		t = x & t;
		//move the distance that p has from y
		t = t << (p-y);
		//Set that same space to 0s in x, starting on p and with size n
		z = ~(~0<<n)<<(p-n);		
		x = x & (~ z);
		//merge them
		t = x | t;
	}
	return t;
}

Ej 2.7

Escriba una función invert( x,p,n ) que regresa x con los n bits que principian en la posición p invertidos, dejando los otros sin cambio.


//Ej 2.7
unsigned invert(unsigned x, int p, int n) {
	unsigned t, z;
	//All 0s except for the space that starts at p and has size n
	t = ~(~0<<n)<<(p-n);
	//Get the inverted value of x in this position
	z = (~x & t);
	//merge them (first clean up x in the space that starts at p and has size n then merge with z)
	t = (~t & x) | z;
	return t;
}

Ej 2.8

Escriba una función rightrot( x,n ) que regresa x rotado a la derecha n posiciones de bits.


//Ej 2.8
unsigned rightrot( unsigned a, int n) {
	unsigned t, mascara, x;
	t = 0;
	mascara = 1;
	for(x=0; x<n; x++) {
		t = t << 1;
		t = t | (mascara & a);
		a = a >> 1;
	}
	return t;
}

Loops Elegantes

Icono de Post Septiembre 6 2008 :: c ::

Estoy en este momento estudiando The C Programming Language, más que nada como repaso de la sintaxis de C. Los ejercicios, aunque parecen básicos, son muy interesantes como problemas de programación.

En uno de los ejercicios se nos pide hacer una función reverse que tome una cadena y la devuelva invertida. Mi solución rapida fue:


void reverse(char line[], int n) {
	int temp, i, j, t ;
	if( (n/2) == ((n+1)/2) ) {
		t = -1 ;
	} else {
		t = 0 ;
	} 

	j = (n/2) ;
	for(i=0 ; i < j ; i++) {
		temp = line[i] ;
		line[i] = line[ (j*2)-i+t ] ;
		line[ (j*2)-i+t ] = temp ;
	}
	line[ n ] = ‘\0′ ;
}

Pero en el cápitulo 3 dan la forma de codificarlo de los autores:


void reverse( char s[] ) {
	int c, i, j;
	for( i = 0, j = strlen(s)-1 ; i < j ; i++, j– ) {
		c = s[i] ;
		s[i] = s[j] ;
		s[j] = c ;
	}
}

Me encanto como esta estructurado el loop, por el simple hecho de que no tengo la costumbre de hacer una doble inicialización y mucho menos un incremento y un decremento simultáneos en la tercera expresión del for. El código es super elegante y ahorra el problema y el exceso de código que tuve que poner en mi versión por si el tamaño de la cadena era impar o par.

Por que no es buena práctica usar breaks en un loop.

Además me di cuenta de lo siguiente, es una frase hecha que no es buena práctica usar breaks en un loop, el tema es que en realidad generalmente no es necesario ( salvo en casos especiales donde se necesita ejecutar algunas lineas de código antes de evaluar la condición del break ). Supongamos el siguiente codigo:


for( x=0 ; x < n ; x++) {
	......
	.....
	if( condicion_de_break ) break ;
}

Se puede resolver más elegantemente de la siguiente forma:


for( x=0 ; x < n && !condicion_de_break ; x++) {
	......
	.....
}