10 diciembre 2006

Solución al problema del 3er Parcial (A)

/*Crear un programa que contenga un menú el cual permita realizar diferentes operaciones. (Suma, resta, multiplicación y división)
a) El programa tendrá dos vectores inicializados de n elementos y un vector puntero de n elementos también.
b) Al presionar cualquiera de las opciones (operaciones) se guardará el resultado de la operación en el vector puntero.
c) También mostrará la dirección en memoria donde se almacenó el valor.
d) Para el caso de división no deberá permitir la división entre cero. Si se llega a dar el caso, deberá de estar validado para que no lo permita, el resultado que se guardará será un cero.
Ej. 4/0 = indefinido (deberá ser = 0 el resultado)*/

…. MENU……..
1.-… Suma
2.-…. Resta
3.- …. Multiplicación
4.- … División
5.- … Salir


using System;
using System.Collections.Generic;
using System.Text;

namespace III_Parcial_Examen1
{


class Program
{
unsafe static void Main(string[] args)
{
int[] vector1 = new int[] { 13, 22, 53, 4, 5, 46, 7, 8, 9, 150 }; //INICIALIZACION DE VECTORES
int[] vector2 = new int[] { 1, 58, 0, 47, 8, 6, 79, 78, 69, 10 };
int* pvector = stackalloc int[10]; //DECLARACION DEL VECTOR PUNTERO
int* pv, p1, p2;
int opc = 0;
int x = 0;
do
{
Console.WriteLine("...Menu...");
Console.WriteLine("1.- ....Suma");
Console.WriteLine("2.- ... Resta");
Console.WriteLine("3.- ... Multiplicacion");
Console.WriteLine("4.- ... Division");
Console.WriteLine("5.- ... Salir");
opc = int.Parse(Console.ReadLine());
switch (opc)
{
case 1:
Console.WriteLine("....Suma....");
for (x = 0; x < pv =" &pvector[x];" p1 =" pvector;" x =" 0;" pv =" &pvector[x];" x =" 0;" pv =" &pvector[x];" x =" 0;"> 0)
pvector[x] = vector1[x] / vector2[x];
else
pvector[x] = 0;
Console.WriteLine("El contenido del puntero {0}", pvector[x]);
pv = &pvector[x];
Console.WriteLine("La direccion del puntero {0}", (int)pv);
}
break;

case 5:
Console.WriteLine("....Salida....");
break;
default:
break;

}

} while (opc < 5);

}
}
}

Solución a los problemas del examen (B)

/*
Crear un programa que contenga un menú el cual permita realizar diferentes operaciones. (Suma, Resta)
a) El programa tendrá dos vectores inicializados de n elementos y un vector puntero de n elementos también.
b) Si presionan sumar el programa solo realizará la suma de números pares
c) Si se presiona Resta, el programa solo realizará la resta de números impares.
b) Al presionar cualquiera de las opciones (operaciones) se guardará el resultado de la operación en el vector puntero.
d) También mostrará la dirección en memoria donde se almacenó el valor.
e) La operación la realizará una función la cual recibirá dos valores del tipo puntero y ahí mismo se desplegará el resultado, como también mostrará la dirección de memoria que le fue asignada a la variable tipo puntero.

…. MENU……..
1.-… Suma
2.-…. Resta
3.- … Salir
*/

using System;
using System.Collections.Generic;
using System.Text;

namespace III_Parcial_Examen1
{


class Program
{

unsafe static void Operacion1(int* v1, int* v2)
{
int x, y = 0;
int* presultado = stackalloc int[10];
int* direccion;
direccion = presultado;
for (x = 0; x < 10; x++)
{
if (v1[x] % 2 == 0 && v2[x] % 2 == 0)//verificando que sea par
{
presultado[y] = v1[x] + v2[x];
Console.WriteLine("solo son valores pares {0} y {1}, el resultado {2}", v1[x], v2[x], presultado[y]);

Console.WriteLine("Direccion {0}", (int)direccion);
direccion++;
y++;
}

}
}
unsafe static void Operacion2(int* v1, int* v2)
{
int x, y = 0;
int* presultado = stackalloc int[10];
int* direccion;
direccion = presultado;
for (x = 0; x < 10; x++)
{
if (v1[x] % 2 != 0 && v2[x] % 2 != 0) //verificando que sea impar
{
presultado[y] = v1[x] - v2[x];
Console.WriteLine("solo son valores pares {0} y {1}, el resultado {2}", v1[x], v2[x], presultado[y]);
Console.WriteLine("Direccion {0}", (int)direccion);
direccion++;
y++;
}

}
}
unsafe static void Main(string[] args)
{
int[] vector1 = new int[] { 13, 22, 53, 4, 5, 46, 7, 8, 9, 150 }; //INICIALIZACION DE VECTORES
int[] vector2 = new int[] { 1, 58, 4, 47, 8, 6, 79, 78, 69, 10 };
int* pvector = stackalloc int[10]; //DECLARACION DEL VECTOR PUNTERO
int* pvector2 = stackalloc int[10];
int opc = 0;
int x = 0;
do
{
Console.WriteLine("...Menu...");
Console.WriteLine("1.- ....Suma");
Console.WriteLine("2.- ... Resta");
Console.WriteLine("3.- ... Salir");
opc = int.Parse(Console.ReadLine());
switch (opc)
{
case 1:
Console.WriteLine("....Suma....");
for (x = 0; x < 10; x++)
{
pvector[x] = vector1[x];
pvector2[x] = vector2[x];

}
Operacion1(pvector, pvector2);

break;

case 2:

Console.WriteLine("....Resta....");
for (x = 0; x < 10; x++)
{
pvector[x] = vector1[x];
pvector2[x] = vector2[x];

}
Operacion2(pvector, pvector2);

break;

case 3:
Console.WriteLine("....Salida....");
break;
default:
break;

}

} while (opc < 3);

}
}
}

04 diciembre 2006

Problemas

Utilizando punteros en todas las operaciones.

1.- Crear una funcion que reciba dos parametros del tipo punteros y que a traves de un menu nos permita ver el resultado de la suma, resta, multiplicacion y division de los dos numeros.
Validar que el divisor nunca sea menor a cero, si es asi, no debera realizar la operacion y debera indicarlo con un mensaje.
El menu puede ser algo como lo que se muestra a continuacion:
....(MENU DE OPERACIONES)......
1......... suma
2......... resta
3.......... multiplicacion
4.- ....... division

Comentarios: El parmetro que recibe la funcion podra ser del tipo que ustedes quieran (float, int, double, etc...)

2.- Generar dos vectores de dimensión 10 y construir un tercer vector del tipo puntero e
imprimirlo, cuyos componentes sean la suma de los dos anteriores.

Funciones con Punteros


/*

El Siguiente ejercicio lo que realiza es crear una funcion que reciba como parametro un puntero y lo despliega en pantalla.

* El procedimiento es el mismo del crear variables. Solo que en este caso el tipo de variable es uno del tipo puntero.

*/

using System;

using System.Collections.Generic;

using System.Text;

namespace punterosw_y_funciones

{

class Program

{

unsafe static void RecibePuntero(int* val)

{

Console.WriteLine("Este es el valor recibido: {0}",*val);

}

unsafe static void Main(string[] args)

{

int valor = 90;

int* pvalor = &valor;

Console.WriteLine("La Direccion donde se encuentra el valor: {0} y el valor de la direccion: {1}",(int)pvalor, *pvalor);

RecibePuntero(pvalor);

Console.ReadLine();

}

}

}

Utilización de Stackalloc

