Resumen Programación todo

27
FUNDAMENTOS DE PROGRAMACIÓN RAFAEL LÓPEZ NAVARRO CURSO 2011/2012 UNED

description

                                           

Transcript of Resumen Programación todo

 

 

 

 

 

 

FUNDAMENTOS  DE  PROGRAMACIÓN  RAFAEL  LÓPEZ  NAVARRO  

CURSO  2011/2012    UNED  

 

                               

TEMA  1  -­‐.-­‐  INTRODUCCIÓN  1.1-­‐  Programación  e  ingeniería  del  software    ¿Qué  es  un  algoritmo?  Conjunto  de  pasos  necesarios  para  resolver  un  determinado  problema.  Los  algoritmos  deben  ser:  -­‐  Correctas  -­‐  Claros  -­‐  Eficientes  ¿Qué  es  un  programa?  Es  la  implementación  o  codificación  de  un  determinado  algoritmo  en  uso  de  algún  lenguaje  de  programación.  Lenguaje  de  programación:  Conjunto  de  símbolos  y  reglas  para  crear  programas.  ¿Programación  e  ingeniería  del  software?  Programación  =  En  general  se  encarga  de  desarrollar  programas  a  pequeña  escala.  Ingeniería  del  software  =  Conjunto  de  etapas  necesarias  para  desarrollar  programas  a  gran  escala  y  de  gran  calidad.  Etapas  de  la  ingeniería  del  software:  Análisis  Diseño  Codificación  Pruebas  y  Mantenimiento    -­‐  Compiladores  y  interpretes  ¿Qué  es  un  traductor?  Es  un  programa  que  traduce  el  código  fuente  de  un  programa  a  un  código  que  pueda  entender  la  CPU.  Hay  dos  tipos  de  traductores:    

• Compiladores  • Intérpretes  

 -­‐  Modelos  abstractos  de  programación  Actualmente  existen  diferentes  modelos  de  programación  (o  paradigmas  de    programación):       -­‐  Programación  funcional         -­‐  Programación  lógica         -­‐  Programación  imperativa         -­‐  Modelo  de  flujo  de  datos         -­‐  Programación  orientada  a  objetos  (POO)    -­‐Objetivos  de  la  programación    

• CORRECIÓN:  Un  programa  debe  realizar  el  tratamiento  esperado,  y  no  producir  resultados  erróneos.  

• CLARIDAD:  Prácticamente  todos  los  programas  han  de  ser  modificados  después  de  haber  sido  desarrollados  inicialmente.  Es  fundamental  que  sus  descripciones  sean  claras  y  fácilmente  inteligibles  por  otras  personas.  

• EFICIENCIA:  Una  tarea  de  tratamiento  de  información  puede  ser  programada  de  muy  diferentes  maneras  sobre  un  computador  determinado,  es  decir,  habrá  muchos  programas  distintos  que  producirán  los  resultados  deseados.  

TEMA  2  -­‐.-­‐  ELEMENTOS_BASICOS  2.1  -­‐  Notación  BNF  (backups-­‐Naur  Form)  Es  el  conjunto  de  reglas  gramaticales  en  las  que  se  basa  un  lenguaje  de  programación.  Para  describir  estas  reglas  se  utilizan  una  seria  de  meta  símbolos:  

::=  Meta  símbolo  de  definición  |  Meta  símbolo  de  alternativa  {  }  Meta  símbolo  de  repetición  [  ]  Meta  símbolo  de  opción  

(  )  Meta  símbolo  de  agrupación      

Las  reglas  BNF  tendrán  dos  tipos  de  elementos:  • Elemento  terminal:  Son  elementos  que  forman  parte  del  lenguaje  C+-­‐  • Elemento  no  terminal:  Están  definidos  a  partir  de  los  elementos  terminales  a  partir  de  

las  reglas  de  producción.    

 2.2  Tipos  predefinidos  • Tipo  entero  (int)  Este  tipo  puede  tomar  valores  positivos  y  negativos.  El  rango  de  

valores  que      puede  tomar  depende  de  la  plataforma  (combinación  de  procesador,  sistema  operativo  y  compilador)  utilizada.  Para  poder  controlar  los  valores  máximo  y  mínimo  que  podemos  utilizar  no  es  preciso  recordar  estos  valores,  podemos  utilizar  las  constantes  INT_MAX  y  INT_MIN  que  están  en  la  librería  <limits.h>  

 • Tipo  real  (float)  

Este  tipo  puede  tomar  valores  positivos  y  negativos,  y  puede  tener  una  parte  decimal.  Es  una  representación  no  exacta.  El  rango  de  valores  que  puede  tomar  depende  de  la  plataforma  (combinación  de  procesador,  sistema  operativo  y  compilador)  utilizada.  

 • Tipo  carácter  (char)  

Lo  utilizaremos  para  manejar  caracteres  imprimibles:  "A",  "a",  "B",  "b",  ...  ."@","{",  ....  y  no  imprimibles:  RETURN,  ESCAPE,  F1,  F2,  ....  Incluye  la  práctica  totalidad  de  caracteres  utilizados  en  el  Mundo  Para  hacer  referencia  a  un  carácter  de  la  mesa  del  código  ascii  podemos  hacer  uso  de  la  función  char(x):  char(13)  à  Tecla  Return   char(27)  à  tecla  ESCAPE   char(65)  à  letra  "A"  También  podemos  hacer  referencia  al  lugar  de  la  tabla  que  ocupa  un  carácter  en  uso  de  la  función  int(c):    

      int  ("Z")à90      

Si  hacemos  uso  de  la  librería  <ctype.h>  podemos  utilizar  las  siguientes  funciones  para  a  manejar  caracteres:  -­‐isalpha(c)àComprueba  si  c  es  una  letra  o  no  isascii(c)àComprueba  si  c  es  un  carácter  ASCII  -­‐isblank(c)àComprueba  si  c  es  un  espacio  en  blanco  o  tabulación  iscntrl(c)àComprueba  si  c  es  un  carácter  de  control  o  no  isdigit(c)àComprueba  si  c  es  un  dígito  decimal  (0  ..  9)  o  no  -­‐-­‐-­‐-­‐tolower(c)àPasa  a  minúscula  el  carácter  c  -­‐toupper(c)àPasa  a  mayúscula  el  carácter  c    

2.3  Expresiones  aritméticas  Si  no  se  utilizan  paréntesis  el  orden  de  prioridad  en  una  expresión  es:    

1.  Operadores  multiplicativos  *  /  %  2.  Operadores  aditivos  +  -­‐  

 

2.4  Operaciones  de  escritura  simples    El  procedimiento  printf  está  en  la  librería  <stdio.h>  y  sirve  para  mostrar  o  escribir  por  pantalla  un  valor:       Ejemplos:     printf  ("Introduce  tu  nombre:");  printf  ("-­‐-­‐-­‐-­‐  Bienvenido  a  este  programa  -­‐-­‐-­‐  \  n  ");    Cuando  a  parte  de  texto  queremos  mostrar  valores  numéricos,  la  función  printf  necesita  saber  el  tipo  de  datos  que  tiene  que  escribir  para  saber  cómo  las  ha  de  escribir.  El  formato  o  forma  de  utilizar  esta  función  es:       printf  ("cadena  con  formatos",  valor1,  valor3,  ...  valorN);      Nota:  no  es  obligatorio  el  signo  de  %  de  principio  del  especificador,  y  el  tipo  de  datos  que  se  quiere  mostrar.  Esto  es  lo  más  normal  y  lo  que  se  ve  en  esta  asignatura.  En  caso  de  que  se  quiera  especificar  más  los  datos  de  salida  se  utilizan  los  demás  parámetros,  que  están  entre  "corchetes".    Los  tipos  habituales:  

Una  cadena  de  caracteres:  s    Un  carácter:  c  Valor  real  con  /  o  sin  notación  exponencial,  con  un  número  dado  de  decimales  de  precisión  .Los  ceros  y  puntos  decimal  salen  si  es  necesario:  g  Valor  real  con  notación  exponencial  e  [+/-­‐]  ddd.  e    Valor  real  (punto  fijo)  de  la  forma  [-­‐]  dddd.dddd.  f    Entero  decimal  (un  entero):  d    Nota:  %10.3f  real  en  coma  fija  que  reserva  10  espacios  y  una  precisión  de  3  

decimales    

2.5  -­‐  ESTRUCTURA  DE  UN  PROGRAMA  EN  C  /  C  +  -­‐  CABECERA  DEL  PROGRAMA     /  *  Nombre  del  programa.  Autor  *  /  /  *  Descripción  del  programa  *  /  LLAMADAS  EN  LIBRERÍAS  Y     #include  <nombre_libreria.h>  DEFINICIONES  DE  CONSTANTES     const  tipo  nombre_constante  =  valor  DECLARACIÓN  DE  FUNCIONES  tipo_dato  función  A  (parámetros)    tipo_dato  función  B  (parámetros)  DECLARACIÓN  DE  VAIABLES  GLOBALES  int  a;  float  b;  FUNCIÓN  PRINCIPAL  tipo_dato  main  ()  {  instrucciones  }    

TEMA  3  -­‐.-­‐  CONSTANTES  Y  VARIABLES  3.1-­‐  IDENTIFICADORES:  

Cualquier  dato  que  se  utiliza  en  un  programa  tiene  asociada  los  siguientes  atributos:  

Identificador  Tipo  Valor    Reglas  para  identificador:    -­‐Palabra  formada  por  caracteres  alfabéticos  o  numéricos    -­‐No  valen  los  espacios  en  blanco  ni  signos  de  puntuación    -­‐Es  necesario  que  comience  por  una  letra    -­‐Pueden  utilizarse  las  52  letras  mayúsculas  y  minúsculas  del  alfabeto  inglés,  la  barra  de  subrayado  y  los  dígitos  de  0  a  9.    

