Mostrando entradas con la etiqueta Unidad I. Mostrar todas las entradas
Mostrando entradas con la etiqueta Unidad I. Mostrar todas las entradas

1.5 Manejo de Bits

Como sabrán, los datos en una computadora internamente se representan en código binario. El microprocesador solo entiende de ceros y unos. Luego mediante una serie de procesos nosotros vemos a este código ya transformado en números, caracteres , imágenes y sonido. Pero en realidad en la trastienda todo sigue siendo binario.

Los Bits

El método mas sencillo de representación son los números naturales. Por ejemplo, si tengo el número 85 en decimal, solo tengo que llevarlo a binario y obtengo una serie de unos y ceros. :

1010101 = 85 en binario

Cada dígito (un cero o un uno) de este número se llama bit. Java tiene una serie de operadores capaces de manipular estos dígitos, son los operadores de bits.


Operador Utilización Resultado
<< A <<> Desplazamiento de A a la izquierda en
B posiciones
>> A >> B Desplazamiento de A a la derecha en
B posiciones, tiene en cuenta el signo.
>>> A >>> B Desplazamiento de A a la derecha en
B posiciones, no tiene en cuenta el signo.
& A & B Operación AND a nivel de bits
| A | B Operación OR a nivel de bits
^ A ^ B Operación XOR a nivel de bits
~ ~A Complemento de A a nivel de bits


Para operar a nivel de bit es necesario tomar toda la longitud predefinida para el tipo de dato. Estamos acostumbrados a desechar los ceros a la izquierda en nuestra representación de números. Pero aquí es importante. Si trabajamos una variable de tipo short con un valor de 3, está representada de la siguiente manera :

0000000000000011

Aquí los 16 bits de un short se tienen en cuenta.

Desplazamientos

Los operadores de desplazamiento, mueven los bits a la izquierda o a la derecha. El primer operando será la victima a sacudir. El segundo indicará cuantas posiciones.

Desplazamiento a la izquierda con signo (cíclico)

Deseamos correr el número 33 dos posiciones a la izquierda. Entonces realizamos :

int j = 33;
int k = j <<>

Como resultado obtenemos el número original.

00000000000000000000000010000100 : k = 132 00000000000000000000000000100001 : m = 132 >> 2 ; m = 33

Podemos ver que el corrimiento a la derecha realiza una división de enteros. Divide por 2, tantas veces como posiciones desplazadas. Los huecos que quedan por la izquierda se cubren con los bits que van saliendo por la derecha (es cíclico).

Veamos que ocurre si pretendemos realizar un desplazamiento a la derecha con un número negativo. Tengan en cuenta que la representación de números es de complemento a 2. Si tengo una variable de tipo int con el valor –1 , internamente está almacenada de la siguiente forma :

11111111111111111111111111111111 : -1 complemento a 2

Ahora realicemos un programa para ver que ocurre con el desplazamiento.

public class CorreNeg {
public static void main(String args[]){
int x = -1;
int y = x >> 2;
System.out.println("El resultado es: " + String.valueOf(y));
}
}

La salida del programa nos indica que:

El resultado es: -1

Quedó exactamente igual. Prueben de correr el número tantas posiciones como tengan ganas y obtendrán el mismo resultado. Esto ocurre porque en el desplazamiento, los "huecos" que quedan a la izquierda se rellenan con el bit uno (1), quedando inalterable.

Este operador desplaza el conjunto de bit a la derecha y agrega a la izquierda los bits que faltan según el signo. Si se encuentra con un número positivo, agrega ceros, en cambio si son negativos agrega unos. Se lo conoce como desplazamiento con signo.

Desplazamiento a la derecha sin signo

Modifiquemos ligeramente el programa anterior agregándole al operador un símbolo >. Nos queda de esta manera :

int x = -1;
int y = x >>> 2;

Si ejecutamos el programa nos dice lo siguiente :

El resultado es: 1073741823

Veamos de donde salió este número raro. Si lo llevamos a binario tenemos :