Este ejercicio pasenlo a la computadora. Recuerden modificar el proyecto para que les permita utilizar código inseguro.

El programa si funciona!

Que hace?: Declaramos un vector del tipo int, y otro vector del tipo puntero. Guardamos el contenido del vector original en otro vector del tipo puntero. Declaramos otro puntero que apunta a la direccion fisica de memoria donde se ecuentra el vector original.


Guradamos en el vector puntero los datos del vector original y lo incrementamos para que pueda seguir guradando datos en la siguiente direccion del tipo int.

Al finalzar de llenarlo, recorremos otro puntero que contiene la direccion original del vector y lo recorremos con otro ciclo for al cual le hacemos un incremento.

Este ejemplo esta basado en el tema de Punteros parte 2 Operadr Stackalloc creacion de pilas


using System;

using System.Collections.Generic;

using System.Text;

namespace Punteros_ejemplos

{

class Program

{

unsafe static void Main(string[] args) // Aqui estoy poniendo el bloque inseguro.

{

int[] arreglo = new int[] { 1,3,4,5,6,7,6,4,3,2};// Un arreglo

int* pt = stackalloc int[10]; //Un puntero del tipo arreglo

int* ppt;// Un puntero sencillo

*pt = arreglo[0];// Aqui apuntamos a la direccion del primer elemento del arreglo

ppt = pt;// Igualamos las direcciones donde se encuentra el inicio del vector

for (int i = 0; i <>

{

*pt = arreglo[i];// Grabamos en esa direccion de memoria el contenido del primer elemento del vector

pt++;// Nos movemos en memoria la distancia del tipo int para poder grabar en el siguiente espacio

}

// Como ya movimos el puntero pt de su lugar de origen, vamos a utilizar el puntero ppt que se encuentra en el lugar

//de inicio del vector orignal, de esa forma lo podemos recorrer.

for (int i = 0; i <>

{

Console.WriteLine("el arreglo: {0}", *ppt);

ppt++;//En este lugar estamos recorriendo nuevamente directametne la memoria. Recordemos que el puntero ppt apuntaba a la direccion de un puntero tipo vector, el cual a su ves apuntaba directamente a un vector del tipo int. No olvidemos que cuando utilizamos el incremento ++, lo que estamos haciendo es incrementando una posicion en memoria del tamaño del tipo del puntero. En este caso es del tipo INT (entero).

}

Console.ReadLine();

}

}

}

Punteros parte #2

Operador stackalloc. Creación de tablas en pila.

Cuando se trabaja con punteros puede resultar interesante reservar una zona de memoria en la pila donde posteriormente se puedan ir
almacenando objetos. Precisamente para eso está el operador stackalloc, que se usa siguiéndose la siguiente sintaxis:

stackalloc []

stackalloc reserva en pila el espacio necesario para almacenar contiguamente el número de objetos de tipo indicado en (reserva sizeof()* bytes) y devuelve un puntero a la dirección de inicio de ese espacio. Si no quedase memoria libre suficiente para reservarlo se produciría una excepción System.StackOverflowException.

stackalloc sólo puede usarse para inicializar punteros declarados como variables locales y sólo en el momento de su declaración.. Por ejemplo, un puntero pt que apuntase al principio de una región con capacidad para 100 objetos de tipo int se declararía con:

int * pt = stackalloc int[100];

Sin embargo, no sería válido hacer:

int * pt;
pt = stackalloc int[100]; // ERROR: Sólo puede usarse stackalloc en declaraciones

Aunque pueda parecer que stackalloc se usa como sustituto de new para crear tablas en pila en lugar de en memoria dinámica, no hay que confundirse: stackalloc sólo reserva un espacio contiguo en pila para objetos de un cierto tipo, pero ello no significa que se cree una tabla en pila. Las tablas son objetos que heredan de System.Array y cuentan con los miembros heredados de esta clase y de object, pero regiones de memoria en pila reservadas por stackalloc no. Por ejemplo, el siguiente código es inválido.

int[] tabla; int * pt = stackalloc int[100];
tabla = *pt; // ERROR: El contenido de pt es un int, no una tabla (int[])
Console.WriteLine(pt->Length); // ERROR: pt no apunta a una tabla
Sin embargo, gracias a que como ya se ha comentado en este tema el operador [] está redefinido para trabajar con punteros, podemos usarlo para acceder a los diferentes objetos almacenados en las regiones reservadas con stackalloc como si fuesen tablas. Por ejemplo, este código guarda en pila los 100 primeros enteros y luego los imprime:

class Stackalloc
{
public unsafe static void Main()
{
int * pt = stackalloc int[100];
for (int i=0; i<100; i++)
pt[i] = i;
for(int i=0; i<100; i++)
System.Console.WriteLine(pt[i]);
}
}


Fijación de variables apuntadas

Aunque un puntero sólo puede apuntar a datos de tipos que puedan almacenarse completamente en pila (o sea, que no sean ni objetos de tipos referencia ni estructuras con miembros de tipos referencia), nada garantiza que los objetos apuntado en cada momento estén almacenados en pila. Por ejemplo, las variables estáticas de tipo int o los elementos de una tabla de tipo int se almacenan en memoria dinámica aún cuando son objetos a los que se les puede apuntar con punteros.

Un uso frecuente de fixed consiste en apuntar a objetos de tipos para los que se puedan declarar punteros pero que estén almacenados en tablas, ya que ello no se puede hacer directamente debido a que las tablas se almacenan en memoria dinámica. Por ejemplo, copiar usando punteros una tabla de 100 elementos de tipo int en otra se haría así:

class CadenaInsegura {
public unsafe static void Main()
{
string s=”Hola”;
Console.WriteLine(“Cadena inicial: {0}”, s);
fixed (char * ps=s)
{
for (int i=0;i }
Console.WriteLine(“Cadena final: {0}”, s);
}
}
La salida por pantalla de este último programa es:
Hola
AAAA

Cuando se modifiquen cadenas mediante punteros hay que tener en cuenta que, aunque para facilitar la comunicación con código no gestionado escrito en C o C++ las cadenas en C# también acaban en el carácter ‘\0’, no se recomienda confiar en ello al recorrerlas con punteros porque ‘\0’ también puede usarse como carácter de la cadena. Por ello, es mejor hacer como en el ejemplo y detectar su final a través de su propiedad Length.

28 noviembre 2006

Punteros

Concepto de código inseguro
Código inseguro es todo aquél fragmento de código en C# dentro del cual es posible hacer uso de punteros.
Un puntero en C# es una variable que es capaz de almacenar direcciones de memoria. Generalmente suele usarse para almacenar direcciones que almacenen objetos, por lo en esos casos su significado es similar al de variables normales de tipos referencia. Sin embargo, los punteros no cuentan con muchas de las restricciones de éstas a la hora de acceder al objeto. Por ejemplo, al accederse a los elementos de una tabla mediante un puntero no se pierde tiempo en comprobar que el índice especificado se encuentre dentro de los límites de la tabla, lo que permite que el acceso se haga más rápidamente.
Aparte de su mayor eficiencia, también hay ciertos casos en que es necesario disponer del código inseguro, como cuando se desea hacer llamadas a funciones escritas en lenguajes no gestionados cuyos parámetros tengan que ser punteros.
Es importante señalar que los punteros son una excepción en el sistema de tipos de .NET, ya que no derivan de la clase primigenia System. Object, por lo que no dispondrán de los métodos comunes a todos los objetos y una variable object no podrá almacenarlos (tampoco existen procesos similares al boxing y unboxing que permitan simularlo)

Compilación de códigos inseguros
El uso de punteros hace el código más proclive a fallos en tanto que se salta muchas de las medidas incluidas en el acceso normal a objetos, por lo que es necesario incluir ciertas medidas de seguridad que eviente la instroducción accidental de esta inseguridad
La primera medida tomada consiste en que explícitamente hay que indicar al compilador que deseamos compilar código inseguro. Para ello, al compilador de línea de comandos hemos de pasarle la opción /unsafe, como se muestra el ejemplo:
csc códigoInseguro.cs /unsafe
Si no se indica la opción unsafe, cuando el compilador detecte algún fuente con código inseguro producirá un mensaje de error como el siguiente:

códigoInseguro(5,23): error CS0277: unsafe code may only appear if compiling with /unsafe

En caso de que la compilación se vaya a realizar a través de Visual Studio.NET, la forma de indicar que se desea compilar código inseguro es activando la casilla
View ->Property Pages ->Configuration Properties->Build ->Allow unsafe code blocks

Marcación de códigos inseguros
Aparte de forzarse a indicar explícitamente que se desea compilar código inseguro, C# también obliga a que todo uso de código inseguro que se haga en un fichero fuente tenga que ser explícitamente indicado como tal. A las zonas de código donde se usa código inseguro se les denomina contextos inseguros, y C# ofrece varios mecanismos para marcar este tipo de contextos.

Una primera posibilidad consiste en preceder un bloque de instrucciones de la palabra reservada unsafe siguiendo la siguiente sintaxis:
unsafe
En el código incluido en podrá definirse variables de tipos puntero y podrá hacerse uso de las mismas. Por ejemplo:

public void f()
{ unsafe {
int *x; } }

Otra forma de definir contextos inseguros consiste en añadir el modificador unsafe a la definición de un miembro, caso en que dentro de su definición se podrá hacer uso de punteros. Así es posible definir campos de tipo puntero, métodos con parámetros de tipos puntero, etc. El siguiente ejemplo muestra cómo definir dos campos de tipo puntero. Nótese sin embargo que no es posible definir los dos en una misma línea:

struct PuntoInseguro
{ public unsafe int *X; // No es válido hacer
public unsafe int *X, Y;
public unsafe int *Y; // Tampoco lo es hacer public unsafe int *X, *Y;
}

Obviamente, en un método que incluya el modificador unsafe no es necesario preceder con dicha palabra sus bloques de instrucciones inseguros.
Hay que tener en cuenta que el añadido de modificadores unsafe es completamente inocuo. Es decir, no influye para nada en cómo se haya de redefinir y si un método Main() lo tiene sigue siendo un punto de entrada válido.
Una tercera forma consiste en añadir el modificador unsafe en el definición de un tipo, caso en que todas las definiciones de miembros del mismo podrán incluir código inseguro sin necesidad de añadir a cada una el modificador unsafe o preceder sus bloques de instrucciones inseguras de la palabra reservada unsafe. Por ejemplo:
unsafe struct PuntoInseguro {
public int * X, *Y; }

Definición de punteros
Para definir una variable puntero de un determinado tipo se sigue una sintaxis parecida a la usada para definir variables normales sólo que al nombre del tipo se le postpone un símbolo de asterisco (*) O sea, un puntero se define así:

* ;

Por ejemplo, una variable puntero llamada a que pueda almacenar referencias a posiciones de memoria donde se almacenen objetos de tipo int se declara así:
int * a;
En caso de quererse declarar una tabla de punteros, entonces el asterisco hay que incluirlo tras el nombre del tipo pero antes de los corchetes. Por ejemplo, una tabla de nombre t que pueda almacenar punteros a objetos de tipo int se declara así:
int*[] t;
Hay un tipo especial de puntero que es capaz de almacenar referencias a objetos de cualquier tipo. Éstos punteros de declara indicando void como . Por ejemplo:

void * punteroACualquierCosa;

Hay que tener en cuenta que en realidad lo que indica el tipo que se dé a un puntero es cuál es el tipo de objetos que se ha de considerar que se almacenan en la dirección de memoria almacenada por el puntero. Si se le da el valor void lo que se está diciendo es que no se desea que se considere que el puntero apunta a ningún tipo específico de objeto. Es decir, no se está dando información sobre el tipo apuntado.
Se pueden declarar múltiples variables locales de tipo puntero en una misma línea. En ese caso el asterisco sólo hay que incluirlo antes del nombre de la primera. Por ejemplo:

int * a, b; // a y b son de tipo int * No sería válido haberlas definido como int *a, *b;

Hay que tener en cuenta que esta sintaxis especial para definir en una misma definición varios punteros de un mismo tipo sólo es válida en definiciones de variables locales. Al definir campos no sirve y hay que dar para cada campo una definición independiente.
El recolector de basura no tiene en cuenta los datos a los que se referencie con punteros, pues ha de conocer cuál es el objeto al referenciado por cada variable y un puntero en realidad no tiene porqué almacenar referencias a objetos de ningún tipo en concreto. Por ejemplo, pueden tenerse punteros int * que en realidad apunten a objeto char, o punteros void * que no almacenen información sobre el tipo de objeto al que debería considerarse que apuntan, o punteros que apunte a direcciones donde no hayan objetos, etc.
Como el recolector de basura no trabaja con punteros, no es posible definir punteros de tipos que se almacenen en memoria dinámica o contengan miembros que se almacenen en memoria dinámica, ya que entonces podría ocurrir que un objeto sólo referenciado a través de punteros sea destruido por considear el recolector que nadie le referenciaba. Por ello, sólo es válido definir punteros de tipos cuyos objetos se puedan almacenar completamente en pila, pues la vida de estos objetos no está controlada por el recolector de basura sino que se destruyen cuando se abandona el ámbito donde fueron definidos.
En concreto, los únicos punteros válidos son aquellos cuyo tipos sean tipos valor básicos, enumeraciones o estructuras que no contengan campos de tipos referencias. También pueden definirse punteros de tipos puntero, como muestra este ejemplo de declaración de un puntero a puntero de tipo int llamando punteroApuntero:

int ** punteroApuntero;

Obviamente la anidación puede hacerse a cualquier nivel de profundidad, pudiéndose definir punteros a punteros a punteros, o punteros a punteros a punteros a punteros, etc.

Manipulación de punteros
Obtención de dirección de memoria. Operador &
Para almacenar una referencia a un objeto en un puntero se puede aplicar al objeto el operador prefijo &, que lo que hace es devolver la dirección que en memoria ocupa el objeto sobre el que se aplica. Un ejemplo de su uso para inicializar un puntero es:
int x =10; int * px = &x;

Este operador no es aplicable a expresiones constantes, pues éstas no se almacenan en ninguna dirección de memoria específica sino que se incrustan en las instrucciones. Por ello, no es válido hacer directamente:

int px = &10; // Error 10 no es una variable con dirección propia

Tampoco es válido aplicar & a campos readonly, pues si estos pudiesen ser apuntados por punteros se correría el riesgo de poderlos modificar ya que a través de un puntero se accede a memoria directamente, sin tenerse en cuenta si en la posición accedida hay algún objeto, por lo que mucho menos se considerará si éste es de sólo lectura.

Lo que es sí válido almacenar en un puntero es la dirección de memoria apuntada por otro puntero. En ese caso ambos punteros apuntarían al mismo objeto y las modificaciones a éste realizadas a través de un puntero también afectarían al objeto visto por el otro, de forma similar a como ocurre con las variables normales de tipos referencia. Es más, los operadores relacionales típicos (==, !=, <, >, <= y >=) se han redefinido para que cuando se apliquen entre dos punteros de cualesquiera dos tipos lo que se compare sean las direcciones de memoria que estos almacenan. Por ejemplo:

int x = 10; int px = &x; int px2 = px; // px y px2 apuntan al objeto almacenado en x Console.WriteLine( px == px2); // Imprime por pantalla True

En realidad las variables sobre las que se aplique & no tienen porqué estar inicializadas. Por ejemplo, es válido hacer:

private void f()
{
int x;
unsafe
{ int px = &x;}
}

Esto se debe a que uno de los principales usos de los punteros en C# es poderlos pasar como parámetros de funciones no gestionadas que esperen recibir punteros. Como muchas de esas funciones han sido programadas para inicializar los contenidos de los punteros que se les pasan, pasarles punteros inicializados implicaría perder tiempo innecesariamente en inicializarlos.

Acceso a contenido de puntero. Operador *
Un puntero no almacena directamente un objeto sino que suele almacenar la dirección de memoria de un objeto (o sea, apunta a un objeto) Para obtener a partir de un puntero el objeto al que apunta hay que aplicarle al mismo el operador prefijo *, que devuelve el objeto apuntado. Por ejemplo, el siguiente código imprime en pantalla un 10:
int x = 10; int * px= &x; Console.WriteLine(*px);
Es posible en un puntero almacenar null para indicar que no apunta a ninguna dirección válida. Sin embargo, si luego se intenta acceder al contenido del mismo a través del operador * se producirá generalmente una excepción de tipo NullReferenceException (aunque realmente esto depende de la implementación del lenguaje)

Por ejemplo:

int * px = null; Console.WriteLine(*px); // Produce una NullReferenceException

No tiene sentido aplicar * a un puntero de tipo void * ya que estos punteros no almacenan información sobre el tipo de objetos a los que apuntan y por tanto no es posible recurperarlos a través de los mismos ya que no se sabe cuanto espacio en memoria a partir de la dirección almacenada en el puntero ocupa el objeto apuntado y, por tanto, no se sabe cuanta memoria hay que leer para obtenerlo.
Acceso a miembro de contenido de puntero. Operador ->
Si un puntero apunta a un objeto estructura que tiene un método F() sería posible llamarlo a través del puntero con:
(*objeto).F();
Sin embargo, como llamar a objetos apuntados por punteros es algo bastante habitual, para facilitar la sintaxis con la que hacer esto se ha incluido en C# el operador ->, con el que la instrucción anterior se escribiría así:
objeto->f();
Es decir, del mismo modo que el operador . permite acceder a los miembros de un objeto referenciado por una variable normal, -> permite acceder a los miembros de un objeto referenciado por un puntero. En general, un acceso de la forma O -> M es equivalente a hacer (*O).M. Por tanto, al igual que es incorrecto aplicar * sobre punteros de tipo void *, también lo es aplicar ->

Conversiones de punteros
De todo lo visto hasta ahora parece que no tiene mucho sentido el uso de punteros de tipo void * Pues bien, una utilidad de este tipo de punteros es que pueden usarse como almacén de punteros de cualquier otro tipo que luego podrán ser recuperados a su tipo original usando el operador de conversión explícita. Es decir, igual que los objetos de tipo object pueden almacenar implícitamente objetos de cualquier tipo, los punteros void * pueden almacenar punteros de cualquier tipo y son útiles para la escritura de métodos que puedan aceptar parámetros de cualquier tipo de puntero.

A diferencia de lo que ocurre entre variables normales, las conversiones entre punteros siempre se permiten, al realizarlas nunca no se comprueba si son válidas. Por ejemplo, el siguiente código es válido:

char c = 'A';
char* pc = &c;
void* pv = pc;
int* pi = (int*)pv;
int i = *pi; // Almacena en 16 bits del char de pv + otros 16 indeterminados Console.WriteLine(i);
*pi = 123456; // Machaca los 32 bits apuntados por pi

En este código pi es un puntero a un objeto de tipo int (32 bits), pero en realidad el objeto al que apunta es de tipo char (16 bits), que es más pequeño. El valor que se almacene en i es en principio indefinido, pues depende de lo que hubiese en los 16 bits extras resultantes de tratar pv como puntero a int cuando en realidad apuntaba a un char.

Del mismo modo, conversiones entre punteros pueden terminar produciendo que un puntero apunte a un objeto de mayor tamaño que los objetos del tipo del puntero. En estos casos, el puntero apuntaría a los bits menos significativos del objeto apuntado.

También es posible realizar conversiones entre punteros y tipos básicos enteros. La conversión de un puntero en un tipo entero devuelve la dirección de memoria apuntada por el mismo. Por ejemplo, el siguiente código muestra por pantalla la dirección de memoria apuntada por px:

int x = 10; int *px = &10;
Console.WriteLine((int) px);
Por su parte, convertir cualquier valor entero en un puntero tiene el efecto de devolver un puntero que apunte a la dirección de memoria indicada por ese número. Por ejemplo, el siguiente código hace que px apunte a la dirección 1029 y luego imprime por pantalla la dirección de memoria apuntada por px (que será 1029):

int *px = (int *) 10;
Console.WriteLine((int) px);
Nótese que aunque en un principio es posible hacer que un puntero almacene cualquier dirección de memoria, si dicha dirección no pertenece al mismo proceso que el código en que se use el puntero se producirá un error al leer el contenido de dicha dirección. El tipo de error ha producir no se indica en principio en la especifricación del lenguaje, pero la implementación de Microsoft lanza una referencia NullReferenceException. Por ejemplo, el siguiente código produce una excepción de dicho tipo al ejecturase:

using System;
class AccesoInválido
{ public unsafe static void Main()
{
int * px = (int *) 100;
Console.Write(*px);
// Se lanza NullReferenceException }
}

Aritmética de punteros
Los punteros se suelen usar para recorrer tablas de elementos sin necesidad de tener que comprobarse que el índice al que se accede en cada momento se encuentra dentro de los límites de la tabla. Por ello, los operadores aritméticos definidos para los punteros están orientados a facilitar este tipo de recorridos.
Hay que tener en cuenta que todos los operadores aritméticos aplicables a punteros dependen del tamaño del tipo de dato apuntado, por lo que no son aplicables a punteros void * ya que estos no almacenan información sobre dicho tipo. Esos operadores son:

++ y --: El operador ++ no suma uno a la dirección almacenada en un puntero, sino que le suma el tamaño del tipo de dato al que apunta. Así, si el puntero apuntaba a un elemento de una tabla pasará a apuntar al siguiente (los elementos de las tablas se almacenan en memoria consecutivamente) Del mismo modo, --resta a la dirección almacenada en el puntero el tamaño de su tipo de dato. Por ejemplo, una tabla de 100 elementos a cuyo primer elemento inicialmente apuntase pt podría recorrerse así:

for (int i=0; i<100; i++)
Console.WriteLine(“Elemento{0}={1}”, i, (*p)++);

El problema que puede plantear en ciertos casos el uso de ++ y --es que hacen que al final del recorrido el puntero deje de apuntar al primer elemento de la tabla. Ello podría solucionarse almacenando su dirección en otro puntero antes de iniciar el recorrido y restaurándola a partir de él tras finalizarlo.

+ y -: Permiten solucionar el problema de ++ y --antes comentado de una forma más cómoda basada en sumar o restar un cierto entero a los punteros. + devuelve la dirección resultante de sumar a la dirección almacenada en el puntero sobre el que se aplica el tamaño del tipo de dicho puntero tantas veces como indique el entero sumado. -tiene el mismo significado pero restando dicha cantidad en vez de sumarla.

Por ejemplo, usando + el bucle anterior podría reescribrise así:

for (int i=0; i<100; i++)
Console.WriteLine(“Elemento{0}={1}”, i, *(p+i));

El operador -también puede aplicarse entre dos punteros de un mismo tipo, caso en que devuelve un long que indica cuántos elementos del tipo del puntero pueden almacenarse entre las direcciones de los punteros indicados. []: Dado que es frecuente usar + para acceder a elementos de tablas, también se ha redefinido el operador [] para que cuando se aplique a una tabla haga lo mismo y devuelva el objeto contenido en la dirección resultante.

O sea *(p+i) es equivalente a p[i], con lo que el código anterior equivale a:

for (int i=0; i<100; i++)
Console.WriteLine(“Elemento{0}={1}”, i, p[i]);

No hay que confundir el acceso a los elementos de una tabla aplicando [] sobre una variable de tipo tabla normal con el acceso a través de un puntero que apunte a su primer elemento. En el segundo caso no se comprueba si el índice indicado se encuentra dentro del rango de la tabla, con lo que el acceso es más rápido pero también más proclive a errores difíciles de detectar. Finalmente, respecto a la aritmética de punteros, hay que tener en cuenta que por eficiencia, en las operaciones con punteros nunca se comprueba si se producen desbordamientos, y en caso de producirse se truncan los resultados sin avisarse de ello mediante excepciones. Por eso hay que tener especial cuidado al operar con punteros no sea que un desbordamiento no detectado cause errores de causas difíciles de encontrar. Operadores relacionados con código inseguro Operador sizeof. Obtención de tamaño de tipo El operador unario y prefijo sizeof devuelve un objeto int con el tamaño en bytes del tipo de dato sobre el que se aplica. Sólo puede aplicarse en contextos inseguros y sólo a tipos de datos para los que sea posible definir punteros, siendo su sintaxis de uso: sizeof()
Cuando se aplica a tipos de datos básicos su resultado es siempre constante. Por ello, el compilador optimiza dichos usos de sizeof sustituyéndolos internamente por su valor (inlining) y considerando que el uso del operador es una expresión constante. Estas constantes correspondientes a los tipos básicos son las indicadas en la Tabla 10: Tipos Resultado sbyte, byte, bool 1 short, ushort, char 2 int, uint, float 4 long, ulong, double 8 Tabla 10:

Resultados de sizeof para tipos básicos
Para el resto de tipos a los que se les puede aplicar, sizeof no tiene porqué devuelver un resultado constante sino que los compiladores pueden alinear en memoria las estructuras incluyendo bits de relleno cuyo número y valores sean en principio indeterminado. Sin embargo, el valor devuelto por sizeof siempre devolverá el tamaño en memoria exacto del tipo de dato sobre el que se aplique, incluyendo bits de relleno si los tuviese.

Nótese que es fácil implementar los operadores de aritmética de punteros usando sizeof. Para ello, ++ se definiría como añadir a la dirección almacenada en el puntero el resultado de aplicar sizeof a su tipo de dato, y --consistiría en restarle dicho valor. Por su parte, el operador + usado de la forma P + N (P es un puntero de tipo T y N un entero) lo que devuelve es el resultado de añadir al puntero sizeof(T)*N, y P – N devuelve el resultado de restarle sizeof(T)*N. Por último, si se usa -para restar dos punteros P1 y P2 de tipo T, ello es equivalente a calcular (((long)P1) - ((long)P2)))/sizeof(T)

Problemas resueltos

1.- Realice el siguiente programa:

En una escuela se requiere tener control de todos sus alumnos (10) . Para ello se necesita realizar un programa que guarde el nombre del alumno y las calificaciones de 3 materias


El programa tendrá el siguiente menú:


1.- Alta (Dar de alta un alumno y las calificaciones de las tres materias)

2.- Mostrar datos (Mostrar a todos los alumnos con sus 3 calificaciones y el promedio por alumno)

3.- Búsqueda (Buscar por nombre de alumno y modificar las tres calificaciones)

4.- Salir

Ejemplos:

En la captura:

Nombre del alumno: Martha Sánchez

Matemáticas: 10

Física: 10

Algebra: 10

Nombre del alumno: Roberto Carlos

Matemáticas: 8

Física: 9

Algebra: 10

Al mostrar la información

Nombre del alumno: Martha Sánchez

Matemáticas: 10

Física: 10

Algebra: 10

Promedio: 10

Nombre del alumno: Roberto Carlos

Matemáticas: 8

Física: 9

Algebra: 10

Promedio: 9


static void main () {

string[] nombre = new string [10];
string[] materias = new string [] {"matematicas","algebra","programacion"}
string busqueda;
int [,] calif = new int [10,3];
int opc, x=0,suma=0;

do {

Console.WriteLine ("1....... Altas");
Console.WriteLine ("2........Mostrar");
Console.WriteLine ("3........Modificar");
Console.WriteLine ("4........Salir");
opc = int.Parse(Console.ReadLine());
switch (opc)
{
case 1:
Console.WriteLine ("Nombre: ");
nombre[x] = Console.ReadLine();
for (int y=0;y<3;y++) k="0;k<3;y++) suma="0;" busqueda =" Console.ReadLine" k="0;k<4);>

2.- Realice el siguiente programa:

En una bodega se requiere tener control de todas las piezas en ella contenida (10). Para ello se necesita realizar un programa que guarde el nombre del artículo, cantidad de piezas, precio unitario y el dueño de los artículos.

El programa tendrá el siguiente menú:

1.- Alta (Dar de alta un artículo cantidad de piezas y el dueño)

2.- Mostrar datos (Mostrar todos los artículos y sus datos, como también el valor total de las piezas almacenadas. Valor total = cantidad * Precio unitario.)

3.- Búsqueda (Buscar por nombre de articulo y modificar cantidad de piezas)

4.- Salir

Ejemplos:

En la captura:

Nombre del artículo: Estufa

Cantidad: 10

Precio unitario: 4500.75

Dueño: Mueblería Alexia

Nombre del artículo: Bocinas Bose

Cantidad: 200

Precio unitario: 3000.00

Dueño: Electra

Al mostrar la información

Nombre del artículo: Estufa

Cantidad: 10

Precio unitario: 4500.75

Dueño: Mueblería Alexia

Valor Total: $ 45007.50

Nombre del artículo: Bocinas Bose

Cantidad: 200

Precio unitario: 3000.00

Dueño: Electra

Valor Total: $ 600000.00



//**************************** Programa 2
static void main ()
{
string[] nombre = new string[10];
string busqueda;
string[] owner = new string[10];
int[] cantidad = new int [10];
float[] Punit = new float [10];
double Ptot;
int opc=0,x=0, y=0,k=0;

do {
Console.WriteLine ("1....... Altas");
Console.WriteLine ("2........Mostrar");
Console.WriteLine ("3........Modificar");
Console.WriteLine ("4........Salir");
opc = int.Parse(Console.ReadLine());
switch (opc){

case 1:
Console.WriteLine ("Nombre: ");
nombre[x] = Console.ReadLine();
Console.WriteLine ("Cantidad: ");
cantidad[x]=int.Parse (Console.ReadLine());
Console.WriteLine ("Precio Unitario: ");
Punit[x] = float.Parse (Console.ReadLine());
Console.WriteLine ("Dueño: ");
owner = Console.ReadLine();
x++;
break;

case 2:
for (y=0;y
{
Console.WriteLine ("Nombre: {0}",nombre[y]);
Console.WriteLine ("Cantidad: {0}",cantidad[y]);
Console.WriteLine ("Dueño: {0}",owner[y]);
Console.WriteLine ("Precio Unitario: {0}",Punit[y]);
Ptot=Punit[y]*cantidad[y])
Console.WriteLine ("Valor Total: {0}",Ptot);
}
break;

case 3:
Console.WriteLine ("Nombre del articulo para modificar la cantidad: ");
busqueda= Console.ReadLine();
for (y=0;y
{
if (busqueda == nombre[y])
{
Console.WriteLine ("Nueva Cantidad:")
cantidad[y] = int.Parse(Console.ReadLine());
}
}

break;

case 4:
break;

default:
break;
}

}while (opc<4);




}

10 noviembre 2006

Problemas con Arreglos

Práctica con arreglos y vectores:

1.- Generar dos vectores de dimensión 10 y construir un tercer vector e
imprimirlo, cuyos componentes sean la suma de los dos anteriores.

2.- Se leen dos vectores de dimensión 5 y hay que calcular componente a
componente, si tienen datos iguales y escribir cuantos datos iguales tienen.

3.- Generar una matriz de 10x10 y hallar e imprimir la suma de cada una de
sus filas y de cada una de sus columnas.

4.- Generar una matriz de 5x5 (numérica entera) e imprimir el valor más
grande y el más pequeño y los lugares donde se encuentran.


Problemas para entregar:
1.- Una empresa tiene 10 almacenes y hay que crear un algoritmo que lea
las ventas mensuales de cada almacén, calcule la media (promedio) de ventas y un listado de
aquellos almacenes que superen la media.


2.- Crear una agenda electrónica.
El Programa contendrá: Nombre, Direccion, teléfonos ( casa, cell y oficina) y e-mail.
Tendrá el siguiente menú:

1.- Alta
2.- Mostrar todo
3.- Busqueda
4.- Modificaciones


Explicación:
En la sección de Alta Capturara todos elementos. En caso de no tener alguno de esos teléfonos escribir un cero.

La sección de mostrar todo, mostrará toda la información capturada.

La búsqueda la realizará por nombre y mostrará toda la información que corresponda al nombre encontrado.


En la sección de modificaciones, esta la realizará a través de la búsqueda de nombre.
Si encuentra el nombre aparecerá un submenú el cual te permita decidir el campo que quieres modificar.
ej.
1.- Nombre
2.- Dirección
3.- Telefonos
4.- e-mail
5.- salir

La información puede ser la siguiente:
Nombre: "Juan Camaney Ronaldo"
Dirección: "Av. Don quijote #232 Col. Insurgentes"
Telefonos: "casa: 6865-43-45-34 cel: 6861-45-45-45 oficina: "
email: "jcr@hotmail.com"

08 noviembre 2006

Arreglo bidimencionales (Matrices)


Para tablas de dos dimensiones, o también llamadas matrices, se utilizan dos
subíndices para referenciar cada elemento, conjuntamente con el nombre de la tabla.


int[,] matriz = new int[2,5];
matriz [0,0] = 10;
Console.WriteLine ("{0}",matriz[0,0]);




Podemos imaginarnos los arreglos bidimencionales como un arreglo matricial. Donde para acceder a una celda es necesario hacerlo a traves de coordenadas [X,Y].

De esta forma tendremos un programa que maneja un arreglo de una sola dimencion y otro de dos dimenciones. Donde en el vector se almacena el nombre y en la matrz podemos guardar 6 calificaciones.




int [,] califi = new int [6,6];
string[] nombre = new string [6];
int x=0, y=0, opc;


do {
Console.WriteLine ("1.- Entrada de datos");
Console.WriteLine ("2.- Consulta");
Console.WriteLine ("3.- Busqueda");
Console.WriteLine ("4.- Salida");
opc = int.Parse (Console.ReadLine ());
switch (opc)
{
case 1:
Console.WriteLine ("Nombre del alumno {0}"x);
nombre[x] = Console.ReadLine ();

Console.WriteLine ("Dar entrada a 6 calificaciones");
for (y=0;y<6;y++) { Console.WriteLine ("Calificacion {0}",y); calif[x,y] = int.Parse(Console.ReadLine ()); } x++; break case 2: break case 3: break case 4: break default : break } } while (opc< 3);

03 noviembre 2006

Ejemplo de Vectores

using System;

namespace AltasyConsultas
{

class Class1
{

[STAThread]
static void Main(string[] args)
{
int [] mat1=new int [10],mat2=new int [10],mat3=new int [10],mat4=new int [10];
string []nombre = new string [10];
int x = 0;
int opc;
do
{
Console.WriteLine("1-alta");
Console.WriteLine("2-consulta");
Console.WriteLine("3-salir");
opc=int.Parse(Console.ReadLine());
switch (opc)
{
case 1:
Console.WriteLine("nombre");
nombre [x]=Console.ReadLine();
Console.WriteLine("materia1");
mat1[x]=int.Parse(Console.ReadLine());
Console.WriteLine("materia2");
mat2[x]=int.Parse(Console.ReadLine());
Console.WriteLine("materia3");
mat3[x]=int.Parse(Console.ReadLine());
Console.WriteLine("materia4");
mat4[x]=int.Parse(Console.ReadLine());
x++;
break;
case 2:
for(int y=0;y
{
Console.WriteLine("nombre{0}",nombre [y]);
Console.WriteLine("calificacion materias {0},{1},{2},{3}",mat1[y],mat2[y],mat3[y],mat4[y]);
}
break;
case 3:
break;
default:
Console.WriteLine("esa opcion no exixte");
break;
}
}while (opc<3);>

02 noviembre 2006

Arreglos-Vectores

Vectores (arrays, tablas, matrices..).
Es una colección finita y homogénea de elementos del mismo tipo, en la que todos sus componentes pueden seleccionarse de forma directa y aleatoria, siendo igualmente accesibles, designandose el tipo de un elemento, y un índice para la selección del componente.

Una declaración de array podría ser:
int[] myIntArray;myIntArray = new int[5];

El tipo de datos de los índices debe ser un tipo de datos tal que permita las operaciones obtener_siguiente y obtener_anterior con precisión.

El tipo de datos de los componentes puede ser cualquier tipo básico, un puntero, una estructura struct, una unión u otra tabla.

nombre_array[índice];

Donde índice es una expresión del mismo tipo de datos que el definido para los índices en la declaración del array, y su valor debe estar comprendido entre límite_inferior y límite_superior.

Una vez realizado el acceso a un componente del array con éste puede realizarse cualquier operación que permita su tipo. En C la única restricción que se hace en la utilización de los arrays a los índices es que su tipo de datos debe ser entero y su valor mayor o igual que 0.

Siendo el valor del índice para el límite inferior siempre 0 y el valor para el límite superior el número de componentes menos uno.

Para tablas de dos dimensiones, o también llamadas matrices, se utilizan dos subíndices para referenciar cada elemento, conjuntamente con el nombre de la tabla. El número máximo de dimensiones es el que impone el lenguaje.

int[,] matriz = new int[2,5];

matriz [0,0] = 10;
Console.WriteLine ("{0}",matriz[0,0]);


Cada variable declarada al iniciar el programa es un espacio en memoria que estamos reservando en la computadora.

Si quisieramos capturar el nombre de 5 alumnos con una calificación tendriamos que crear 5 variables del tipo string y 5 del tipo int.
Tendriamos algo como lo que sigue:






Video 2

20 octubre 2006

Simplificando un problema con funciones.



Simplificación de un problema.

En el problema del 12 de octubre en donde teniamos que evaluar la edad y dependiendo la edad le corresponderia pagar un descuento. Utilizaremos este código para ejemplificar el uso de Funciones.
...
if (edad >=5 && edad <= 14)
{
cat1 = cat1 + (boleto*.35F);
pago = boleto - (boleto*.35F);
Console.WriteLine ("Total a pagar: {0}",pago);
}
...


Vemos que esta porción de código se tiene que repetir por cada categoría. Vamos a modularizar este código de tal forma que lo podamos reutilizar para cada caso.

static float evaluaredad (float porciento, float acumulador,int vboleto)

{float pago;
acumulador=acumulador+(vboleto*porciento);
pago= vboleto-(vboleto*porciento);
Console.WriteLine("total a pagar:{0}",pago);
return acumulador;
}

Este código queda posicionado dentro de la clase principal, de tal forma que lo podamos reutilizar las veces requeridas.
La función regresa un valor y recibe 3 valores. Regresa el el valor del acumulador despues de haber sumado el descuento dado al cliente. Recibe 3 parametros, el descuento aplicado segun la categoria, el valor actual del acumulador dentro de la funcion principal y el valor del boleto.

using System;

namespace kungfuciones
{

class Class1
{
static float evaluaredad (float porciento, float acumulador,int vboleto)
{float pago;
acumulador=acumulador+(vboleto*porciento);
pago= vboleto-(vboleto*porciento);
Console.WriteLine("total a pagar:{0}",pago);
return acumulador;
}


[STAThread]
static void Main(string[] args)
{
string opc;
int edad;
float cat1=0,cat2=0,cat3=0,cat4=0,cat5=0;
do
{
Console.WriteLine("dame la edad");
edad=int.Parse(Console.ReadLine());

if (edad>=5&&amp;amp;amp;edad<=14) cat1=evaluaredad(.35F,cat1,200); if (edad>=15&&edad<=19) cat2=evaluaredad(.25F,cat2,200); if (edad>=20&&edad<=45) cat3=evaluaredad(.10F,cat3,200); if (edad>=46&&edad<=65) cat4=evaluaredad(.25F,cat4,200); if (edad>65)
cat5=evaluaredad(.35F,cat5,200);
Console.WriteLine("hay mas clientes?si/no");
opc=Console.ReadLine();
}while ( opc=="si");
Console.WriteLine("en la categoria 1 se ahorraron los clientes {0}",cat1);
Console.WriteLine("en la categoria 2 se ahorraron los clientes {0}",cat2);
Console.WriteLine("en la categoria 3 se ahorraron los clientes {0}",cat3);
Console.WriteLine("en la categoria 4 se ahorraron los clientes {0}",cat4);
Console.WriteLine("en la categoria 5 se ahorraron los clientes {0}",cat5);
Console.ReadLine();


}
}
}

Este seria el resultado final en donde despues de evaluar cada caso de cada categoría mandamos llamar la funcion la cual hara el descuento y regresara el valor acumulado.

*Este programa fue contribución de nuestro compañero Luis Garcia, Gracias por su contribución

19 octubre 2006

Video Turorial Funciones

1.- Funcion que no regresa ni recibe valor
2.- Funcion que no regresa pero recibe valor


Problemas con Funciones

  1. Escribir una funcion que realicen una entrada controlada de datos de forma que sólo admita valores enteros positivos.
  2. Escribir una función que tenga como parámetro un número entero y que devuelva 1 si el valor de número es par y 0 si es impar. Construir un programa que pruebe dicha funcíón.
  3. Escribir una función que tenga como parámetro número entero y devuelva si número es primo o no lo es. Construir un programa que pruebe dicha funcion.
  4. Escribir una función que permita elevar un número en coma flotante a una potencia entera, esto es, que resuelva la fórmula y = xn. Construir un programa que pruebe dicha función.
  5. Escribir una función en C que reciba cuatro valores reales representado las coordenadas de dos puntos en el plano (x, y) y devuelva la distancia existente entre los dos puntos.
  6. Escribir una función que devuelva el menor de tres números reales.
  7. Escribir una función que reciba tres números reales y los devuelva ordenados de mayor a menor.
  8. Dado un numero N que filtraremos a que sea positivo, escribir un programa que nos diga cuál es el numero primo más cercano a él por debajo. Hacer este programa usando la función primo construida (problema no. 3).
  9. Dado un numero N leído por teclado encontrar el numero P primo mas cercano a él por exceso. Hacer este programa usando la función primo construida.
  10. Genera un programa que simule un juego de adivinanzas de números del sgte. modo:
• Se genera un número al azar entre 1 y 15(función random)
• El jugador da al programa el número a comprobar y el programa contesta “te pasas” ó “no llegas” según el número esté por encima ó por debajo del numero clave.
• El número máximo de intentos es 7.

Tipos de Funciones o Modulos en C#

En esta sección vamos a ver los 4 tipos principales de Funciones.
  1. Funcion que no recibe y no regresa valor
  2. Funcion que recibe y no regresa valor
  3. Funcion que no recibe y regresa valor
  4. Funcion que recibe y regresa valor
Nuestras Funciones o Modulos las declararemos dentro de la clase Principal (Class1).

class Class1
{
static void Mifuncion()
{
//Elementos de la funcion
}
static void Main()
{
//elementos de la funcion principal
}
}

Ejemplos de los 4 tipos de Funcion


using System;

namespace Ej_Funciones
{
///
/// Summary description for Class1.
///

class Class1
{

//Funcion que no recibe ni regresa valor
static void Mensaje ()
{
for (int x = 0; x<10;x++)
Console.WriteLine (" {0} Hello World",x+1);
}
//Funcion que recibe pero no regresa
static void RecibeMensaje (string msj, int altura, int largo)
{ int area; area = altura*largo;
//Console.WriteLine ("Datos recibidos: "+msj+ " "+area.ToString());
Console.WriteLine ("Datos recibidos: {0} {1}",msj,area);
}
//Funcion que no recibe pero que si regresa variables
static int Regresavalor ()
{ int valorregresado = 100;
Console.WriteLine ("En mi funcion El valor que voy a regresar es una variable del tipo int: {0}",valorregresado);
return valorregresado;
}
//Funcion que recibe y regresa valor
static float Multiplica (float x, float y)
{ float producto;
producto = x*y;
return producto;
}


[STAThread]
static void Main(string[] args)
{
string msjPrin;
int altura = 10, largo =4;
int valorRecibido;
// Mando llamar la funcion que no recibe ni regresa valor
Mensaje ();
Console.WriteLine ("Escribe el Mensaje: ");

// Capturo la variable msjPrin la cual voy a enviar a la funcion para que la imprima en pantalla
msjPrin = Console.ReadLine ();
RecibeMensaje (msjPrin,altura, largo);

//Aqui mando llamar la funcion que regresa valor pero no recibe.
valorRecibido=Regresavalor ();
Console.WriteLine ("en la func principal: Este es el valor que recibo: {0}", valorRecibido);

// Este ejemplo nos permite ver que la funcion que llamamos realiza una operacion a traves de dos valores que le enviamos del tipo float. La funcion realiza una multiplicacion y nos resgresa el resultado y en nuestra funcion principal la mandamos imprimir.
float x, y;
Console.WriteLine ("Dame el primer valor:");
x= float.Parse (Console.ReadLine ());

Console.WriteLine ("Dame el segundo valor:");
y= float.Parse (Console.ReadLine ());

Console.WriteLine ("El producto de la multiplicacion es la siguiente {0}", Multiplica (x,y));
Console.ReadLine ();

}
}
}
Notas: Es importante aclarar que las variables que declaremos dentro de nuestras funciones son independientes de las variables declaradas dentro de la funcion principal main( ). Quiere decir que la vida de las variables solo existiran mientras se esten ejecutando las funciones.

18 octubre 2006

Funciones

Introducción

Una de las formas más adecuadas de resolver un problema de programación consiste en descomponerlo en subproblemas. A cada uno de ellos se le asocia una función que lo resuelve, de tal modo que la solución del problema se obtiene por medio de llamadas a funciones. A su vez, cada función puede descomponerse en subfunciones que realicen tareas más elementales, intentando conseguir que cada función realice una y sólo una tarea.


En Lenguaje C# una función se define de la siguiente forma:

tipo NombreFunción (parámetros formales)
{
...
cuerpo de la función
...
}

Al manejar funciones en C debemos tener en cuenta que a una función sólo se puede acceder por medio de una llamada. Nunca se puede saltar de una función a otra mediante una sentencia goto. Tampoco está permitido declarar una función dentro de otra.

En C, a diferencia de otros lenguajes, como Pascal, no existe distinción entre funciones y procedimientos. Por ello, una función puede estar o no dentro de una expresión. Si la función devuelve un valor, son válidas sentencias del tipo:

a = b * funcion (c, d);

Pero también podemos encontrar funciones solas en una línea de programa,

funcion (a, b, c);

incluso aunque la función devuelva algún valor. En ese caso la función realizaría la tarea encomendada y el valor devuelto se perdería. Esto no provoca ningún error.

Por último, hemos de tener en cuenta que, aunque una función puede formar parte de una expresión compleja, nunca se le puede asignar un valor. Por tanto, es incorrecto escribir sentencias como

funcion () = variable;

que sería tan improcedente como escribir

5 = variable;

El único método para que una función reciba valores es por medio de los parámetros formales.


Argumentos de funciones

Los parámetros formales son variables locales que se crean al comenzar la función y se destruyen al salir de ella. Al hacer una llamada a la función los parámetros formales deben coincidir en tipo y en número con las variables utilizadas en la llamada, a las que denominaremos argumentos de la función. Si no coinciden, puede no detectarse el error. Esto se evita usando los llamados prototipos de funciones que estudiaremos más adelante, en este capítulo.

Los argumentos de una función pueden ser:

• valores (llamadas por valor)
• direcciones (llamadas por referencia)

Llamadas por valor

En las llamadas por valor se hace una copia del valor del argumento en el parámetro formal. La función opera internamente con estos últimos. Como las variables locales a una función (y los parámetros formales lo son) se crean al entrar a la función y se destruyen al salir de ella, cualquier cambio realizado por la función en los parámetros formales no tiene ningún efecto sobre los argumentos. Aclaremos esto con un ejemplo.

static void intercambia (int x, int y)
{
int aux;

aux = x;
x = y;
y = aux;
}



void main ()
{
int a = 3, b = 4;

intercambia (a, b);
printf ("\nValor de a: %d - Valor de b: %d", a, b);
}





17 octubre 2006

Que se tomó en cuenta en la evaluación del 1er Parcial.

* Para la evaluación de los parciales se toma en cuenta lo siguiente:

20% tareas
10% participacion
70% examen
___________
100% Final

En el examen se tomó en cuenta:
1.- La lógica del programa
2.- Relizar los debidos Contadores y acumuladores
3.- Ciclos
4.- Condiciones de salida y restriccion de edad
5.- Mostrar toda la información al final del programa


Nota * AL PRINCIPIO DEL CURSO SE ESTABLECIO.

16 octubre 2006

Resultados 1er. Parcial

Los que quieran los resultados favor de enviar correo a
dgallo@uabc.mx
O si estan a favor de publicarlos por este medio, envien sus correos.

12 octubre 2006

Problema del dia 12 de octubre 2006

Realizar el programa siguiente:

1. Un teatro otorga descuentos según la edad del cliente.
Determinar la cantidad de dinero que el teatro deja de percibir por cada una de las categorías. Tomar en cuenta que los niños menores de 5 años no pueden entrar al teatro y que existe un precio único en los asientos (según la categoría). Los descuentos se hacen teniendo en cuenta el siguiente cuadro:


Edad

Descuento

Categoría 1

5 - 14

35 %

Categoría 2

15 - 19

25 %

Categoría 3

20 - 45

10 %

Categoría 4

46 - 65

25 %

Categoría 5

66 en adelante

35 %



  • El programa preguntará siempre la edad y le dirá el total a pagar (puedes crear un menú en donde preguntes en que categoría se encuentra).
  • Si es menor de 5 no permitirá la entrada y volverá a preguntar la edad al siguiente cliente. (no aplica en caso de menú)
  • El programa mostrara cuantos clientes han entrado.
  • El precio por boleto es de $200.00 pesos.
  • El programa preguntara si hay más clientes. Al no haber más, el programa desplegará por cada Categoría la cantidad de dinero que el teatro no percibió (la suma de todo lo que se ahorraron los clientes)
  • Mostrara el total de personas que entraron.

Ej.

(suponiendo que fueron 10 personas por cada categoría)
o En la Categoría 1 se ahorraron $700.00
o En la Categoría 2 se ahorraron $500.00
o En la Categoría 3 se ahorraron $200.00
o En la Categoría 4 se ahorraron $500.00
o En la Categoría 5 se ahorraron $700.00
o Entraron un total de 50 personas

static void Main(string[] args)
{
float cat1=0, cat2=0, cat3=0, cat4=0, cat5=0, pago=0;
string personas;
int edad=0;
int boleto = 200, contpersonas=1;

do
{
do
{
Console.WriteLine ("Cliente No. {0} Que edad tiene:? ", contpersonas);//mostrando el numero de personas y preguntando la edad del cliente
edad = int.Parse (Console.ReadLine());
if (edad < 5) // si es menor a 5 no se acepta
Console.WriteLine ("No pueden entrar menores de 5 años");
} while (edad < 5);
contpersonas ++;
if (edad >=5 && edad <= 14)
{ // Categiria 1, se le aplica el descuento y se acumula el total de los descuentos, de la misma forma en las demas categorías según el descuento.
cat1 = cat1 + (boleto*.35F);
pago = boleto - (boleto*.35F);
Console.WriteLine ("Total a pagar: {0}",pago);
}

if (edad >= 15 && edad <= 19)
{
cat2 = cat2 + (boleto*.25F);
pago = boleto - (boleto*.25F);
Console.WriteLine ("Total a pagar: {0}",pago);
}
if ( edad >=20 && edad <=45)
{
cat3 = cat3 + (boleto*.10F);
pago = boleto - (boleto*.10F);
Console.WriteLine ("Total a pagar: {0}",pago);
}
if (edad >= 46 && edad <=65)
{
cat4 = cat4 + (boleto*.25F);
pago = boleto - (boleto*.25F);
Console.WriteLine ("Total a pagar: {0}",pago);
}
if ( edad >=66)
{
cat5 = cat5 + (boleto*.35F);
pago = boleto - (boleto*.35F);
Console.WriteLine ("Total a pagar: {0}",pago);
}


Console.WriteLine ("Hay mas personas: [s/n]");
personas = Console.ReadLine ();

}while (personas =="s" );
Console.WriteLine ("En la Categoria 1 se ahorraron $ {0}",cat1);
Console.WriteLine ("En la Categoria 2 se ahorraron $ {0}",cat2);
Console.WriteLine ("En la Categoria 3 se ahorraron $ {0}",cat3);
Console.WriteLine ("En la Categoria 4 se ahorraron $ {0}",cat4);
Console.WriteLine ("En la Categoria 5 se ahorraron $ {0}",cat5);
Console.WriteLine ("Entraron {0} personas", contpersonas-1);
Console.ReadLine();
}