3.2  -­‐  CONSTANTES:  Son  datos  que  mantienen  su  valor  durante  toda  la  ejecución  del  programa.  La  forma  de  declarar  una  constante  es:         Const  tip  nombre  =valor;  

 

3.3-­‐  VARIABLES  Son  datos  que  pueden  ir  variando  su  valor  durante  la  ejecución  del  programa.  La  forma  de  declarar  la  variable  es:         tipoDeVariable  nombreVariable;    

 

3.4-­‐  LECTURA  DE  VARIABLES  (SCANF)  Para  leer  variables  desde  el  teclado  hacemos  uso  de  la  función  scanf  que  está  en  la    librería  <stdio.h>  

Scanf  (“%d”,&a);  à  lee  un  valor  entero  por  teclado,  el  valor  se  asigna  a  la  variable  int  a;    

TEMA  4  -­‐.-­‐  METODOLOGÍA  1  4.1  –  DESCOMPOSICIÓN  EN  SUBPROBLEMAS(REFINAMIENTOS  SUCESIVOS)  

A  la  hora  de  resolver  problemas  complejos  lo  mejor  es  intentar  descomponer  el  problema  en  subproblemas  más  pequeños  y  más  fáciles  de  resolver.    Consiste  en  expresar  inicialmente  el  programa  a  desarrollar  como  una  acción  global,  que  si  es  necesario  se  irá  descomponiendo  en  acciones  más  sencillas  hasta  llegar  a  acciones  simples  que  puedan  ser  expresadas  directamente  como  sentencias  del  lenguaje  de  programación.  Cada  paso  de  refinamiento  consiste  en  descomponer  cada  acción  compleja  en  otras  más  simples.  Esta  descomposición  exige:  

o Identificar  las  acciones  componentes.  o Identificar  la  manera  de  combinar  las  acciones  componentes  para  conseguir  el  

efecto  global.  

4.2  –  ASPECTOS  DE  ESTILO  • Encolumnado  • Comentarios  • Elección  de  nombres  • Uso  correcto  de  mayúsculas  y  minúsculas  • Constantes  con  nombre.  

TEMA  5  -­‐.-­‐  ESTRUCTURAS  DE  CONTROL  5.1  –  Programación  estructurada  

Es  una  metodología  de  programación  que  fundamentalmente  trata  de  construir  programas  que  sean  fácilmente  comprensibles.  Un  programa  no  solo  debe  funcionar  correctamente,  sino  que  ademças  debe  estar  escrito  de  manera  que  se  facilite  su  compresión  posterior.      -­‐Representación  de  la  estructura  de  un  programa       La  estructura  de  los  programas  imperativos  se  representa  tradicionalmente  mediante  diagramas  de  flujo  llamados  en  ingles  flow-­‐chart.  Estos  diagramas  contienen  dos  elementos  básicos,  correspondientes  a  acciones  y  condiciones.  Las  acciones  son  rectángulos  y  las  condiciones  son  rombos.  Las  condiciones  equivalen  a  preguntas  a  las  que  se  puede  responder  “SI”  o  “NO”.              El  flujo  de  control  durante  la  ejecución  del  programa  se  refleja  mediante  líneas  o  vías  que  van  de  un  elemento  a  otro.  Las  acciones  tienen  una  sola  vía  de  entrada  o  comienzo  y  una  de  terminación  o  salida.  Las  condiciones  tienen  una  vía  de  entrada,  y  dos  vías  de  salida  marcadas  son  “SI”  y  “NO”.      La  programación  estructurada  recomienda  descomponer  las  acciones  usando  las  estructuras  más  sencillas  posibles,  que  son:  

• Secuencia:  secuencia  de  acciones.  • Selección:  consiste  en  ejecutar  una  acción  u  otra.  • Iteración:  repetición  de  una  acción  mientras  que  se  cumpla  una  determinada  

condición.  (Bucle)    

5.2  -­‐  ESTRUCTURAS  BÁSICAS  EN  C+-­‐  • Sentencia  IF  • Sentencia  WHILE  • Sentencia  FOR  

 

TEMA  6  -­‐.-­‐  METODOLOGÍA  2  6.1  –  DESARROLLO  CON  ESQUEMAS  DE  SELECCIÓN  E  ITERACIÓN  

Se  tiente  3  posibilidades  a  la  hora  de  refinar  una  acción  compuesta:  • Organizarla  como  secuencia  de  acciones  • Organizarla  como  selección  entre  acciones  alternativas.  • Organizarla  como  iteración  de  acciones.  

     

Acción          SI   NO  

1.1 ESQUEMAS  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.    Para  desarrollar  un  esquema  de  selección  debemos  identificar  sus  elementos  componentes.  Por  tanto  habrá  que:  

• Identificar  cada  una  de  las  alternativas  del  esquema,  y  las  acciones  correspondientes.  

• Identificar  las  condiciones  para  seleccionar  una  alternativa  u  otra.    

1.2 ESQUEMAS  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.    

• Identificar  las  acciones  útiles  a  repetir,  y  las  variables  necesarias.  Precisar  el  significado  de  estas  variables  al  comienzo  y  final  de  cada  repetición.  

• Identificar  cómo  actualizar  la  información  al  pasar  de  cada  iteración  a  la  siguiente.  • Identificar  la  condición  de  terminación.    • Identificar  los  valores  iniciales  de  las  variables,  y  si  es  necesaria  alguna  acción  

para  asignárselos  antes  de  entrar  en  el  bucle.  

 6.2–  VERIFICACIÓN  DE  PROGRAMAS  Un  programa  es  correcto  si  produce  siempre  los  resultados  esperados  de  acuerdo  con  la  especificación  del  programa.  Una  forma  de  verificar  un  programa  es  mediante  ensayos.  Consiste  en  ejecutar  el  programa  con  unos  datos  preparados  de  antemano  y  a  los  que  sabemos  cuál  será  el  resultado  correcto.  Si  con  la  ejecución  del  programa  con  estos  datos  se  obtienen  los  resultados  esperados  no  podemos  afirmar  que  el  programa  sea  correcto  ya  que  puede  tener  un  caso  concreto  y  no  probado  que  dé  resultados  inesperados.  La  única  forma  de  verificar  un  programa  es  con  una  demostración  formal  utilizando  la  lógica  de  predicados.    

6.3-­‐  EFICIENCIA  DE  PROGRAMAS.  COMPLEJIDAD.  El  coste  que  tiene  un  programa  va  a  estar  en  función  de  los  recursos  que  consume.  Recursos  son  la  memoria,  el  tiempo  de  procesador  y  el  tiempo  que  tarda  en  ejecutarse.  Nosotros  sólo  nos  fijaremos  en  el  tiempo  que  tarda  un  algoritmo  en  ejecutarse,  que  vendrá  en  función  del  número  de  instrucciones  que  ejecuta.        

TEMA  7  -­‐.-­‐  FUNCIONES  Y  PROCEDIMIENTOS  7.1  CONCEPTO  DE  SUBPROGRAMA  

Es  una  parte  de  un  programa.  Como  mecanismo  de  programación,  un  subprograma  es  una  parte  de  un  programa  que  se  desarrolla  por  separado  y  se  utiliza  invocándolo  mediante  un  nombre  simbólico.  

7.2  FUNCIONES  Una  función  permite  agrupar  un  conjunto  de  instrucciones  en  un  bloque  que  típicamente  realizará  una  tarea  concreta.  Su  estructura  en  C  tiene  dos  partes:  

• El  encabezamiento  de  la  función  • El  cuerpo  de  la  función  

 

//encabezamiento  de  la  función  (descripción)    tipo_resultado  nombre_funcion  (lista_argumentos  o  parámetros)  {  declaración  de  variables;  instrucciones….  }    Al  encabezamiento  de  una  función  se  define  el  nombre  de  la  función  y  el  modo  en  el  que  se  le  va  a  transferir  información.  Tipo_resultado  representa  el  tipo  de  dato  (void,  int,  char,  float…)  que  devuelve  la  función  al  módulo  que  le  ha  llamado.  Nombre_funcion  Hace  referencia  al  nombre  que  utilizaremos  para  llamar  a  la  función  desde  otro  mòdulo.  Lista_argumentos  Es  un  listado  de  variables  o  de  valores  que  el  modulo  que  llama  le  pasa  a  la  función  para  que  realice  operaciones  con  ellos.    Algunas  funciones  predefinidas    Librería  ctype.h    char  toupper  (char  c)àConvierte  es  mayúscula    char  tolower  (char  c)àConvierte  es  minúscula    bool  isblank  (char  c)àComprueba  si  c  es  un  espacio  en  blanco  o  tabulación    bool  isdigit  (char  c)àComprueba  si  c  es  un  número  entre  0  y  9    bool  isalpha  (char  c)àComprueba  si  c  es  un  carácter  o  no  Librería  math.h  float  Tanf  (float  x)àTangente  (x)    float  cosf  (float  x)àCoseno  (x)    float  senf  (float  x)àSeno  (x)    float  powf  (float  x,  float  y)àPotencia  x  ^  float  sqrtf  (float  x)àRaíz  cuadrada  de  x    

7.3  –  PROCEDIMIENTOS  Son  subprogramas  que  realizan  una  cierta  tarea  o  acción.  No  devuelven  ningún  valor.  

7.4  –  PASO  DE  ARGUMENTOS  O  PARAMETROS  A  FUNCIONES  Y  PROCEDIMIENTOS  Cuando  una  función  llama    a  otra  función  para  que  se  ejecute  hay  un  intercambio  de  información  entre  las  dos  funciones.  Este  intercambio  de  información  se  lleva  a  cabo  mediante  el  uso  de  parámetros  o  argumentos.  Se  pueden  clasificar  en:  