00111111111111111111111111111111 : 1073741823 en binario

Ahora nos damos cuenta que se han agregado dos ceros a la izquierda. Este operador desplaza a la derecha, pero no tiene en cuenta el signo. Siempre agrega bit con el valor cero, por lo que se llama desplazamiento sin signo.

Operadores lógicos de bits

Estos operadores extienden las operaciones booleanas a los enteros. Para comprender como trabajan debemos descomponer los enteros en un conjunto de bits. El operador aplicará una operación lógica bit por bit, tomando el valor de uno como verdadero y el valor de cero como falso. De un operando toma un bit y aplica la operación al bit que tiene la misma posición del segundo operando. Como resultado obtenemos otro entero.

Operador AND de Bits

Si ambos bits comparados son 1, establece el resultado en 1. De lo contrario da como resultado 0.

int k = 132; // k: 00000000000000000000000010000100
int l = 144; // l: 00000000000000000000000010010000
int m = k & l; // m: 00000000000000000000000010000000

El resultado da 128

Operador XOR de Bits

Si uno de los bits comparados es 0 y el otro 1, el resultado es 1. Si ambos bits comparados son iguales, el resultado es 0.

int k = 132; // k: 00000000000000000000000010000100
int l = 144; // l: 00000000000000000000000010010000
int m = k ^ l; // m: 00000000000000000000000000010100

El resultado da 20

Operador NOT de Bits

Sólo invierte los bits, es decir, convierte los ceros en unos y viceversa. Observemos que es el único de esta familia que tiene un solo operando.

int k = 132; // k: 00000000000000000000000010000100
int m = ~k; // m: 11111111111111111111111101111011

El resultado da -133

Como los enteros negativos en Java se representan con el método del complemento a dos, podemos realizar un sencillo experimento de cambiarle el signo a un número. Para realizarlo debemos aplicar a un entero el operador NOT y sumarle uno.

int x = 123;
int y = ~x;
int z = y + 1;

El resultado da -123,


Fuente Original:

http://es.wikibooks.org/wiki/Programaci%C3%B3n_en_Java_/_Operadores_de_bits

Leer más...

1.4 Tipos de Datos Enumerados

Una enumeración o tipo enumerado es un tipo especial de estructura en la que los literales de los valores que pueden tomar sus objetos se indican explícitamente al definirla. Por ejemplo, una enumeración de nombre Tamaño cuyos objetos pudiesen tomar los valores literales Pequeño, Mediano o Grande se definiría así:

enum Tamaño {
Pequeño,
Mediano,
Grande
}


Tipos enumerados. Un tipo enumerado o enumeración está construido por una serie de constantes simbólicas enteras. Los tipos enumerados se tratan de modo ligeramente diferente en C++ que en ANSI C. El nombre de la etiqueta enum se considera como un nombre de tipo igual que las etiquetas de struct y union. Por tanto se puede declarar una variable de enumeración, estructura o union sin utilizar las palabras enum, strcut o union. C define el tipo de enum de tipo int. En C++, sin embargo, cada tipo enumerado es su propio tipo independiente. Esto significa que C++ no permite que un valor int se convierta automáticamente a un valor enum. Sin embargo, un valor enumerado se puede utilizar en lugar de un int.

Ejemplo:

enum lugar{
primero,
segundo,
tercero
};

lugar pepe=primero; //correcto
int vencedor=pepe; //correcto
lugar juan=1; //incorrecto

La última sentencia de asignación es aceptable en C pero no en C++, ya que 1 no es un valor definido en lugar.

Elementos Basicos.

Ya sabemos que en los algoritmos aparecen objetos que tienen tres características:

Nombre.
Tipo.
Valor:
Variables.
Constantes.

Tipo:

• Define el conjunto de valores que puede tomar un determinado

Objeto:

• Determina las operaciones que se pueden aplicar a un objeto. • Define el espacio que será necesario reservar en memoria para albergar a un objeto. • Define la interpretación del valor almacenado en memoria.

Cardinalidad:

