jueves, 29 de diciembre de 2011

555 Astable

El circuito 555 es un circuito muy utilizado en temporizadores y aquí os traigo un ejemplo del circuito 555 funcionando como Astable, es una captura de pantalla donde podéis ver cómo hay que configurarlo y el resultado que produce:

martes, 27 de diciembre de 2011

Punteros y Variables Dinámicas

Términos muy importantes a la hora de programar son los punteros y las variables dinámicas , son fundamentales, por ello en este post vamos a tratar de explicarlos para que podamos entenderlos de la mejor manera posible.

La estructura secuencia:

          La estructura secuencia puede definirse como un esquema de datos del tipo iterativo, pero con un número variable de componentes. La estructura secuencia resulta parecida a una formación con número variable de elementos. Las operaciones de construcción pueden incluir: añadir o retirar componentes al principio de la secuencia, añadir o retirar componentes al final de la secuencia, añadir o retirar componentes en posiciones intermedias de la secuencia, etc. Las operaciones de acceso pueden ser: acceso secuencial las componentes deben tratarse una por una, en el orden en que aparecen en la secuencia, acceso directo se puede acceder a cualquier componente directamente indicando su posición, como en una formación o vector.

          En muchos casos, y en particular cuando el acceso es secuencial, el tratamiento de una secuencia se realiza empleando un cursor. El cursor es una variable que señala a un elemento de la secuencia. El acceso, inserción o eliminación de componentes de la secuencia se hace actuando sobre el elemento señalado por el cursor. Dicho elemento lo representaremos simbólicamente como cursor empleando una flecha como símbolo gráfico para designar el elemento de información señalado por otro. Para actuar sobre el cursos se suelen plantear las siguientes operaciones: iniciar que pone el cursos señalando el primer elemento, avanzar que pasa el cursor al siguiente elemento y fin que es una función que indica si el cursor ha llegado al final de la secuencia.

Variables dinámicas:

          Una manera de realizar estructuras de datos ilimitadas en C+- es mediante el empleo de variables dinámicas. Una variable dinámica no se declara como tal, sino que se crea en el momento necesario, y se destruye cuando ya no se necesita. Las variables dinámicas no tienen nombre, sino que se designan mediante otras variables llamadas punteros o referencias.

Punteros:

          En C+- los punteros o referencias son variables simples cuyo contenido es precisamente una referencia a otra variable. El valor de un puntero no es representable como número o texto. Para poder detectar si un puntero señala realmente o no a otra variable, existe en C+- el valor especial NULL. Este valor es compatible con cualquier tipo de puntero, e indica que el puntero no señala a ninguna parte, por lo tanto debería ser asignado a cualquier puntero que sepamos que no señala a ninguna variable. Normalmente se usará para inicializar las variables de tipo puntero al comienzo del programa. La inicialización no es automática, sino que debe ser realizada expresamente por el programador. Esta sentencia garantiza que sólo se usa la variable apuntada cuando realmente existe.

Uso de variables dinámicas:

          Una variable de un programa se corresponde, en general, con una zona concreta de la memoria que el compilador reserva para almacenaren ella el valor de la variable. La forma mas sencilla de crear una variable dinámica es mediante el operador new. La variable dinámica se crea a base de reservarle el espacio necesario en una zona general de memoria gestionada dinámicamente. La sentencia delete, permite destruir la variable dinámica que señala un puntero. Una variable dinámica puede estar referenciada por más de un puntero. Esto ocurre cuando se copia un puntero en otro. Un problema delicado al manejar variables dinámicas es que pueden quedar perdidas, sin posibilidad de hacer referencia a ellas. Una variable perdida es totalmente inútil y representa una pérdida de la capacidad de memoria disponible.

Una definición recursiva es aquella en que se hace referencia a sí misma. Sería deseable que una secuencia ilimitada se pudiese definir de manera recursiva, sin necesidad de punteros.

Punteros y paso de argumentos:

          Un puntero puede pasarse como argumento a un subprograma. En general el valor de un puntero en sí mismo no es significativo, sino que el puntero es sólo un medio para designar la variable apuntada. Desde un punto de vista conceptual el paso de un puntero como argumento puede ser considerado equivalente a pasar como argumento la variable apuntada.

jueves, 8 de diciembre de 2011

Circuito codificador para controlar ALU 74181

La ALU 74181 es una Unidad Aritmético Lógica que está implementada con un circuito integrado TTL de la serie 7400 y cómo dato anecdótico fue la primera ALU completa en un chip.

En este post os traigo una captura de pantalla de un circuito codificador que tuve que hacer para una asignatura para controlar una ALU 74181 dependiendo de si la palabra que entrara era: A>B, si A=B y si A<B

Circuito codificador ALU 74181
Circuito Codificador para ALU 74181

domingo, 4 de diciembre de 2011

Tipos en C+-

Pequeño esquema resumen con los tipos más importantes en el lenguajes de programación C + -:

Tipos definidos:

          Tipos predefinidos: int, char y float.

          La definición de tipos supone crear un nuevo nivel de abstracción dentro del programa.

          En C+- la declaración de cada nuevo tipo siempre se inicia con la palabra clave typedef. Ejemplo:

                    typedef int TipoEdad;

Tipo enumerado:

          Una manera sencilla de definir un nuevo tipo de dato es enumerar todos los posibles valores que pueden tomar. En C+- el nuevo tipo enumerado se define det´ras de la palabra clave enum mediante un identificador del tipo y a continuación se detalla la lista con los valores separados por comas (,) y encerrados entre llaves {…}. Cada posible valor también se describe mediante un idenfiticador. Estos identificadores al mismo tiempo quedan declarados como valores constantes. Ejemplo:

                    typedef enum TipoDia {

                              Lunes, Martes, Miercoles, Jueves,

                              Viernes, Sabado, Domingo

                    };

          Los tipos de datos enumerados forman parte de una clase de tipos de C+- denominados tipos ordinales, a la cual pertenecen también los tipos int y char, pero no el tipo float.

Uso de tipos enumerados:

          Los tipos enumerados se emplean de manera similar a los tipos predefinidos. El identificador de tipo se puede emplear para definir variables de este tipo, y los identificadores de los valores enumerados se emplean como las constantes con nombre. Al igual que para el resto de los tipos ordinales, con los tipos enumerados se puede utilizar la notación int(e) para obtener la posición de un valor en la lista de valores del tipo. La operación inversa, que permita conocer qué valor enumerado ocupa una determinada posición, se consigue mediante la notación inversa que hace uso del identificador del tipo enumerado y que se invoca de la siguiente forma: TipoEnumerado(N) que devuelve el valor que ocupa la posición N en la colección de valores del tipo TipoEnumerado.

El tipo predefinido bool:

          Bool responde a la siguiente definición, análoga a la de un tipo enumerado: typedef enum boll { false, true};. El nombre bool es el identificador del tipo y las constante simbólicas false y true corresponde a los valores de verdad falso y cierto, respectivamente. Es posible realizar operaciones entre ellas. En concreto, entre operandos booleanos (variables o no) es posible realizar las operaciones lógicas para formas expresiones lógicas cuyos operadores son: && para y, || para o y ! para no.

          Es frecuente definir funciones cuyo resultado es un valor booleano cuando se quiere realizar un test sobre los argumentos de la función. Este tipo de funciones se denominan predicados.

Tipos estructurados:

          Un tipo estructurado de datos, o estructura de datos, es un tipo cuyos valores se construyen agrupando datos de otros tipos más sencillas. Los elementos de información que integran un valor estructurado se denominan componentes. Todos los tipos estructurados se definen, en último término, a partir de tipos simples combinados.

Tipo formación:

          Las estructuras de datos de tipo formación son quizá las más básicas, o al menos las que se introdujeron primero en los lenguajes de programción imperativos. Estas estructuras se denominan genéricamente formaciones y permiten la generalización de la declaración, referencia y manipulación de colecciones de datos todos del mismo tipo.

Tipo vector:

          Un vector está constituido por una serie de valores, todos ellos del mismo tipo, a los que se les da un nombre común que identifica a toda la estructura globalmente. Cada valor concreto dentro de la estructura se distingue por su índice o número de orden que ocupa en la serie.

Declaración de vectores:

          typedef TipoElemento TipoVector[NumeroElementos];

Para poder utilizar los tipos declarados es necesario declarar a su vez, posteriormente, las correspondientes variables, pues sino no podremos utilizarlas.

Es obligatorio que todas las variables se declaren precedidas del identificador de un tipo predefinido del lenguaje o bien definido previamente en el programa. Tanto en la sintaxis de C+- como en el Manual de Estilo está expresamente prohibido la declaración de variables de tipo anónimo.

Inicialización de un vector:

          En C+- cuando se declara una única variable de cualquier tipo simper es posible darle un valor inicial. En el caso de un vector la inicialización afecta a todos sus elementos y por tanto la notación es algo especial y en ella se indica el valor inicial de todos los elementos agrupándolos entre llaves {…} y separándolos por comas (,).

Operaciones con elementos de vectores:

          La mayoría de las operaciones interesantes con vectores hay que realizarlas operando con sus elementos uno por uno. La referencia a un elemento concreto de un vector se hace mediante el nombre del vector seguido, entre corchetes, del índice del elemento referenciado.

Como índice para designar un elemento de un vector se puede utilizar una variable o expresión, siempre que sea de tipo entero.

La comprobación de que el índice para acceder a un elemnto de vector está dentro del rango permitido es responsabilidad del programador. Muchos ataques informáticos aprovechan la falta de previsión de esta comprobación para alterar el funcionamiento normal de un programa suministrándole datos de mayor tamaño que el previsto y provocar lo que se denomina en inglés buffer overrum.