• Parámetros  actuales:  son  variables  locales  que  pertenecen  a  la  función  que  realiza  la  llamada  y  el  valor  (contenido)  o  la  dirección  de  memoria  de  esta  variable  es  enviada  a  la  función  invocada.  

• Parámetros  formales:  son  variables  locales,  que  pertenecen  al  módulo  invocado,  que  reciben  el  valor  o  la  dirección  de  memoria  de  los  parámetros  actuales  del  módulo  que  lo  invoca  en  el  momento  de  ser  ejecutada  la  llamada.  

Tenemos  dos  formas  de  pasarle  los  argumentos  a  una  función  o  un  procedimiento  • Por  valor:  Se  envía  una  copia  del  valor  que  tienen  cada  uno  de  los  parámetros  

actuales  en  el  momento  de  realizar  la  llamada.  Esta  valoración  son  recogidos  por  los  parámetros  formales  del  módulo  invocado.  En  este  caso  el  módulo  invocado  no  puede  modificar  el  valor  original  de  las  variables  actuales.  

• Por  referencia:  en  este  caso,  el  módulo  invocado,  en  lugar  de  trabajar  con  los  valores  de  las  variables  actuales,  trabaja  sobre  las  direcciones  de  las  variables  locales.  Por  tanto  en  este  caso  el  módulo  invocado  puede  modificar  los  valores  de  las  variables  actuales  del  módulo  que  hace  la  llamada.  Este  uso  ofrece  menos  seguridad  que  el  paso  por  valor.  

7.5  –  VARIABLES  LOCALES  Y  GLOBALES  Variable  local:  Es  aquella  que  su  uso  está  restringido  a  la  función  que  la  ha  declarado,  se  dice  entonces  que  la  variable  es  local  a  esa  función.  Esto  implica  que  esa  variable  sólo  va  a  poder  ser  manipulada  en  esa  sección,  y  no  se  podrá  hacer  referencia  fuera  de  esa  sección.  Cualquier  variable  que  se  define  dentro  de  las  claves  del  cuerpo  de  una  función  se  interpreta  como  una  variable  local  en  esa  función.  Variable  global:  es  aquella  que  se  define  fuera  del  cuerpo  de  cualquier  función,  normalmente  al  principio  del  programa,  tras  la  definición  de  los  archivos  de  biblioteca,  de  la  definición  de  constantes  y  antes  de  cualquier  función.  El  ámbito  de  una  variable  global  son  todas  las  funciones  que  componen  el  programa,  cualquier  función  puede  acceder  a  esas  variables  para  leer  y  escribir  en  ellas.    

7.6  –  RECURSIVIDAD  DE  SUBPROGRAMAS  Cuando  un  subprograma  hace  una  llamada  a  sí  mismo  se  dice  que  es  un  subprograma  recursivo.    (algoritmo  del  factorial)  Int  FactorialRecursivo  (int  n)  {      If  (n<=1){          Return  1;          }  else  {                          return  n  *  FactorialRecursivo(n-­‐1);                        }      }    

7.7  Problemas  al  utilizar  subprogramas  El  uso  inadecuado  de  las  variables  que  pasamos  como  argumentos  puede  dar  lugar  a  algunos  problemas:  

• Efectos  secundarios.  Cuando  un  subprograma  modifica  alguna  variable  externa,  se  dice  que  está  produciendo  efectos  secundarios  o  laterales.  El  concepto  al  efecto  lateral  sería  la  transparencia  referencial.  Siempre  que  llamamos  a  un  subprograma  con  los  mismo  parámetros  se  debe  obtener  el  mismo  resultado.  

• Redefinición  de  elementos.  Cuando  a  un  subprograma  se  define  una  variable  local  da  igual  el  nombre  que  elegimos,  ya  que  al  ser  local  sólo  afecta  al  propio  subprograma.  El  problema  viene  cuando  el  nombre  de  la  variable  local  que  elegimos  es  lo  mismo  que  una  variable  global.  Aparte  de  sacar  resultados  incorrectos,  se  pierde  mucho  en  claridad.  

• Doble  referencia(aliasing)  Se  produce  cuando  una  misma  variable  se  referencia  con  dos  nombres  diferentes.  Se  puede  dar  los  siguientes  casos:  1. Cuando  un  subprograma  utiliza  una  variable  externa  que  también  se  le  pasa  

como  argumento.  2. Cuando  para  utilizar  un  subprograma  se  pasa  la  misma  variable  en  dos  o  más  

argumentos.    

TEMA  8  -­‐.-­‐  METODOLOGÍA  3  8.1-­‐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  en  lo  posible  de  los  detalles.  Las  entidades  que  podemos  abstraer  para  materializarlas  como  subprogramas  osn,  en  genral,  oeraciones  (  acción  o  función).  Especificación:  Que  hace  la  operación  (  punto  de  vista  de  quien  la  invoca)  Realización:  Cómo  se  hace  la  operación  (punto  de  vista  de  quien  la  ejecuta).  La  especificación  consiste  en  indicar  cuál  es  el  nombre  de  la  operación  y  cuales  son  sus  argumentos.  La  realización  debe  suministrar  toda  la  información  necesaria  para  poder  ejecutar  la  operación  (código).      

8.2-­‐DESARROLLO  USANDO  ABSTRACCIONES    Desarrollo  descendente:      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  alterativas  siguientes:  

• Considerar  la  operación  como  operación  terminal,  y  codificarla  mediante  sentencias  del  lenguaje  de  programación  

• Considerar  la  operación  como  operación  compleja,  y  descomponerla  en  otras  más  sencillas.  

• Considerar  la  operación  como  operación  abstracta,  y  especificarla,  escribiendo  más  adelante  el  subprograma  que  la  realiza.  

Resultará  ventajoso  definir  una  operación  como  abstracta  si  se  consigue  alguna  de  las  ventajas  siguientes:  

• Evitar  mezclar  en  un  determinado  fragmento  de  programa  operaciones  con  un  nivel  de  detalle  muy  diferente.  

• Evitar  escribir  repetidamente  fragmentos  de  código  que  realicen  operaciones  análogas.  

 Reutilización:  Si  la  operación  identificada  como  operación  abstracta  tiene  un  cierto  sentido  en  sí  misma,  es  muy  posible  que  resulte  de  utilidad  en  otros  programas,  además  de  en  aquél  para  el  cual  se  ha  desarrollado.  La  escritura  de  otros  programas  que  se  utilicen  esa  misma  operación  resulta  mas  sencilla,  ya  que  se  aprovecha  el  código  de  su  definición,  que  ya  estaba  escrito.  Los  procedimientos  y  funciones  cuanto  mas  genéricos  sean  en  su  comportamiento,  más  reutilizables  son.  Un  procedimiento  o  función  es  reutilizable  si  podemos  aplicarlo  no  sólo  al  programa  para  el  que  está  diseñado,  sino  que  también  a  otros  programas  en  los  que  se  requiere  un  procedimiento  o  función  similar.    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.    

8.3  –  PROGRAMAS  ROBUSTOS  Cualquier  programa  deber  ser:  

o Correcto:  Los  resultados  que  dé  son  los  esperados.  o Claro:  Cualquier  programador  pueda  entender  como  está  estructurado.  o Eficiente:  Que  consume  los  mínimos  recursos  en  cuanto  a  tiempo  y  memoria.  Se  dice  

que  un  programa  es  robusto  si  controla  sus  operaciones  aunque  le  introducimos  datos  incorrectos  y  erróneos.  

-­‐Programación  a  la  defensiva:  Es  una  forma  de  diseño  defensivo  aplicada  al  diseño  de  programas  que  busca  garantizar  el  comportamiento  de  todo  elemento  de  una  aplicación  ante  cualquier  situación  de  uso  por  incorrecta  o  imprevisible  que  ésta  pueda  parecer.  En  general,  esto  supone  multiplicar  las  comprobaciones  que  hacen  en  todos  los  módulos  programados,  con  la  consiguiente  penalización  en  carga  de  procesador,  tiempo  y  aumento  de  la  complejidad  del  código.  Las  técnicas  de  programación  defensiva  se  utilizan  especialmente  en  componentes  críticos,  en  los  que  un  mal  funcionamiento,  ya  sea  por  descuido  o  por  un  ataque  malicioso,  podría  tener  consecuencias  graves.  La  programación  defensiva  es  un  enfoque  que  busca  mejorar  el  software  y  el  código  fuente,  en  términos  de:  

o Calidad,  reduciendo  el  número  de  fallos  de  software  y,  en  consecuencia,  problemas.  o Haciendo  el  código  fuente  comprensible  (el  código  fuente  debe  ser  legible  y  

comprensible,  a  prueba  de  una  auditoría  de  código.  o Hacer  que  el  software  se  comporte  de  una  manera  predecible  a  pesar  de  entradas  o  

acciones  de  usuario  inesperadas.  

8.3.1  Tratamiento  de  excepciones:  Ante  la  posibilidad  de  errores  con  los  datos  que  se  opera  a  un  programa,  hay  que  considerar  dos  actividades  diferentes:  

a) Detección  de  situación  de  error-­‐  b) Corrección  de  la  situación  de  error.  

En  C+-­‐  la  sentencia  throw  provoca  una  terminación  de  un  subprograma  para  excepción:  void  operacion  (argumentos)  {    ...  ....  }      accion1            if  (error1)  {                throw  excepcion1;  /  *  Finaliza  con  una  excepción  *  /            }  ...          accion2  if  (error2)  {          throw  excepcion2;  /  *  Finaliza  con  una  excepción  *  /  }  ...  }      

TEMA  9  -­‐.-­‐  DEFINICIÓN  DE  TIPOS  A  parte  de  los  tipos  de  datos  predefinidos  en  C+-­‐  (int,  char,  float..)  el  programador  también  puede  definirse  sus  propios  tipos  de  datos  con  el  uso  de  la  instrucción  typedef,  por  ejemplo    