• Es el número de valores que tiene un tipo de datos. La simbolizaremos con ‘#’.

Operadores básicos:

• Aspecto práctico: manipulación de objetos.

Para ello a cada tipo de datos se le asocia un conjunto de operadores básicos. ¿Cuántos?, ¿cuáles?. Como criterio general: se le asocian aquellos que posibiliten al programador construir cualquier operación adicional, en función de los básicos, de una forma eficiente. • Un mismo símbolo (por ejemplo ‘+’) puede utilizarse como operador para distintos tipos de datos. Esto se denomina “sobrecarga”.

Tipos de datos simples

Compatibilidad de tipo:

• Los operadores tienen que aplicarse a objetos de tipos de datos compatibles (iguales o no). • La información del tipo de dato ayuda a los compiladores a detectar operaciones inapropiadas con tipos de datos no compatibles. • Hay lenguajes de programación más estrictos que otros en cuanto al chequeo de tipos. • En nuestro pseudolenguaje seremos lo más flexible posible, pero tendiendo a aplicar operadores a tipos de datos iguales, con el fin de evitar posibles errores de diseño.

Declaración de constantes y variables en nuestro pseudo lenguaje:

Constantes nombre = valor Variables nombre : Tipo

Clasificacion de tipos de datos simples:

• Tipos simples: Predefinidos (N, Z, R, C, B)

Definidos por el programador (enumerado y subrango)

• Tipos estructurados. Arrays Cadenas de Caracteres

--
Fuente Original:

http://www.mitecnologico.com/Main/TiposDeDatosEnumerados
Leer más...

1.3 Registros Variantes

Los registros variantes son una especie de sentencia “case” dentro de un registro y son muy útiles cuando tenemos ciertos atributos que sabemos de antemano que sólo usaremos en ciertas situaciones.

Los registros variantes ahorran memoria, porque como sólo una de las estructuras debidas dentro de ellos puede usarse a la vez, el compilador sólo necesita reservar memoria para la mayor de ellas (en vez de tener que hacerlo para todos los componentes del registro).

Registros Variantes Esta propiedad de compartición de espacio en memoria es similar a la de los bloques disjuntos en ALGOL. De hecho, también en el caso de los registros variantes se permite el anidamiento.

Por tanto, los registros variantes son muy eficientes y seguros, porque sólo permiten al programador realizar operaciones que tengan algún significado dentro del contexto dado y que sean permisibles dentro del registro.

Sin embargo, el problema con esta estructura de datos es que no requiere ser inicializada. Esto significa que podríamos tener cualquier valor dentro de un registro variante después de modificar el valor de un campo identificador.

El acceso a variables no inicializadas puede dar lugar a que se usen valores que fueron dejados previamente en el bloque y que compartían la misma zona de memoria. En el caso de los registros variantes, este valor puede incluso ser de un tipo diferente al esperado.

La raíz del problema es que los registros variantes permiten una forma de “aliasing”, ya que los campos de las diferentes variantes son todos “alias” de las mismas posiciones de memoria.

He aquí un archvivo que nos habla sobre los registros variantes dale click aquí para descargar.

"Proximamente subiremos pequeños programas para ejemplificar mejor lo temas, revisa en la siguiente dirección http://geeksmarts.4shared.com ahí podrás descargar el material que vayamos subiendo."

Leer más...

1.2- Uniones de Datos

Demasiado sencillo para ser verdad, en si la unión de datos como su nombre lo dice, une o enlaza dos registros de variables para que puedan ser utilizados de una sola forma, pero en distintos métodos y obtener una salida de dos en una.