Operaciones globales con vectores: para realizar una operación global con vectores la operación se tiene que programar explícitamente mediante un bucle que realice la operación elemento a elemento.

Paso de argumentos de tipo vector: en C+- el modo por defecto de paso de argumentos de tipo formación, y más concretamente de tipo vector, es el paso por referencia. En C+- cuando se utilizan argumentos de tipo formación y no se quiere que se modifiquen los parámetros reales en la llamada al procedimiento, los argumentos formales deben ir precedidos de la palabra clave const.

Vectores de caracteres: Cadena (String)

          En C+- cualquier tipo vector cuya declaración sea de la forma: typedef char Nombre[N] se considera una cadena o string, con independencia de su longitud particular, esto es, del valor de N

Una cadena de caracters (en inglés string) es un vector en el que se pueden almacenar textos de diferentes longitudes (si caben). Para distinguir la longitud útil en cada momento se reserva siempre espacio para un carácter más, y se hace que toda cadena termine con un carácter nulo ‘\0’ situado al final. Por tanto, para declarar una cadena de un máximo de veinte caracteres se debe hacer de la siguiente forma: typedef char Cadena20[21]

Tipo tupla: otra forma de construir un dato estructurado consiste en agrupar elementos de información usando el esquema de tupla o agregado. En este esquema el dato estructurado está formado por una colección de componentes, cada uno de los cuales puede ser de un tipo diferente. Podemos definir tupla como colección de elementos componentes, de diferentes tipos, cada uno de los cuales se identifica por un nombre.

Tipo registro (struct):

          Los esquemas de tupla pueden usarse en programs en C+- definiéndolos como estructuras del tipo registro o struct. Un resgistro o struct es una estructura de datos formada por una colección de leemtnos de información llamados campos.