• Typedef  int  TipoEdad;  • Typedef  char  TipoSexo;  • Typedef  float  TipoAltura;  

Una  vez  declarados  los  tipos  se  pueden  definir  y  declarar  variables  de  esos  tipos  que  acabamos  de  definir,  por  ejemplo  sería  valido:  

• TipoEdad  edad1,  edad2;  • TipoSexo  sexo;  • TipoAltura  altura;  

Y  podríamos  utilizar  estas  variables  como  las  variables  que  hemos  visto  hasta  ahora:  • Edad2=  edad1  +  10;  • Sexo  =  ‘H’;  • Altura  =  1,72;  

 

9.1  TIPO  ENUMERADO  Podemos  definir  una  lista  de  valores  con  los  tipos  predefinidos  y  con  los  tipos  que  el  programador  cree.  Esta  lista  tomar  valores  numéricos  automáticamente  empezando  por  el  valor  0  y  hasta  n-­‐1.  

• Typedef  enum  {Lunes,Martes,Miercoles,Jueves,Viernes,Sabado,Domingo};  Los  tipos  enumerados  se  consideran  tipos  ordinales  (al  igual  que  los  int  y  char),  ya  que  tienen  un  orden  establecido  de  antemano.  Como  los  tipos  enumerados  son  ordinales,  podemos  utilizarlos  en  comparaciones  como  por  ejemplo:    If  (  mes  >=  Julio)  {  }  Para  hacer  referencia  al  lugar  que  ocupa  un  elemento  en  la  lista  lo  hacemos  así:  Int  (abril)>3  Podemos  ir  pasando  de  un  elemento  a  otro  de  una  lista  enumerada  de  la  siguiente  manera:  Dia=  jueves;  Dia  =  tipodia(int(dia)+1);    Tipo  predefinido  bool  El  tipo  predefinido  bool  se  puede  considerar  como  un  tipo  enumerado  con  dos  valores:  Typedef  enum  bool  {false,true};  Se  cumple  que  false=0  y  true=1.    

9.2  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  sencillos.  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.    

9.3  TIPO  FORMACION  Y  SU  NECESIDAD  Estas  estructuras  se  denominan  genéricamente  formaciones  (en  ingles  array)  y  permiten  la  generalización  de  la  declaración,  referencia  y  manipulación  de  colecciones  de  datos  todos  del  mismo  tipo.      

9.4  TIPO  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:  Se  declara  de  la  siguiente  forma:    

typedef  TipoElemento  TipoVector{NumElemtos}  

donde  TypoVector  es  el  nombre  del  nuevo  tipo  de  vector  que  se  declara  y  NumElementos  es  un  valor  constante  que  indica  el  número  de  elementos  que  constituyen  el  vector.  El  tamaño  del  array  estará  comprendido  entre  0  y  NumElementos-­‐1.  TipoElemento  corresponde  al  tipo  de  dato  de  cada  uno  de  los  elementos  del  vector  y  puede  ser  cualquier  tipo  de  dato  predefinido  del  lenguaje  o  definido  por  el  programado.  INICIALIZACIÓN:  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  (,).       TipoAgenda  agendaUno  =  {  Lunes,  Martes,  Miércoles,  Jueves,  Viernes…..};  OPERACIONES  CON  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.  VectorUno[0];  //  frase[13];  Un  elemento  de  un  vector  puede  formar  parte  de  cualquier  expresión  con  constantes,  variables  u  otros  elementos.      

9.5  VECTOR  DE  CARACTERES  Las  cadenas  de  caracteres  son  en  realidad  vectores  de  caracteres.    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.    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  si  hace  que  toda  cadena  termine  con  carácter  nulo  ‘\0’  situado  al  final.     Typedef  char  Cadena20[21];    FUNCIONES:      

• Strcpy(  c1,c2  )  à  Copia  c2  en  c1  • Strcat  (  c1,c2  )  à  Concatena  c2  a  continuación  de  c1  • Strlen(  c1  )  à    Devuelve  la  longitud  de  c1  • Strcmp  (  c1,c2  )  à  Compara  c1  y  c2  

   

9.6  TIPO  TUPLA  Y  SU  NECESIDAD  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.  Por  ejemplo,  una  fecha  se  escribe  habitualmente  como  un  dato  compuesto  de  los  elementos,  día,  mes  y  año.  Tupla:  Colección  de  elementos  componentes,  de  diferentes  tipos,  cada  uno  de  los  cuales  se  identifica  por  un  nombre.    Un  aspecto  importante  del  empleo  de  datos  estructurados  corresponde  al    punto  de  vista  de  abstracción.    Una  tupla,  como  cualquier  otro  dato  compuesto,  puede  verse  de  forma  abstracta  como  un  todo,  prescindiendo  del  detalle  de  sus  componentes.  La  posibilidad  de  hacer  referencia  a  toda  la  colección  de  elementos  mediante  un  nombre  único  correspondiente  al  dato  compuesto  simplifica  en  muchos  casos  la  escritura  del  programa  que  lo  maneja.    

 

9.7  TIPO  REGISTRO  (STRUCT)  Los  esquemas  de  tupla  pueden  usarse  en  programas  en  C+-­‐  definiéndolos  como  estructuras  del  tipo  registro  o  struct.  Un  registro  struct  es  una  estructura  de  datos  formada  por  una  colección  de  elementos  de  información  llamados  campos.  DEFINICIÓN  DE  REGISTROS:  se  hace  utilizando  la  palabra  clave  struct:     Typedef  struct  Tipo-­‐registro  {        Tipo-­‐campo1  nombre-­‐campo1;        Tipo-­‐campo2  nombre-­‐campo2;          ….          };  Cada  una  de  las  parejas  Tipo-­‐campo  y  nombre-­‐campo,  separadas  por  punto  y  coma,  define  un  campo  o  elemento  componente  y  su  correspondiente  ipo.  Además  hay  que  tener  en  cuenta  que  la  estrucutra  acaba  siempre  con  punto  y  coma:     Typedef  enum  TipoMes  {        Enero,  febrero  ……           };  VARIABLES  DE  TIPO  REGISTRO  Y  SU  INICIALIZACIÓN:  Para  declara  variables  de  tipo  registro  es  necesario  haber  realizado  previamente  la  deinición  del  tipo  del  registro.  No  se  permite  declarar  variables  de  tipo  anónimo.       TipoFecha  ayer,  hoy;     TipoPunto  punto1,  punto2;     TipoFecha  hoy  =  {12  ,Marzo,2009};  USO:  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.  En  estas  asignaciones  debe  cumplirse  la  compatibilidad  de  tipos.  No  es  suficiente  la  compatibilidad  estructural,  es  decir,  dos  estructuras  con  los  mismos  campos  no  son  compatibles  si  sus  definiciones  se  hacen  por  separado.  ACCESO  A  UNA  ESTRUCTURA:  Dado  que  los  campos  de  una  estructura  se  procesa  generalmente  de  forma  individual,  deberá  poder  acceder  a  cada  uno  de  estos  campos  individualmente.  Para  acceder  a  un  determinado  campo  para  poder  leer  o  escribir  en  él  haremos:       variable.campo    

TEMA  10-­‐.-­‐  AMPLIACIÓN  DE  ESTRUCTURAS  DE  CONTROL  10.1  ESTRUCTURAS  COMPLEMENTARIAS  DE  ITERACIÓN  

10.1.1  Repetición:  sentencia  DO  A  veces  resulta  mas  natural  comprobar  la  condición  que  controla  las  iteraciones  al  finalizar  cada  una  de  ellas,  en  lugar  de  hacerlo  al  comienzo  de  las  mismas.  La  condición  que  controla  las  repeticiones  es  una  expresión  cuyo  resultado  es  un  valor  de  tipo  bool.  Si  el  resultado  es  true  se  vuelve  a  ejecutar  la  acción  y  cuando  el  resultado  es  false  finaliza  la  ejecución  de  la  estructura.  Do  {      Operaciones….        Printf  (“otra  operación”);        Scanf(“%”  &tecla);  }  while  (tecla==  ‘s’);  

 

10.1.2  Sentencia  continue  Esta  sentencia  dentro  de  cualquier  bucle  (while,  for  o  do)  finaliza  la  iteración  en  curso  e  inicia  la  siguiente  iteración.  A  veces  dependiendo  de  la  evolución  de  los  cálculos  realizados  en  una  iteración,  no  tiene  sentido  completar  la  iteración  que  se  está  realizando  y  resulta  más  adecuado  iniciar  una  nueva.  For  (int  i  =  0;  i  <  N;  i++){      If  (vectorCoeficientes[i]  ==  0){     Continue;      }  calculo  =  calculo  /  vectorCoeficientes[i];  }    

10.2  ESTRUCTURAS  COMPLEMENTARIAS  DE  SELECCIÓN  El  if  es  una  estructura  de  selección  pero  cuando  tenemos  que  hacer  muchos  if  anidades  se  vuelve  inestable  y  difícil  de  entender  para  resolver  este  problema  utilizaremos  el  switch.  

10.2.1  Sentencia  SWITCH  Cuando  la  selección  entre  varios  casos  alternativos  depende  del  valor  que  toma  una  determinada  variable  o  del  resultado  final  de  una  expresión,  es  necesario  realizar  comparaciones  de  esa  misma  variable  o  expresión  con  todos  los  valores  que  puede  tomar,  uno  por  uno,  para  decidir  el  camino  a  tomar.  

Switch  (expresion)  {  Case  valor1:     Acción  A;  

Break;  Case  valor2:  Case  valor3:     Accion  B;  

Break;  ………  }  