Las uniones de XDR son diferentes a las de C, ya que tienen un discriminante. Su sintaxis es la siguiente:
union tipo_union switch (int discriminante) {
case valor1: declaración 1;
case valor2: declaración 2;
………………………
default: declaración N;
};
Hay que resaltar que una de las declaraciones de la unión puede ser de tipo void, lo que indica que en ese caso no hay datos asociados, o sea, no se transmitiría nada al enviar esa unión (sólo el valor del discriminante, claro). El único otro lugar donde puede aparecer un void en XDR es en la definición del argumento o del valor devuelto por una función. Este tipo XDR se proyecta en C como un tipo struct que contiene un tipo unión de C:
struct tipo_union {
int discriminante;
union {
declaración 1;
declaración 2;
…………..
declaración N;
} tipo_union_u;
};
Nótese que en el código del cliente y del servidor habría que especificar el campo tipo_union_u para acceder a los campos más internos. Asimismo, si uno de los casos tiene asociada una definición void, en C desaparecería directamente esa declaración. Para aclarar el uso del tipo union de XDR, se presenta, a continuación, un ejemplo. Supóngase que se quiere definir un servicio RPC denominado read, que devuelve un valor Booleano que indica si la operación se realizó con éxito. En caso afirmativo, la función devuelve los datos leídos. El tipo XDR que define el valor devuelto por esa función de servicio podría ser el siguiente:
union read_res switch (bool exito) {
case FALSE: void;
case TRUE: opaque datos<>;
};
Nótese que, suponiendo que tenemos una variable de ese tipo llamada resultado, para acceder a los datos habría que especificar lo siguiente:
resultado.read_res_u.datos
Fuente Original:
http://www.mitecnologico.com/Main/UnionesDeDatosUnion


Leer más...

1.1- Agregacion De Datos Struct

La palabra clave struct define una estructura. Por tratarse de un tipo de datos puede utilizarse directamente para definir una variable. La variable aparece entre corchetes puesto que puede ser omitida. Si se especifica una variable, estaremos definiendo una
variable cuyo tipo será la estructura que la precede.
struct [Nombre_de_la_estructura]
{
tipo1 campo1;
tipo2 campo2;
.
.
tipoN campoN;
} [variable];

Si la variable no es indicada definimos un nuevo tipo de datos (struct Nombre_de_la_estructura), que podremos utilizar posteriormente. Si es el nombre de la estructura lo que se omite, tendremos que especificar obligatoriamente una variable que tendrá esa estructura y no podremos definir otras variables con esa estructura sin tener que volver a especificar todos los campos. Lo que se encuentra dentro de las llaves es una definición típica de variables con su tipo y su identificador.

Todo esto puede parecer un poco confuso pero lo aclararemos con unos ejemplos.
struct punto
{
float x;
float y;
int color;
} punto_de_fuga;
Aquí estamos definiendo una variable llamada punto_de_fuga cuyo tipo es una estructura de datos formada por tres campos y a la que hemos llamado punto. Dos de ellos son de tipo float y representan las coordenadas del punto, el tercer valor es un entero que indica el color de ese punto. En este caso hemos definido una variable y una estructura. Al disponer de un identificador para esta última podemos definir nuevas variables de esta estructura.
struct punto origen1;
struct punto final1;
Donde origen1 y final1 son variables de tipo struct punto que hemos definido anteriormente. Si en la definición de punto_de_fuga no se hubiese incluído un identificador para la estructura (en este caso el identificador es punto), no podríamos definir nuevas variables con esa estructura ya que no estaría identificada por ningún nombre.

También podríamos haber excluído el nombre de la variable (punto_de_fuga). En este caso lo que definiríamos sería una estructura llamada punto que pasaría a ser un nuevo tipo disponible por el usuario. Así los tipos de variables de que dispondríamos ahora serían:
int

float
double
char
struct punto
Por tanto podríamos definir cualquier variable con estos tipos o incluso definir matriz de estos tipos.

struct punto matriz_de_puntos[30];

Así estaríamos definiendo una matriz de 30 elementos en la que cada elemento es una struct punto con sus tres campos.

Lo que ahora nos interesa es saber como referenciar esos campos y acceder o modificar, por tanto la información que contienen. Esto se consigue separando el identificador del campo de la variable mediante un punto.

Fuente Original:

http://www.mitecnologico.com/Main/AgregacionDeDatosStruct

Leer más...