Definición de registros: la declaración de un tipo registro en C+- se hace utilizando la palabra clave struct de la siguiente forma:

                    Typedef struct TipoFecha {

                              int dia;

                              tipoMes mes;

                              int anno;

Variables de tipo registro y su inicialización: para declarar variables de tipo registro es necesario haber realizado previamente la definición del tipo del registro. También estas variables se pueden inicializar en la declaración de una manera semejante a las formaciones agrupando los valores iniciales entre llaves {…} y separándolos por una coma (,), pero teniendo en cuenta el tipo de dato de cada campo.

Uso de registros: al manejar datos estructurados de tipo registro se dispone de dos posibilidades: operar con el dato completo, o bien operar con cada campo por separado. Las posibilidades de operar con el dato completo son bastante limitadas. La única operación admisible es la de asignación. La forma de hacer referencia a un campo es mediante la notación: registro.campo

jueves, 1 de diciembre de 2011

Desarrollo de Programas 3/3

Aquí está el último apartado del resumen de desarrollo de programas, aquí tenéis el anterior por si queréis verlo (Desarrollo de Programas 2/3)

Usar subprogramas como técnica de abstracción

Operaciones abstractas: los subprogramas constituyen un primer paso hacia la metodología de programación basada en abstracciones. Los subprogramas permiten definir operaciones abstractas. Una abstracción es una visión simplificada de una cierta entidad, de la que sólo consideramos sus elementos esenciales, prescindiendo de los detalles. Las entidades que podemos abstraer para materializarlas como subprogramas son, en general, operaciones. Con la palabra operación englobamos tanto la idea de acción como la de función.

La visión abstracta es la que permite usar dicha operación sin más que conocer que hace dicha operación. La visión detallada es la que define cómo se hace dicha operación y permite que el procesador la ejecute.

La visión abstracta es la interfaz de la operación y la visión detallada es la implementación.

En programación la idea de función surge al aplicar el concepto de abstracción a las expresiones aritméticas. Una expresión representa un nuevo valor obtenido por cálculo a partir de ciertos valores ya conocidos que se usan como operandos.

Los operandos que intervienen en el cálculo del valor de la función y que pueden cambiar de una vez a otra se especifican como argumentos de dicha función. La función aparece así como una expresión parametrizada.

La cualidad más deseable al utilizar funciones es conseguir su transparencia referencial, que significa que la función devolverá siempre el mismo resultado cada vez que se la invoque con los mismos argumentos.

La transparencia referencial se garantiza si la realización de la función no utiliza datos exteriores a ella, es decir, si no emplea variables externas al subprograma, datos procedentes del exterior, llamadas a otras funciones o procedimientos que no posean transparencia referencial.

Las funciones que cumplen la cualidad de transparencia referencial y que no producen efectos laterales o secundarios se denominan funciones puras.

Los procedimientos pueden ser considerados como acciones abstractas, igualmente parametrizadas. Como acciones abstractas, podemos tener dos visiones de un procedimiento, la primera es la visión abstrata o especificación, formada por la cabecera del procedimiento y una descripción de qué hace dicho procedimiento y la segunda es la realización, en que se detalla codificada en el lenguaje de programación elegido, cómo se hace la acción definida como procedimiento.

Podemos recomendar que los procedimientos se escriban siempre como procedimientos puros, entendiendo por ello que no produzcan efectos laterales o secundarios. Se garantiza que un procedimiento cumple con esta cualidad si en su realización no utiliza variables externas al subprograma y llamadas a otros subprogramas que no sean procedimientos o funciones puras.

Desarrollo usando abstracciones:

          Desarrollo descendente: la estrategia de desarrollo descendente, es simplemente el desarrollo por refinamientos sucesivos, teniendo en cuenta además la posibilidad de definir operaciones abstractas. En cada etapa de refinamiento de una operación habrá que optar por una de las alternativas siguientes: considerar la operación como operación terminal, considerarla como operación compleja o considerar la operación como operación abstracta.

En general resultará ventajoso refinar una operación como operación abstracta si se consigue alguna de las ventajas siguientes: mezclar operaciones con un nivel de detalle muy diferente y evitar escribir repetidamente fragmentos de código que realicen operaciones análogas.

          Reutilización: la realización de ciertas operaciones como subprogramas independientes facilita lo que se llama reutilización de software. Si la operación identificada como operación abstracta tiene un cierto sentido en sí misma, es muy posible que resulta de utilidad en otros programas.

          Desarrollo para reutilización: para aplicara de manera eficaz las técnicas de reutilización de software es preciso pensar en las posibles aplicaciones de un cierto subprograma en el momento de especificarlo, con independencia de las necesidades particulares del programa que se está desarrollando en ese momento. La principal ventaja es que se amplía el conjunto de aplicaciones en que se podrá reutilizar más adelante el subprograma que se está desarrollando ahora. Su principal inconveniente es que será más costoso hacer el desarrollo del subprograma planteado como operación de uso general, que planteando como operación particular, hecha a medida del programa que lo utiliza en este momento.

          Desarrollo ascendente: la metodología de desarrollo ascendente consiste en ir creando subprogramas que realicen operaciones significativas de utilidad para el programa que se intenta construir, hasta que finalmente sea posible escribir el programa principal, de manera relativamente sencilla, apoyándose en los subprogramas desarrollados hasta ese momento. En este caso se trata de que la identificación de las operaciones no surja de un proceso de descomposición o refinamiento de alguna acción en particular, sino simplemente pensando en el programa que se desarrolla, casi como una más de las posibles aplicaciones futuras.

Programas robustos:

Un programa se dice que es un programa robusto si su operación se mantiene en condiciones controladas aunque se le suministren datos erróneos.

          Programación a la defensiva: la postura más cómoda desde el punto de vista del programador es declinar toda responsabilidad  en el caso de que los datos no sean válidos. Si los datos de entrada con cumplen con los requisitos previstos, el programa puede entonces hacer cualquier cosa. La llamada programación a la defensiva consiste en que cada programa o subprograma esté escrito de manera que desconfíe sistemáticamente de los datos o argumentos con que se le invoca, y devuelva siempre como resultado el resultado correcto o una indicación precisa de error.

          Tratamiento de excepciones: ante la posibilidad de errores en los datos con que se opera hay que considerar dos actividades diferentes: detección de la situación de error y corrección de la situación de error. Existen varios esquemas de programación posibles para tratamiento de errores. Un modelo recomendado es el modelo de terminación. En este modelo, si se detecta un error en una sección o bloque del programa, la acción de tratamiento del error reemplaza al resto de las acciones pendientes de dicha sección, con lo cual tras la acción correctora se da por terminado el bloque. La sentencia throw provoca la terminación del subprograma de manera semejante a una sentencia return. Sin embargo, ambas terminaciones son distintas: con return se realiza una terminación normal y con throw se realiza una terminación por excepción. La sentencia throw puede devolver cualquier tipo de resultado en excepción. Además, la sentencia throw es la encargada de indicar que se ha detectado una situación de error y lanzar el mecanismo de tratamiento de excepciones. La sentencia try agrupa el bloque de código en el que se programa el algoritmo del problema a resolver sin tener en cuenta las posibles excepciones que se pudieran producir. A continuación, la sentencia catch agrupa el código para el tratamiento de la excepción que se declara entre paréntesis.

sábado, 26 de noviembre de 2011

Funciones y Procedimientos

Las funciones y los procedimientos son muy importantes a la hora de programar así cómo conocer bien que son los argumentos y cómo se usan, con este pequeño resumen espero que os ayude a entenderlos mejor.


Funciones y procedimientos:

          Las dos formas clásicas de subprogramas, disponibles prácticamente en cualquier lenguaje imperativo, son las funciones y los procedimientos.

Concepto de subprograma:

Un subprograma, como su propio nombre indica, es una parte de un programa. Un subprograma sirve para resolver un subproblema.

La técnica de refinamientos sucesivos sugiera descomponer las operaciones complejas de un programa en otras más simples.

Las dos formas fundamentales de subprogramas en programación imperativa son las funciones y los procedimientos.

Funciones:

Una función es un tipo de subprograma que calcula como resultado un valor único a partir de otros valores dados como argumentos.

El primer paso en el manejo de una función es declarar su interfaz. Esta declaración incluye su nombre, los argumentos que necesita con el correspondiente tipo para cada uno de ellos, y el tipo de resultado que proporciona.

Ejemplo:

          int edad (int nacimiento, int fechaactual)


Los argumentos que aparecen en la cabecera son los argumentos formales. La definición completa de una función se compone de una cabecera seguida de un cuerpo de función que tiene la misma estructura que un bloque de programa completo. Este bloque comienza con una parte declarativa y continúa con una parte ejecutiva. En la parte declarativa se pueden declarar constantes y variables locales que sólo son visibles en el cuerpo de la función. La parte ejecutiva estará constituida por una secuencia de sentencias. En las sentencias que constituyen el cuerpo de la función se puede y se debe hacer uso de los argumentos formales declarados en su interfaz.



          Uso de funciones:

Para usar un función en los cálculos de un programa se invoca dicha función escribiendo su nombre y a continuación, entre paréntesis, los valores concretos de los argumentos, separados por comas.

Existen funciones predefinidas en el propio lenguajes que están siempre disponibles en cualquier programa. Son en general seudofunciones.


Procedimientos:

Un procedimiento es un subprograma que realiza una determina acción. A diferencia de las funciones, un procedimiento no tiene como objetivo en general, devolver un valor obtenido por cálculo. Un procedimiento es una forma de subprograma que agrupa una sentencia o grupo de sentencias que realizan una acción, y permite darles un nombre por el que las pueden identificar posteriormente. Otra forma de ver a los procedimientos es como acciones parametrizadas.

La definición de un procedimiento es prácticamente igual a la de una función, la diferencia principal es que no se declara el tipo de valor del resultado, ya que no existe dicho valor. La palabra reservada void es la que indica que no hay resultado de ningún tipo. Además, con cierta frecuencia interesa definir procedimientos sin argumentos. En estos casos sólo es necesario dar el nombre y no habrá lista de argumentos entre los paréntesis.

Si se desea, en la definición de un procedimiento pueden usarse también sentencias de retorno, pero con un significado algo diferente que en el caso de las funciones. La sentencia return; sirve para terminar la ejecución del procedimiento en ese momento y volver al punto siguiente donde se invocó.

Ejemplo:

          void escribiralgo () {

                    printf (“algo”);

          }



Uso de procedimientos:

Para usar un procedimiento hay que invocarlo. Dicha invocación o llamada constituye por sí sola una sentencia. Un procedimiento se invoca escribiendo su nombre y a continuación, si los hay, los valores de los argumentos particulares en esa llamada, separados por comas. Los valores de los argumentos pueden darse, en general, mediante expresiones. Si no hay argumentos s su suprimen los paréntesis. Los argumentos en la llamada (argumentos reales) deberán ser compatibles con los indicados en la declaración (argumentos formales).

Ejemplo de invocación de un procedimiento:

          escribiralgo();



Paso de argumentos:

          La manera fundamental de comunicar información entre las sentencias de un subprograma y el programa que lo utiliza es mediante los argumentos. Existen dos formas de realizar esta comunicación, paso por valor y paso por referencia.



Paso por valor:

Los argumentos representan valores que se transmiten desde el programa que llama hacia el subprograma. En el caso de las funciones hay además un valor de retorno, que es el valor de la función que se transmite desde el subprograma hacia el programa que lo llamó.



Paso por referencia:

El mecanismo de paso por valor no permite que el subprograma modifique directamente una variable. Para conseguirlo hay que usar el paso de argumentos por referencia. El paso de un argumento por referencia se indica en la cabecera del subprograma, anteponiendo el símbolo & al nombre del argumento formal. Si un argumento se pasa por referencia ya no será válido usar como argumento real una expresión. El argumento real usado en la llamada deber ser necesariamente una variable del mismo tipo. Esta variable será utilizada en el subprograma como si fuera suya, es decir, la asignación del nuevo valor al argumento modifica realmente la variable externa pasada como argumento.



Visibilidad. Estructura de bloques:

La definición de un subprograma está formada por una cabecera o interfaz, y un bloque de código que es el cuerpo del subprograma. Ese bloque de código constituye una barrera de visibilidad que hace que los elementos declarados en el interior del cuerpo de un subprograma no sean visibles desde el exterior. Es decir, la definición de un subprograma construye un nuevo elemento, utilizable en el resto del código, y al mismo tiempo realiza una ocultación de sus detalles de realización. Los elementos definidos en el ámbito más externo son elementos globales, mientras que los elementos definidos en el interior del bloque de un subprograma son elementos locales a dicho subprograma. Cada bloque es completamente opaco desde el exterior y se puede considerar como una caja negra. La vista externa de la cabecera es realmente la interfaz del subprograma. El contenido lógico de la interfaz es lo que se denomina signatura del subprograma, que es suficiente para comprobar si las invocaciones son consistentes con su definición.



Recursividad de subprogramas:

Cuando un subprograma hace una llamada a sí mismo se dice que es un subprograma recursivo.



Problemas en el uso de subprogramas:

          Uso de variables globales. Efecto secundarios:

Una cualidad deseable es la transparencia referencial, que consiste en que el efecto de una llamada al subprograma pueda predecirse simplemente con la información contenida en el código de la llamada. Dicho de otro modo, siempre que se invoque al subprograma con los mismos valores de los argumentos se debe obtener el mismo resultado. La transparencia referencial se garantiza si el código del subprograma utiliza solamente elementos mencionados en la lista de argumentos o definidos como elementos locales. Cuando un subprograma modifica alguna variable externa, se dice que está produciendo efectos secundarios o laterales, side effects. Por tanto la transparencia referencial es deseable tanto para las funciones como para los procedimientos. Sin embargo, para las funciones es una cualidad casi imprescindible. Una función que no produzca efectos laterales y todos sus argumentos se pasen por valor se dice que es una función pura.



Redefinición de elementos:

Dentro de cada bloque se pueden definir elementos locales dándoles el nombre que se considere más adecuado en cada caso. Los nombres locales no afecta al código fuera del bloque, ya que no son visibles. Incluso es posibles repetir el mismo nombre para elementos diferentes definidos en distintos bloques.

Al dar un nombre ya utilizado como global a un nuevo elemento local del bloque se está redefiniendo dicho nombre, y automáticamente se pierda la posibilidad de acceso al elemento global del mismo nombre. Se dice que el nombre local ocultar o hace sombra, shadow al nombre global. No se debe utilizar la redefinición de elementos.



Doble referencia:

Se produce doble referencia, aliasing, cuando una misma variable se referencia con os nombres distintos, cosa que puede ocurrir en la invocación de subprogramas con argumentos pasados por referencia. Fundamentalmente esto puedo ocurrir en dos situaciones muy concretas: cuando un subprograma utiliza una variable externa que también se le pasa como argumento y cuando para utilizar un subprograma se pasa la misma variable en dos o más argumentos. No se debe utilizar la doble referencia, salvo que el subprograma se diseñe pensando en esa posibilidad. Esto último deberá quedar claro en los comentarios del subprograma.

miércoles, 23 de noviembre de 2011

Programa calcular interés

Este programa es muy útil para todos aquell@s que tengáis hipoteca ya que nos permite calcular los intereses y el saldo que debemos amortizar para pagarla en un determinado tiempo. Nosotros tenemos que ponerle el tipo de interés y la cantidad de años que quedan y ya nos calcula cuanto tenemos que ir pagando. Programa muy útil, os dejo el código fuente para que lo podáis usar y una captura de pantalla de ejemplo para que lo veáis:

#include <math.h>
#include <stdio.h>
int main(){
  float parte_capital,parte_intereses,saldo_vivo;
  float anualidad;
  float interes;
  float capital;
  int annos;

  printf("\250Capital? ");
  scanf("%f",&capital);
  printf("\250Tipo de inter\202s (%%)? "); 

  scanf("%f",&interes);
  printf("\250A\244os? ");
  scanf("%d",&annos);

  if(capital>0&&interes>0&&annos>0){

   anualidad=capital*(pow(1+interes/100.0,annos)*interes/100.0)/(pow
   (1+interes/100.0,annos)-1.0);   
    printf("Anualidad: %10.02f\n",anualidad);
    printf("A\244o   Intereses   Amortizaci\242n   Saldo vivo\n");
    printf("-------------------------------------------\n");

    for(int i=1;i<=annos;i++){
      parte_intereses=saldo_vivo*interes/100;
      parte_capital=anualidad-parte_intereses;
      saldo_vivo=saldo_vivo-parte_capital;
      printf("%6d%12.02f%15.02f%10.02f\n",i,parte_intereses,parte_capital,saldo_vivo);
    }
  }
}
 
interes de hipoteca con programa
Calcular Hipoteca

Programa que lee longitud de tres lados de un triángulo

Con este programa podemos leer la longitud de los lados de un triángulo y analizarlos para saber que tipo de triángulo es, os dejo el código fuente y una captura de pantalla para que lo podáis ver en funcionamiento:


#include <stdio.h>
int main(){
  int primer_lado,segundo_lado,tercer_lado;
  int auxiliar;

  printf("\250Primer lado? ");
  scanf("%d",&primer_lado);
  printf("\250Segundo lado? ");
  scanf("%d",&segundo_lado);
  printf("\250Tercer lado? ");
  scanf("%d",&tercer_lado);

  if(primer_lado>0&&segundo_lado>0&&tercer_lado>0){

    if(primer_lado>segundo_lado){
      auxiliar=segundo_lado;
      segundo_lado=primer_lado;
      primer_lado=auxiliar;
    }
    if(primer_lado>tercer_lado){
      auxiliar=tercer_lado;
      tercer_lado=segundo_lado;
      segundo_lado=primer_lado;
      primer_lado=auxiliar;
    }
    else if(segundo_lado>tercer_lado){
      auxiliar=tercer_lado;
      tercer_lado=segundo_lado;
      segundo_lado=auxiliar;
    }

    if(primer_lado+segundo_lado<tercer_lado){
      printf("Los lados no forman tri\240ngulo\n");
    }
    else if(primer_lado==segundo_lado&&segundo_lado==tercer_lado){
      printf("Los lados forman un tri\240ngulo equil\240tero\n");
    }
    else{
      if(primer_lado==segundo_lado||segundo_lado==tercer_lado){
        printf("Los lados forman un tri\240ngulo is\242sceles\n");
      }
      else{
        printf("Los lados forman un tri\240ngulo escaleno\n");
      }
      if(primer_lado*primer_lado+segundo_lado*segundo_lado==tercer_lado*tercer_lado){
        printf("Adem\240s, el tri\240ngulo es rect\240ngulo\n");
      }
    }
  }
}
código de programa para comprobar que tipo de triángulo es
Programa que comprueba que tipo de triángulo es

martes, 22 de noviembre de 2011

Programa Calcular número e

Aquí tenéis un programa que calcula el número e mediante el desarrollo en serie con un error menor del introducido como dato. Tenéis el código fuente y una captura de pantalla para que podáis ver que resultado da por pantalla:


#include <stdio.h>
int main(){
  const float e_referencia=2.7182818284590452353602874713527;
  const float error_tolerado_maximo=0.1;
  float error_tolerado;
  float numero_e;
  int iteracion;
  int factorial;
  float error;


  printf("\250Error tolerado? ");
  scanf("%f",&error_tolerado);


  if(error_tolerado>=0&&error_tolerado<=error_tolerado_maximo){


    numero_e=1;
    error=e_referencia-numero_e;
    iteracion=1;


    while(error>error_tolerado){


      factorial=1;
      for(int i=2;i<=iteracion;i++){
        factorial=factorial*i;
      }


      numero_e=numero_e+1/float(factorial);


      error=e_referencia-numero_e;


      iteracion++;
    }


    printf("Valor de e calculado: %10.8f tras %d iteraciones\n",numero_e,iteracion);
  }


}

calcular número e
Programa que calcula el número e

lunes, 21 de noviembre de 2011

Programa Construir Rombo de Asteriscos

Este es un programa para construir un rombo de asteriscos, dándole un número nos crea un rombo de asteriscos de ese tamaño. Os dejo el código fuente completo y una captura de pantalla para que veáis cómo se hace y el resultado final:


#include <stdio.h>
int main(){
  const int lado_maximo=50;
  const int lado_minimo=1;

  int lado;
 
  printf("\250Lado? ");
  scanf("%d",&lado);

 
  if(lado>=lado_minimo&&lado<=lado_maximo){

   
    for(int i=1;i<=lado;i++){

     
      for(int j=0;j<lado-i;j++){
        printf(" ");
      }

     
      for(int j=0;j<i;j++){

        printf("*");
        if(j+1<i){
          printf(" ");
        }
      }

     
      printf("\n");

    }
   
    for(int i=lado-1;i>=1;i--){

     
      for(int j=0;j<lado-i;j++){
        printf(" ");
      }

     
      for(int j=0;j<i;j++){

        printf("*");
        if(j+1<i){
          printf(" ");
        }
      }

    
      printf("\n");

    }
  }


}


programa rombo asteriscos
Captura de pantalla de programa que crea un rombo de asteriscos

Máximo común divisor de 2 números

Con este programa podemos calcular el MCD de 2 números, el programa nos pide 2 números y nos calcula el máximo común divisor de ellos, su estructura es muy sencilla y con el código fuente podéis entender muy bien cómo lo hace:

#include <stdio.h>
int main(){
  int primer_numero,segundo_numero;
  int auxiliar,dividendo,divisor;
  int maximo_comun_divisor;

  printf("\250Primer n\243mero? ");
  scanf("%d",&primer_numero);
  printf("\250Segundo n\243mero? ");
  scanf("%d",&segundo_numero);


  if(primer_numero>=segundo_numero){
    dividendo=primer_numero;
    divisor=segundo_numero;

  }
  else{
    dividendo=segundo_numero;
    divisor=primer_numero;

  }

  while(dividendo%divisor!=0){

    auxiliar=divisor;
    divisor=dividendo%divisor;
    dividendo=auxiliar;

  }

  maximo_comun_divisor=divisor;

  printf("El m\240ximo com\243n divisor de %d y %d es %d\n",primer_numero,segundo_numero,maximo_comun_divisor);

}

Programa para calcular el máximo común divisor de 2 números
Programa para calcular el Máximo Común Divisor

domingo, 20 de noviembre de 2011

Tabla de Multiplicar en C+-

Hola, aquí os traigo el código completo de una tabla de multiplicar en C+-, es muy fácil de implementar y consiste en un programa que nos pide un número y que en base a el nos crea su tabla de multiplicar.


#include <stdio.h>

int main(){
 
     int numero;

     printf("\250N\243mero? ");
     scanf("%d",&numero);

     printf("Tabla de multiplicar por %d\n",numero);
     printf("---------------------------\n");
 
     for(int i=1;i<=10;i++){
         printf("%6d x %2d = %7d\n",numero,i,numero*i);
      }

 }



Una captura de pantalla del resultado final:

programar tabla de multiplicar en C
Tabla de multiplicar en C + -

sábado, 19 de noviembre de 2011

Desarrollo de programas 2/3

Seguimos con más normas básicas que tenemos que es importante utilizar y tener en cuenta a la hora de desarrollar nuestros programas (Desarrollo de programas 1/3):

Desarrollo con esquemas de selección e iteración:

Esquema de selección:      Un esquema de selección consiste en plantear una acción compuesta como la realización de una acción entre varias posibles, dependiendo de ciertas condiciones.


Esquema de iteración:       Una iteración o bucle consiste en la repetición de una acción o grupo de acciones hasta conseguir el resultado deseado.


Verificación de programas:


Uno de los objetivos de la programación es la corrección. Un programa es correcto si produce siempre resultados de acuerdo con la especificación del programa. En la práctica, la verificación de un programa se hace muchas veces mediante ensayos. Un ensayo, (testing) consiste en ejecutar el programa con unos datos preparados de antemano y para los cuales se sabe cuál ha de ser el resultado a obtener. Este proceso se llama depuración (debugging).
 
          Corrección parcial y total:

          Corrección parcial: si el programa termina el resultado es correcto.
Corrección total: lo anterior y además para todo dato de entrada válido el programa termina.


Eficiencia de programas. Complejidad:

          Medidas de eficiencia:
          La eficiencia de un programa se define en función de la cantidad de recursos
          que consume durante su ejecución.
          Las principales medidas de recursos empleados son:

                    El tiempo que tarda en ejecutarse un programa.

                    La cantidad de memoria usada para almacenar datos.


          Análisis de programas:

La determinación de la eficiencia (o complejidad) de un programa se hace analizando los siguientes elementos:

Cuánto tarda en ejecutarse cada instrucción básica del lenguaje utilizado.

Cuántas instrucciones de cada clase se realizan durante una ejecución del programa.


          Crecimiento asintótico:

En los análisis de eficiencia (o complejidad) se considera muy importante la manera como la función de complejidad va aumentando con el tamño del problema. Lo que interesa es la forma de crecimiento del tiempo de ejecución, y no tanto el tiempo particular empleado. La complejidad constante sería la ideal y la complejidad exponencial resultaría muy poco eficiente.

jueves, 17 de noviembre de 2011

Estructuras básicas de la programación imperativa

Estas son las estructuras básicas que podemos encontrar en el lenguaje de programación C+-:

Programación estructurada:

La programación estructurada es una metodología de programación que trata de construir programas que sean fácilmente comprensibles.



La estructura de los programas imperativos se representa mediante diagramas de flujo llamados en inglés flow-chart.



Secuencia:

La estructura más sencilla para emplear en la descomposición es utilizar una secuencia de acciones o partes que se ejecutan de forma sucesiva.



Selección:

La estructura de selección consiste en ejecutar una acción u otra, dependiendo de una determinada condición que se analiza a la entrada de la estructura.



Iteración:

La iteración es la repetición de una acción mientras se cumpla una condición.



Estructuras anidadas:

Cualquier parte o acción puede estar constituida por cualquiera de las estructuras descritas, por tanto el anidamiento puede ser tan complejo como sea necesario.



Expresiones condicionales (operador en C+-):

          Mayor que                        >

          Mayor o igual que             >=

          Menor que                        <

          Menor o igual que             <=

          Igual a                              ==

          Diferente                           !=

          Conjunción                       &&

          Disyunción                        ||

          Negación                          !



Estructuras básicas en C+-:



          Secuencia:

                    Se escriben las acciones una tras otra.

                    Ejemplo:

                              printf(“hola:\n”);

                              printf(“estoy aquí”);



          Sentencia if, estructura de selección:

          if(condición) {

                    Acción A

          }else{

                    Acción B

          }

          Ejemplo:

          if(edad >= 18) {

                    printf(“es mayor de edad”);

          }





Sentencia while:

while (condición) {

          Acción

}

Ejemplo:

while(publico==aforo){

          printf(“no hay entradas disponibles”);

}





Sentencia for:

for (int índice = inicial; índice <=final; índice ++) {

          Acción

}

Ejemplo:

for (int j=1;j<=numeroBlancos;j++) {

printf(" ");

}