La  sentencia  comienza  con  la  palabra  clave  switch  y  a  continuación,  entre  paréntesis,  se  indica  la  expresión,  después,  para  cada  valor  que  puede  tomar  la  variable  utilizaremos  la  palabra  clave  case  seguido  del  valor  que  pueda  tomar  y  dos  puntos  (:)  Y  después  la  sentencia  a  realizar,  y  para  cerrar  el  case  utilizaremos  un  break.  El  manual  de  estilo  de  C+-­‐  impone  que  cada  acción  finaliza  siempre  con  la  sentencia  break  para  que  finalice  la  sentencia  swith  después  de  cada  acción.  El  uso  del  break  es  obligatorio  para  C+-­‐  y  opcional  para  C/C++.    

TEMA  11-­‐.-­‐  ESTRUCTURAS  DE  DATOS  

11.1  ARGUMENTOS  DE  TIPO  VECTOR  ABIERTO  Los  vectores  con  un  tamaño  indefinido  se  denominan  vectores  abiertos.    En  C+-­‐  los  argumentos  de  tipo  vector  abierto  se  especifican  de  manera  similar  a  una  declaración  de  tipo  vecotr,  omitiendo  el  tamño  explicito  pero  no  los  corchetes  (^*):    const  int  V[  ];  El  precio  que  hay  que  pagar  por  disponer  de  esat  facilidad  es  tener  que  pasar  siempre  la  longitud  concreta  del  vector  como  argumento,  en  cada  llamada.     EscribirVectorAbierto  (vectorDos,  NumeroElementos);  

 

11.2  FORMACIONES  ANIDADAS.  MATRICES  

11.2.1  Declaración  de  matrices  y  uso  de  sus  elementos  Las  matrices  son  estructuras  de  tipo  formación  (array)  de  dos  o  más  dimensiones.  Una  forma  sencilla  de  plantear  la  definición  de  estas  estructuras  es  considerarlas  como  vectores  cuyos  elementos  son  a  su  vez  vectores.  Los  vectores  multidimensionales  más  utilizados  son  los  bidimensionales  o  matrices,  los  cuales  vendrán  definidos  por  dos  índices  (  normalmente,  fila  y  columna).  DEFINICIÓN:  const  int  NFILAS=4;  

Const  int  NCOL=5;  Typedef  int  TipoElemento;  Typedef  TipoElemento  TipoFila  [NCOL];  Typedef  TipoFila  TipoMatriz  [NFILAS];  

Una  vez  tenemos  definido  el  TipMatriz  ya  podemos  definir  una  variable  de  ese  tipo:     TipoMatriz  tabla1,  tabla2;  La  definición  de  tipos  que  hemos  hecho  la  podríamos  simplificar:     Typedef  int  TipoMatriz  [4]  [5];     TipoMatriz  tabla1,tabla2;    OPERACIONES:  Las  operaciones  con  elementos  individuales  de  una  matrid  pueden  hacerse  directamente,  de  forma  análoga  a  la  operación  con  variables  simpes  de  ese  tipo.  En  cambio  las  operaciones  globales  con  matrices  han  de  plantearse  de  manera  similar  a  las  operaciones  globales  con  vectores.  En  general  se  operará  elemento  a  elemento,  o  a  lo  sumo  por  filas  completas.    

11.3  EL  ESQUEMA  UNIÓN  Hay  aplicaciones  en  las  que  resultaría  deseable  que  el  tipo  de  un  dato  variase  según  las  circunstancias.  Si  las  posibilidades  de  variación  son  un  conjunto  finito  de  tipos,  entocnes  se  puede  decir  que  el  tipo  del  dato  corresponde  a  un  esquema  que  es  la  unión  de  los  tipos  particulares  posibles.  Cada  uno  de  los  tipos  particulares  constituye  una  variante  o  alternativa  del  tipo  unión.  Como  situaciones  típicas  en  las  que  se  pueden  aplicar  los  esquemas  unión  tenemos:  

• Datos  que  pueden  representarse  de  diferentes  maneras.  • Programas  que  operan  indistintamente  con  varias  clases  de  datos.  • Datos  estructurados  con  elementos  opcionales.  

Ejemplo:  numero_general  =  entero  |  fracción  |  real  

11.3.1  El  tipo  unión  Un  tipo  unión  se  define  como  una  colección  de  campos  alternativos,  de  tal  manera  que  cada  dato  particular  sólo  usará  uno  de  esos  campos  en  un  momento  dado,  dependiendo  de  la  alternativa  aplicable.  La  definición  es  similar  a  la  de  un  agregado  o  struct,  usando  ahora  la  palabra  clave  unión:     Typedef  unión  TipoNumero  {       Int  valorEntero;       Float  valorReal;       TipoFraccion  valorRacional;  };  La  referencia  a  los  elementos  componentes  se  hace  también  como  en  los  tipos  struct:     Tiponumero  numero,  otro,  fraccion1,  fraccion2;  

  Numero.valorentero  =  33;     Otro.valorreal  =  float  (numero.valorentero);     Fraccion2.valorracional  =  fraccion1.valorracional;  Como  se  ha  dicho,  sólo  una  de  las  variantes  puede  estar  vigente  en  un  momento  dado.  Si  asignamos  valor  a  una  de  ellas  será    ésta  la  que  exista  a  partir  de  ese  momento,  al  tiempo  que  dejan  de  existir  las  demás.  

11.3.2  Registros  con  Variantes  El  hecho  de  que  un  dato  de  tipo  unión  deba  ir  acompañado  de  información  complementaria  para  saber  cuál  es  la  variante  aplicable  hace  que  los  tipos  unión  aparezcan  casi  siempre  formando  parte  de  estructuras  mas  complejas.  Un  ejemplo  es  lo  que  se  denomina  como  registros  con  variantes.  Se  trata  de  agregados  o  tuplas  en  los  que  hay  una  colección  de  campos  fijos,  aplicables  en  todos  los  casos,  y  campos  variantes  que  se  definen  según  el  esquema  unión.  Además  suele  reservarse  un  campo  fijo  para  indicar  explícitamente  cual  es  la  variante  aplicable  en  cada  momento,  a  esto  se  le  llama  discriminante.  

  typedef enum ClaseNumero {Enter, Real, Fracción}; typedef struct TipoFraccio { int numerador; int denominador;

} typedef union TipoValor { int valorEnter; float valorReal; TipoFraccio valorracional; };

typedef struct TipoNumero { ClaseNumero clase; ßCampo discriminante TipoValor valor; }

void EscriureNumero (TipoNumero n) { switch (n.clase) {

case Enter: printf ("% d", n.valor.valorEnter); break; case Real: printf ("% f", n.valor.valorReal); break; case fracción: printf ("% d /% d", n.valor.valorRacional.numerad oro, n.valor.valorRacional.denominador); break; default: printf («?????»); } }

TEMA  12  -­‐.-­‐  ESQUEMAS  TIPICOS  DE  OPERACIÓN  CON  FORMACIONES  

12.1  ESQUEMAS  DE  RECORRIDO  Consiste  en  realizar  cierta  operación  con  todos  y  cada  uno  de  los  elementos  de  una  formación.  Aplicable  a  vector  con  cualquier  dimensión  y  mátrices.  El  forma  más  general  del  esquema  de  recorrido  sería:     Iniciar  operación       While  (quedan  elementos  sin  tratar){         Elegir  uno  de  ellos  y  tratarlo       }       completar  operación  La  terminación  del  elemento  while  está  garantizada  ya  que  el  número  de  elementos  que  faltan  por  tratar  es  un  valor  finito  no  negativo,  que  va  disminuyendo  en  cada  iteración.    

Para  el  recorrido  de  matrices  necesitaremos  dos  for  por  ejemplo:     For  (int=0;  i<N;i++){       For  (int  j=0;  j<N;  j++){            Z  [i][j]=0;       }     }    

12.1.1  Recorrido  no  lineal  En  ciertos  casos  el  elemento  a  procesar  debe  elegirse  realizando  ciertos  cálculos  y  el  contador  de  iteraciones  sirve  fundamentalmente  para  contabilizar  el  avance  del  recorrido  y  detectar  el  final  del  bucle.    

12.2  BUSQUEDA  EN  UN  VECTOR  Una  operación  de  búsqueda  de  un  elemento  en  un  vector  consiste  en:  

1-­‐ Determinar  si  el  elemento  pertenece  o  no  al  vector.  2-­‐ En  caso  de  que  el  elemento  pertenezca  al  vector  determinar  cuál  es  su  lugar  o  posición  

en  el  vector.  Dos  de  los  métodos  más  usuales  de  búsqueda  en  vectores  son:     à  Búsqueda  secuencial  o  lineal     à  Búsqueda  binario  o  dicotómica.  BUSQUEDA  SECUENCIAL:  consiste  en  explorar  secuencialmente  (recorrer)  un  vector  desde  el  primer  elemento  hasta  el  último  y  comprobar  si  alguno  de  los  elementos  del  vector  contiene  el  valor  buscado  (comparar  cada  elemento  con  el  elemento  a  buscar).  BUSQUEDA  BINARIO  o  dicotómicos:  requiere,  para  el  peor  de  los  casos  (  cuando  el  elemento  a  buscar  es  el  último  o  no  se  encuentra)  recorrer  todo  el  vector  y  hacer  un  número  de  comparaciones  igual  al  tamaño  del  vector.  Para  vectores  con  muchos  elementos  esta  búsqueda  quizás  no  sea  muy  conveniente.  La  búsqueda  binaria  requiere  menos  comparaciones  (iteraciones)  que  la  secuencial,  pero  para  realizar  la  búsqueda  es  necesario  que  el  vector  esté  previamente  ordenado.     La  búsqueda  binario  consiste  en:  

• En  la  primera  repetición  analizamos  el  elemento  central  del  vector  • Si  el  valor  a  buscar  coincide  con  este  central  ya  se  ha  acabado  de  buscar.  • Si  el  valor  a  buscar  es  menor  que  el  central  se  buscará  en  el  tramo  izquierdo  al  

central  en  uso  de  la  misma  técnica,  y  sino  se  buscará  en  el  tramo  derecho.  • En  la  segunda  repetición  el  tramo  a  buscar  es  la  mitad  (izquierda  o  derecha  al  

central)  del  vector  y  el  elemento  a  evaluar  es  el  central  de  este  nuevo  tramo.  Estos  pasos  se  repetirán  hasta  que  encontremos  el  elemento  o  hasta  que  el  tramo  a  buscar  se  reduce  a  un  elemento  y  basta.    

12.3  INSERCION    El  problema  que  se  plantea  aquí  es  insertar  un  nuevo  elemento  en  una  colección  de  elementos  ordenados,  manteniendo  el  orden  de  la  colección.    Tenemos  elementos  almacenados  en  un  vector,  ocupando  posiciones  desde  el  principio  hasta  el  final  quedando  libre  algunos  elementos  del  final.  La  operación  se  puede  realizar  de  forma  iterativa,  examinando  los  elementos  empezando  por  el  final  hasta  encontrar  uno  que  sea  inferior  o  igual  al  que  se  quiere  insertar.  Los  elementos  mayores  que  el  que  se  quiere  insertar  se  van  moviendo  una  posición  hacia  delante,  con  lo  que  va  quedando  un  huevo  en  medio  del  vector.  Al  encontrar  un  elemento  menor  que  el  nuevo,  se  

copia  el  nuevo  elemento  en  el  

hueco  que  hay  en  ese  momento.                

12.4  ORDENACIÓN  POR  INSERCIÓN  DIRECTA  Aquí  se  aborda  una  solución  para  la  ordenación  de  datos  almacenados  en  un  vector.  El  método  más  sencillo  es  el  de  ordenación  por  inserción  directa.  Está  basado  en  el  esquema  de  inserción  mostrado  en  el  apartado  anterior.  Ejemplo:  queremos  ordenar  un  vector  v  de  diez  elementos  (0  a  9)  y  que  inicialmente  esta  desordenado,  {21,5,3,12,65,9,36,7,2,45}  Para  comenzar  el  primer  elemento  (21)  ya  está  ordenado  consigo  mismo.  Luego  extraemos  el  segundo  elemento  (5)  y  se  genera  un  hueco  ,  que  se  puede  utilizar  para  ampliar  la  parte  del  vector  ya  ordenada.  El  método  de  ordenación  consiste  en  insertar  el  elemento  extraído  en  su  lugar  correspondiente  entre  los  elementos  ya  ordenador.  Este  proceso  se  repite  con  el  tercero  y  sucesivamente.      

12.5  SIMPLIFICACIÓN  DE  LAS  CONDICIONES  DE  CONTORNO  La  programación  de  operaciones  con  vectores,  realizadas  elemento  a  elemento,  exige  con  frecuencia  realizar  un  tratamiento  especial  de  los  elementos  extremos  del  vector  o,  en  general,  de  los  elementos  del  contorno  de  una  formación.  A  continuación  veremos  algunas  técnicas  particulares  para  evitar  la  necesidad  de  detectar  de  manera  explicita  si  se  ha  llegado  a  un  elemento  del  contorno  y/o  realizar  con  él  un  tratamiento  especial.    

12.5.1  Técnica  del  centinela  Por  ejemplo,  en  el  procedimiento  general  de  búsqueda  es  necesario  comprobar  en  cada  iteración  una  condición  doble:  si  no  se  ha  alcanzado  todavía  el  final  del  vector,  y  si  se  encuentra  el  elemento  buscado.  La  doble  condición  del  bucle  complica  el  código  y  supone  un  tiempo  adicional  en  la  ejecución  de  cada  operación.    La    técnica  centinela  consiste  en  incluir  el  dato    a  buscar  en  el  vector  antes  de  comenzar  la  búsqueda.  El  vector  se  amplia  en  +1.    Se  colocal  al  final  si  la  búsqueda  es  hacia  delante  y  viceversa.  Este  actuará  de  centinela  y  asegura  que  la  búsqueda  nunca  acaba  de  manera  infructuosa.  El  esquema  de  búsqueda  se  simplifica  a  una  condición  :      Inicar  operación(colocar  centinela)     While  (no  se    encuentre  un  elemento  aceptable)  {       Elegir  siguiente  elemento  y  ver  si  es  aceptable  }  completar  operación  (  si  se  ha  encontrado  el  centinela,  indicare  fallo  en  la  búsqueda)  

 

TEMA  13  -­‐.-­‐  PUNTEROS  Y  VARIABLES  DINÁMICAS  Los  tipos  de  datos  y  variables  vistas  hasta  ahora  se  llaman  estáticas,  porque  su  tamaño  se  especifica  al  inicio  por  el  programador.  El  compilador  reserva  un  espacio  en  memoria  constante  para  la  variable  estática  durante  toda  la  ejecución  del  programa.  En  ocasiones  no  se  puede  

conocer  el  tamaño  de  las  variables,  en  estos  casos  habrá  que  hacer  uso  de  variables  dinámicas.  Estas  estructuras  se  caracerizan  por  la  reserva  y  liberación  de  memoria  que  se  realiza  durante  la  ejecución  del  programa,  y  este  espacio  puede  variar.      

13.1  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.  Hay  2  tipos  de  operaciones:     Operaciones  de  construcción:    

• 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.  

Operaciones  de  acceso:  • 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.  Para  el  caso  de  acceso    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.  Para  actuar  sobre  el  cursor  se  suelen  plantear  las  siguientes  operaciones:  

• Iniciar:  Pone  el  cursor  señalando  al  primer  elemento  • Avanzar:  El  cursor  pasa  a  señalar  al  siguiente  elemento.  • Fin:  Es  una  función  que  indica  si  el  cursor  ha  llegado  al  final  de  la  secuencia.  

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

13.2.1  Punteros  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.  En  su  lugar  usaremos  una  representación  gráfica  en  la  que  utilizaremos  una  flecha  para  enlazar  una  variable  de  tipo  puntero  con  la  variable  a  la  que  hace  referencia.      

Declaración   de  punteros:    El  tipo  de  puntero  especifica  en  realidad  el  tipo  de  variable  a  la  que  puede  apuntar.       Typedef  tipo-­‐de-­‐variable*  tipo-­‐puntero;  Una  vez  declarado  el  tipo,  se  pueden  declarar  variables  puntero  de  dicho  tipo.  Una  variable  puntero  se  puede  usar  para  designar  la  variable  apuntada  mediante  la  notación:  *puntero  Ejemplo:       Typedef  int*  tp_entero;     Tp_entero  pe;     *pe  =  33;     printf  (“%d”,*pe);  

Puntero   Variable  apuntada  

Estas  sentencias  asignan  el  valor  33  a  la  variable  dinámica  señalada  por  el  puntero  pe,  y  luego  la  imprime.  Para  poder  detectar  si  un  puntero  señala  realmente  o  no  a  otra  variable,  existe  el  valor  especial  NULL  (no  es  una  palabra  clave,  esta  dentro  de  la  librería  stdlib.h)  Este  valor  es  compatible  con  cualquier  tipo  de  puntero,  e  indica  que  el  puntero  no  señala  a  ninguna  variable.  Se  usará  para  inicializar  las  variables  de  tipo  puntero  al  comienzo  del  programa:     If  (*pe  ¡=  NULL)  {       *pe  =  33;       printf  (“%d”,  *pe);     }  

13.2.2  Uso  de  variables  dinámicas  Las  variables  dinámicas  no  tienen  reservado  el  espacio  en  memoria  que  tienen  las  demás  variables,  sino  que  se  crean  a  partir  de  punteros  en  el  momento  en  que  se  indique.  Una  vez  creadas  siguen  existiendo  incluso  después  de  que  termine  la  ejecución  del  subprograma  donde  se  crean.  Se  crean  así:           Typedef  Tipo-­‐de-­‐variable*  tipo-­‐puntero;       Tipo-­‐puntero  puntero;       Puntero  =  new  tipo-­‐de-­‐variable;  El  operador  new  crea  una  variable  dinámica  del  tipo  indicado  y  devuelve  una  referencia  que  puede  asignarse  aun  puntero  de  tipo  compatible.    La  variable  dinámica  se  crea  a  base  de  reservarle  el  espacio  necesario  en  una  zona  general  de  memoria  gestionada  dinámicamente.  En  principio  no  se  puede  asumir  que  la  variable  recién  creada  tenga  un  valor  concreto,  igual  que  las  variables  normales  que  se  declaran  sin  un  valor  inicial  explicito.  Una  vez  creadas  siguen  existiendo  hasta  que  el  programador  no  especifique  que  ya  no  se  necesita.  Para  ello  existe  la  sentencia  delete,  que  permite  destruir  la  variable  dinámica:       Delete  puntero;    

13.3  REALIZACIÓN  DE  SECUENCIAS  MEDIANTE  PUNTEROS  o  Lista  enlazada  Los  punteros  son  un  elemento  de  programación  de  muy  bajo  nivel.  Los  lenguajes  de  programación  simbólicos  deberían  evitar  su  empleo,  sustituyendo  por  mecanismos  mas  potentes  de  declaración  de  estructuras  de  datos,  que  permitiesen  definir  directamente  estructuras  dinámicas  ilimitadas.  Para  crear  una  secuencia  ilimitada  tendremos  que  recurrir  al  empleo  de  variables  dinámicas  y  punteros,  enlazando  cada  elemento  de  la  secuencia  con  el  siguiente.  Cada  elemento  de  la  secuencia  se  materializa  como  un  registro  con  dos  campos:  el  primero  contiene  el  valor  de  una  componente,  y  el  segundo  es  un  puntero  que  señala  al  siguiente.  El  último  apuntará  a    NULL.  Una  lista  es  una  esctructura  de  datos  dinámica  formada  por  un  conjunto  de  elementos,  llamados  nodos,  del  mismo  tipo  y  almacenados  en  la  memoria  principal  siguiendo  una  secuencia  lógica.  Podemos  distinguir:  

• Listas  contiguas:  Son  aquellas  en  las  que  los  elementos  se  guardan  en  posiciones  de  memoria  contiguas,  de  forma  que  equivalen  a  tablas  o  vectores  unidimensionales  .  Las  operaciones  que  podemos  hacer  en  una  lista  contigua  son:  o Ordenarla  o Buscar  un  elemento  o Añadir  un  elemento  o Eliminar  un  elemento  o Borrar  la  lista  completa  

o Copiar  una  lista  origen  a  un  destino  

o Concatenar  varias  listas  o Dividir  una  lista  en  sublistas  

 

• Listas  enlazadas:  Son  aquellas  en  la  que  los  elementos  se  encuentran  almacenados  en  posiciones  de  memoria  no  contiguas.  Cada  nodo  es  equivalente  a  un  struct  (registro)  y  está  formado  por  dos  campos:  o Campo  valor,  que  es  el  campo  que  contiene  el  dato  o Campo  siguiente:  es  el  campo  que  actúa  de  enlace  con  el  siguiente  nodo  de  la  lista  en  

secuencia  lógica.  Para  definir  un  nodo:     Typedef  struct  tiponodo  {       Tipo_elemento  valor:       Tiponodo*  siguiente;     };      

13.4  PUNTEROS  Y  PASO  DE  ARGUMENTOS    Como  cualquier  otro  dato,  un  puntero  puede  pasarse  como  argumento  a  un  subprograma.  Por  defecto,  los  datos  de  tipo  puntero  se  pasan  como  argumentos  por  valor.  Si  se  desea  usar  un  subprograma  para  modificar  datos  de  tipo  puntero,  entonces  habrá  que  pasar  el  puntero  por  referencia.      

13.4.1  Paso  de  argumentos  mediante  punteros  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.  La  dificultad  reside  en  el  hecho  de  que  pasar  un  puntero  por  valor  no  evita  que  el  subprograma  pueda  modificar  la  variable  apuntada.      

TEMA  14-­‐.-­‐  TIPOS  ABSTRACTOS  DE  DATOS  (TAD)  Concepto:  Si  identifica  el  concepto  de  tipo  de  dato  con  el  del  conjunto  de  valores  que  pueden  tomar  los  datos  de  este  tipo.      Un  enfoque  mas  moderno  de  la  programación  trata  de  asociar  la  idea  de  tipo  de  datos  con  la  clase  de  valores,  abstractos,  que  pueden  tomar  los  datos.  Esto  quiere  decir  que  la  representación  o  codificación  particular  de  los  valores  no  cambia,  el  tipo  del  dato  considerado.      En  el  enfoque  actual  de  la  programación  se  identifican  los  tipos  de  datos  de  forma  completamente  abstracta,  llegando  a  la  idea  de  tipo  abstracto  de  datos  (TAD).  Esto  quiere  decir  que  un  programa  que  use  ese  tipo  de  datos  no  debería  necesitar  ningún  cambio  por  el  hecho  de  modificar  la  representación  o  codificación  de  los  valores  de  ese  tipo.  Si  analizamos  con  cuidado  que  necesita  un  programa  para  poder  usar  datos  de  un  tipo,  encontramos  que  hace  falta:  

o Hacer  referencia  al  tipo  en  sí,  mediante  un  nombre,  para  poder  definir  variables,  subprogramas,  etc.  

o Hacer  referencia  a  algunos  valores  particulares,  generalmente  como  constantes  con  nombre.  

o Invocar  operaciones  de  manipulación  de  los  valores  de  ese  tipo,  bien  usando  operadores  en  expresiones  aritméticas  o  bien  mediante  subprogramas.  

El  conjunto  de  todos  estos  elementos  constituye  el  tipo  abstracto  de  datos  (TAD):  

  Un  tipo  abstracto  de  datos  es  una  agrupación  de  una  colección  de  valores  y  una  colección  de  operaciones  de  manipulación.  Es  importante  comprender  que  estas  colecciones  son  cerradas,  es  decir  sólo  se  deben  poder  usar  los  valores  abstractos  y  las  operaciones  declaradas  para  ese  tipo.  Además  los  detalles  de  cómo  se  representan  los  valores  y  cómo  se  implementan  las  operaciones  pueden  estar  ocultos  para  quien  utiliza  el  tipo  abstracto.      

14.1  REALIZACIÓN  DE  TIPOS  ABSTRACTOS  EN  C+-­‐  DEFINICION:  De  esta  manera  se  pueden  definir  tipos  abstractos  de  datos,  ya  que:  

o Los  campos  de  datos  sirven  para  almacenar  el  contenido  de  información  del  dato  abstracto.  

o Los  subprogramas  permiten  definir  operaciones  sobre  esos  datos.  o La  posibilidad  de  declarar  ciertos  elementos  como  privados  permite  ocultar  detales  de  

implementación,  y  dejar  visible  sólo  la  interfaz  del  tipo  abstracto.    

Ejemplo de TAD typedef struct TipoPunto { float x; float y; void Leer (); void Escribir (); float Distancia (TipoPunto p);

};

A  parte  de  definir  dentro  del  registro  los  procedimientos  y  funciones  estos  subprogramas  deben  implementarse  fuera  del  registro  (fuera  del  .h)  de  la  siguiente  manera:  void TipoPunto::Leer( ) { printf ("Coordenada X?"); scanf ("% f", & x); printf ("Coordenada Y?"); scanf ("% f", & y); } void TipoPunto::Escribir( ) { printf (“(%f, %f)”, x, y); } float TipoPunto::Distancia (TipoPunto p) { float deltaX, deltaY; deltaX = xp.x; deltaY = yp.y; } return sqrt (deltaX * deltaX + deltaY * deltaY); }

14.2  OCULTACIÓN    Para  que  un  tipo  sea  realmente  abstracto  haría  falta  que  los  detalles  de  implemtenación  no  fueran  visibles.  Tanto  en  los  campos  como  en  los  procedimientos.  En  el  caso  anterior  del  TipoPunto  los  campos  x  e  y  son  visibles  en  todo  momento,  y  pueden  funcionar  consultados  y  modificados  por  el  código  que  usa  el  tipo.  

Para  permitir  esta  ocultación  los  tipos  struct  admiten  la  posibilidad  de  declara  ciertos  elementos  componentes  como  privados,  utilizando  la  palabra  clave  private  para  delimitar  una  zona  de  declaraciones  privadas  dentro  de  la  estructura.  Ejemplo typedef struct TipoData { void pone (int día, int mes, int año); int Día (); int Mes (); int Año (); void Leer (); bool EsCorrecta (int día, int mes, int año); ........ private: int día, mes, año; }; Las variables dia, mes y año slo son visibles (accesibles) desde las funciones y procedimientos que están definidos dentro de la estructura, ya que están definidas con el modificador private.

14.3  METODOLOGÍA  BASADA  EN  ABSTRACCIONES  La  técnica  de  programación  estructurada,  basada  en  refinamientos  sucesivos,  puede  ampliarse  para  contemplar  la  descomposición  modular  de  un  programa.  La  metodología  de  desarrollo  será  esencialmente  la  misma  que  se  ha  presentado  anteriormente.  La  diferencia  es  que  ahora  disponemos  también  de  un  nuevo  mecanismo  de  abstracción,  que  son  los  tipos  abstractos  de  datos.  En  cualquier  caso  el  desarrollo  deberá  atender  tanto  a  la  organización  de  las  operaciones  como  a  la  de  los  datos  sobre  los  que  operan,  de  manera  que  habrá  que  ir  realizando  simultáneamente  las  siguientes  actividades:  

o Identificar  las  operaciones  a  realizar,  y  refinarlas.  o Identificar  las  estructuras  de  información  y  refinarlas.  

14.3.1  Desarrollo  por  refinamiento  basado  en  abstracciones  En  cada  etapa  de  refinamiento  de  una  operación  hay  que  optar  por  una  de  las  alternativas  siguientes:  

o Considerar  la  operación  como  operación  terminal,  y  codificarla  mediante  sentencias  del  lenguaje  de  programación  

o Considerar  la  operación  como  operación  compleja,  y  descomponerla  en  otras  más  sencillas.  

o Considerar  la  operación  como  operación  abstracta,  y  especificarla,  escribiendo  más  adelante  el  subprograma  que  la  realiza.  

Ahora  podemos  reformular  estas  opciones  para  las  estructuras  de  datos  a  utilizar:  o Considerar  el  dato  como  un  dato  elemental,  y  usar  directamente  un  tipo  predefinido  

del  lenguaje  para  representarlo.  o Considerar  el  dato  como  un  dato  complejo,  y  descomponerlo  en  otros  más  sencillos  

(como  registro,  unión  o  formación).  o Considerar  el  dato  como  un  dato  abstracto  y  especificar  su  interfaz,  dejando  para  

más  adelante  los  detalles  de  su  implementación.  

TEMA  15  -­‐.-­‐  MÓDULOS  CONCEPTO:  un  módulo  es  un  fragmento  de  programa  desarrollado  de  forma  independiente  (diseñado  y  compilando  de  forma  independiente  del  programa  que  lo  va  a  utilizar).  El  concepto  

de  módulo  está  íntimamente  asociado  a  la  abstracción  de  datos.  Cuando  utilizamos  un  módulo  a  un  programa,  solo  hay  que  saber  que  hace  el  módulo  y  sus  funciones,  pero  no  tenemos  por  qué  saber  cómo  esta  implementado.  La  razón  de  exigir  compilación  por  separado  para  los  distintos  módulos  de  un  programa  obedece  a  la  necesidad  de  limitar  la  complejidad  de  aquello  que  está  siendo  elaborado  por  una  persona  en  un  momento  dado.  El  programador  podrá  concentrarse  en  el  módulo  prescindiendo  en  parte  de  cómo  se  utiliza  ese  modulo  desde  el  resto  del  programa.  El  concepto  de  módulo  esta  íntimamente  ligado  al  concepto  de  abstracción.  ESPECIFICACIÓN  Y  REALIZACIÓN:  Al  igual  que  en  cualquier  elemento  abstracto,  en  un  módulo  podemos  distinguir:  

o La  especificación  del  modulo  es  todo  lo  que  se  necesita  para  poder  usar  los  elementos  definidos  en  él.  (que  hace  el  módulo)  (interfaz)  

o La  realización  del  modulo  consistirá  en  la  realización  de  cada  uno  de  los  elementos  abstractos  contenidos  en  dicho  modulo.(como  lo  hace).  

15.1.1    Compilación  separada  Los  lenguajes  de  programación  que  permiten  programar  usando  módulos  pueden  emplear  diversas  técnicas  para  definirlos  e  invocar  los  elementos  definidos  en  ellos.    

o Compilación  separada:  El  programa  está  formado  por  varios  ficheros  fuente,  cada  uno  de  los  cuales  se  compila  por  separado.  

o Compilación  segura:  Al  compilar  un  fichero  fuente  el  compilador  comprueba  que  el  uso  de  elementos  de  otros  módulos  es  consistente  con  la  interfaz.  

o Ocultación:  Al  compilar  un  fichero  fuente  el  compilador  no  usa  información  de  los  detalles  de  realización  de  los  elementos  de  otros  módulos.  

15.1.2  Descomposición  modular  La  posibilidad  de  compilar  módulos  de  forma  separada  permite  repartir  el  trabajo  de  desarrollo  de  un  programa,  a  base  de  realizar  su  descomposición  modular.  Los  diferentes  módulos  pueden  ser  encargados  a  programadores  diferentes  y  gracias  a  ello  todos  pueden  trabajar  al  mismo  tiempo.  De  esta  forma  se  puede  desarrollar  en  un  tiempo  razonable  los  grandes  programas  correspondientes  a  las  aplicaciones  de  hoy  dia,  que  totalizan  cientos  de  miles  o  millones  de  sentencias.  La  descomposición  modular  puede  reflejarse  en  un  diagrama  de  estructura.              En  este  diagrama  se  representa   cada  módulo   como  un  rectángulo,  con  el  nombre  del  módulo  en  su   interior.  Las  líneas  indican  las  relaciones   de  uso,   pueden  llevar  punta  de  flecha  si  es  necesario  indicar  expresamente  cuál  es  el  sentido  de  la  relación.  Normalmente  no  es  necesario,  pues,  como  en  este  caso,  un  módulo  que  usa  otro  se  dibuja  por  encima  de  él.  Las  líneas  se  interpretan  en  la  dirección  de  arriba  abajo.  El  objetivo  de  la  ingeniería  de  software  es  facilitar  el  desarrollo  de  una  aplicación  de  forma  organizada,  de  manera  que  muchas  personas  puedan  colaborar  simultáneamente  en  un  mismo  proyecto.  Para  que  la  descomposición  en  módulos  sea  adecuada,  desde  un  punto  de  vista,  conviene  que  los  módulos  resulten  tan  independientes  unos  de  otros  como  sea  posible.  Esta  independencia  se  analiza  según  dos  criterios,  denominados  acoplamiento  y  cohesión.  

C   D  

B  

A  

o El  acoplamiento  entre  módulos  indica  cuántos  elementos  distintos  o  características  de  uno  o  varios  módulos  han  de  ser  tenidos  en  cuenta  a  la  vez  al  usar  un  módulo  desde  otro.  Este  acoplamiento  debe  reducirse  a  un  mínimo.  

o La  cohesión  indica  el  grado  de  relación  que  existe  entre  los  distintos  elementos  de  un  mismo  módulo,  y  debe  ser  lo  mayor  posible.  Esto  quiere  decir  que  dos  elementos  íntimamente  relacionados  deberían  ser  definidos  en  el  mismo  módulo,  y  que  un  mismo  módulo  no  debe  incluir  elementos  sin  relación  entre  sí.  

 

15.2  MÓDULOS  EN  C+-­‐  Un  programa  descompuesto  en  módulos  se  escribe  como  un  conjunto  de  ficheros  fuente  relacionados  entre  sí,    y  que  pueden  compilarse  por  separado.  Cada  fichero  fuente  constituye  así  una  unidad  de  compilación.    

15.2.1  Proceso  de  compilación  simple  Un  fichero  fuente    es  un  fichero  de  texto  que  contiene  el  código  de  una  unidad  de  compilación,  es  decir,  es  posible  invocar  el  compilador  dándole  como  entrada  solo  ese  fichero  fuente.  La  compilación  de  un  fichero  fuente  produce  un  fichero  objeto  que  contiene  la  traducción  del  código  C+-­‐  a  instrucciones  de  máquina.  Los  ficheros  fuente  tienen  la  extensión  .cpp,  los  ficheros  objeto  la  extensión  .o.        

     

   

15.2.2  Módulo  principal  Cuando  se  descompone  un  programa  en  módulos  uno  de  ellos  debe  ser  el  módulo  principal  uno  de  ellos  ha  de  ser  el  programa  principal  (main).      

15.2.3  Módulos  no  principales  No  permiten  generar  un  programa  ejecutable  por  sí  solos  (  sin  main).  Están  destinados  a  ser  usados  por  el  programa  principal.  Hay  que  distinguir  los  elementos  públicos  y  los  privados.  La  distinción  de  estos  elementos  se  hace  repartiendo  el  código  del  modulo  en  dos  ficheros  fuente  separados:  un  fichero  interfaz  (.h)  o  fichero  de  cabecera,  y  un  fichero  de  implementación  (.cpp).    Los  ficheros  de  cabecera  (.h)  se  deben  incluir  en  los  ficheros  de  implenteación  (cpp)  y  esto  se  hace  con  la  directiva  #include  que  sirve  para  hacer  referencia  a  un  fichero  fuente  desde  otro,  y  tiene  como  parámetro  el  nombre  dl  fichero  físico  (.h)  incluyendo  la  extensión.  

15.2.4  Uso  de  módulos  Para  usar  los  elementos  públicos  definidos  en  un  módulo  hay  que  incluir  la  interfaz  de  ese  módulo  en  el  código  donde  se  vaya  a  utilizar,  con  la  directiva  #include.  La  novedad  ahora  es  que  los  nombres  de  los  ficheros  de  la  propia  aplicación  deben  escribirse  entre  comillas  (“….”)  y  no  entre  ángulos  (<…>).  Con  esto  se  indica  al  compilador  que  debe  buscar  dichos  ficheros  en  donde  reside  el  código  fuente  de  la  aplicación  y  no  donde  está  instalada.    

15.2.5  Declaración  y  definición  de  elementos  públicos  En  la  declaración  de  un  elemento  hay  que  especificar  lo  necesario  para  que  el  compilador  pueda  compilar  correctamente  el  código  que  usa  dicho  elemento.  

..cpp   .o   .exe  compilador   Montador  

En  la  definición  de  un  elemento  hay  que  especificar  lo  necesario  para  que  el  compilador  genere  el  código  del  propio  elemento.  

o Los  tipos  y  constantes  se  especifican  totalmente  en  el  fichero  de  interfaz.  No  hay  declaración  y  definición  separadas.  

o Las  variables  se  definen  de  la  manera  habitual  en  el  fichero  de  implementación,  incluyendo  la  especificación  de  valor  inicial  en  su  caso.    En  el  fichero  de  interfaz  se  pone  además  una  declaración  que  indica  el  tipo  y  el  nombre  de  la  variable,  sin  indicar  valor  inicial,  y  precedida  de  la  palabra  clave  extern.  

o Los  subprogramas  se  definen  de  la  manera  habitual  en  el  fichero  de  implementación  y  permiten  al  compilador  generar  el  código  objeto  del  subprograma.  En  el  fichero  de  interfaz  se  pone  además  una  declaración  en  forma  de  prototipo  o  cabecera  de  subprograma  sólo  con  el  tipo,  nombre  y  argumentos.    

 

15.3  DEPENDENCIAS  ENTRE  FICHEROS  Las  relaciones  de  uso  entre  módulos  se  corresponden,  con  las  directivas  #include  usadas  en  un  fichero  fuente  para  hacer  visibles  los  elementos  de  otro,  y  que  pueden  aparecer  en  el  fichero  .cpp  y/o  en  el  .h.  La  recomendación  es:  

o Un  fichero  xxx.h  debe  incluir  otros  yyy.h  que  use  directamente.  o Un  fichero  xxx.cpp  debe  incluir  su  propio  xxx.h  y  otros  yyy.h  que  use  directamente.  

 

15.3.1  Datos  encapsulados  Cuando  definimos  un  tipo  abstracto  de  datos,  luego  hay  que  declarar  variables  de  ese  tipo  para  poder  trabajar  con  ellas.  Los  datos  encapsulados  nos  permiten  declarar  dentro  de  un  módulo  una  variable  y  evitar  una  declaración  explicita  externa.  Por  ejemplo,  el  módulo  de  implementación  pondríamos:  Static  Untipo  valorInterno;  Static  void  Operacion3  (  )  {  …..valor  interno…..  }  void  Operacion1  (  )  {  …..valor  interno….  }  void  Operacion2  (  )  {  ….valor  interno….  }  Hay  que  recordar  que  por  defecto  las  variables  y  subprogramas  definidos  en  el  nivel  más  externo  de  un  módulo  son  globales  por  defecto.  Para  que  sean  tratados  como  locales  hay  que  poner  delante  de  ellos  la  palabra  static.