Subido por Edu Oliver

TAIN - B03T03 - PROGRAMACION

Anuncio
Temario Oposición a
Técnicos Auxiliares de Informática
(BOE 03 NOV 2015)
Considerando los Reyes, de gloriosa memoria, cuánto era provechoso e honroso
que a estos sus Reinos se truxiesen libros de otras partes, para que con ellos se
ficiesen los hombres letrados, quisieron e ordenaron: que de los libros non se
pagase alcabala, y porque de pocos días a esta parte, algunos mercaderes
nuestros, naturales y extranjeros, han trahido y cada día trahen libros mucho
buenos, lo cual, por este que redunda en provecho universal de todos, e
ennoblecimiento de nuestros Reinos; por ende, ordenamos e mandamos que,
allende de la dicha franquiza, de aquí en adelante, de todos los libros que se
truxeren a estos nuestros Reinos, así por mar como por tierra, non se pida, nin se
pague, nin lleve almoxarifazgo, nin diezmo, nin portazgo, nin otros derechos algunos
por los nuestros Almoxarifes, nin los Desmeros, nin Portazgueros, nin otras
personas algunas, así como las cibdades e villas e lugares de nuestra Corona Real,
como de Señoríos e órdenes e behenias; más que de todos los dichos derechos o
almoxarifazgos sean libres e francos los dichos libros.
Orden de los Reyes Católicos
1
TODOS LOS TEXTOS DE ESTOS APUNTES LLEVAN LA
SIGUIENTE LICENCIA, EXCEPTO SI SE INDICA LO CONTRARIO
Edición:
1ª Edición Julio 2.015
Título:
Temario Oposición a Técnicos Auxiliares de Informática
Autor:
http://apuntedecaramelo.blogspot.com.es/
2
TEMA 3. LENGUAJES DE PROGRAMACIÓN. REPRESENTACIÓN DE TIPOS DE DATOS.
OPERADORES. INSTRUCCIONES CONDICIONALES. BUCLES Y RECURSIVIDAD. PROCEDIMIENTOS,
FUNCIONES Y PARÁMETROS. VECTORES Y REGISTROS. ESTRUCTURA DE UN PROGRAMA
1. INTRODUCCIÓN
2. LENGUAJES DE PROGRAMACIÓN
2.1. Abstracción
2.2. Objetivo
2.3. Paradigma
2.4. Traducción
3. REPRESENTACIÓN DE TIPOS DE DATOS
3.1. Clasificación
3.2. Tipos de datos simples
3.2.1. Direcciones (punteros)
3.2.2. Enteros
3.2.3. Reales
3.2.4. Caracteres
4. OPERADORES
4.1. Aritméticos
4.2. Relacionales e Incrementales
4.3. Lógicos y de bits
4.4. De asignación
5. INSTRUCCIONES CONDICIONALES
6. BUCLES Y RECURSIVIDAD
7. PROCEDIMIENTOS, FUNCIONES Y PARÁMETROS
8. VECTORES Y REGISTROS
9. ESTRUCTURA DE UN PROGRAMA
9.1. Proceso de creación de un programa ejecutable
9.2. Estructura
9.3. Factores y métricas de calidad
9.4. Estrategias de pueba
190
1. INTRODUCCIÓN
Un lenguaje de programación es una representación formal de algoritmos y funciones. Suponen un sistema
normalizado para definir cómo y qué datos operar y desarrollar aplicaciones portables. Un lenguaje se dota
de un conjunto de símbolos, más esquemático que el lenguaje natural, y una sintaxis que otorga significado
a los símbolos para gestionar el comportamiento del sistema.
Las características comunes, como la definición de sus elementos, (operadores, instrucciones), los niveles
léxico (símbolos), sintáctico (reglas de relación entre símbolos) y semántico (significado de las expresiones),
y el ámbito de su aplicación (concreta o genérica). La sintaxis hace uso de parámetros como datos
(variables y constantes), operadores e instrucciones. Otra característica común es la posibilidad de definir
subprogramas, estructuras que agrupan código repetido, que al necesitarse se invoca; lo que ahorra líneas
de código y facilita su legibilidad.
Su evolución se puede agrupar en 5 generaciones. Las 2 primeras responden al estado primitivo de
desarrollo; lenguaje máquina y ensamblador. La 3ª, lenguajes de medio y alto nivel, imperativos y OO, con
sintaxis más amigables (C, C++, Java…). La 4ª, lenguajes declarativos, como SQL, más cercanos al
lenguaje natural. La 5ª, lenguajes en que no interviene el programador, propios de inteligencia artificial (IA).
2. LENGUAJES DE PROGRAMACIÓN
Se caracterizan por la definición de los niveles léxico, sintáctico y semántico y el ámbito de aplicación. La
definición de estos 3 niveles usa distintos elementos.
Datos. Un lenguaje define tipos de datos y su representación interna. Típicos son enteros, reales o lógicos.
En general, al declarar un tipo de dato, el lenguaje lo representa como variable. Las constantes suelen usar
una palabra reservada como constant o similar. La asignación puede usar símbolos tipo “:=”, “=”, etc.
Una variable se define en un ámbito local (limitado; al abandonarse se elimina la variable) o global (contexto
del programa). No se deben mezclar ámbitos de uso. Es buena práctica no dar el mismo nombre a variables
locales y globales. Para cambiar de ámbito se usa el “paso por valor”, no la variable en memoria.
Operadores. Indican operaciones entre datos. Se distinguen aritméticos (suma, resta, etc. en forma común),
relacionales, (para comparar, <, > =, <>, ==,...) y lógicos, que evalúan expresiones devolviendo un valor
binario (NOT (!), AND (&), OR (||), etc.). Los operadores tienen prioridades, lo que hace que se evalúen
primero unas operaciones prioritarias.
Instrucciones. Indican la acción a realizar. Se distinguen condicionales, de repetición y ordinarias.
Las instrucciones condicionales evalúan una expresión y en función del resultado realizan un proceso.
Ejemplos son IF... THEN... ELSE, SWITCH o CASE. Las instrucciones de repetición realizan el mismo
proceso varias veces. Ejemplos son WHILE, FOR o DO... UNTIL. Las instrucciones ordinarias englobarían
el resto de instrucciones que realizan procesos comunes de lectura, escritura, etc.
Los subprogramas, procedimientos (o métodos) y funciones, se diferencian en que los primeros realizan un
proceso y las funciones además devuelven un valor. Llamar o invocar un subprograma requiere parámetros
de entrada, en general variables. Un parámetro concreto, se denomina argumento. El paso de variables se
realiza por valor o por referencia. Se pueden incluir comentarios en el código, que facilitan el mantenimiento
y son ignorados en la traducción. Pueden usar símbolos como // o /*. Los lenguajes de programación se
pueden clasificar según varios criterios: abstracción, objetivo, paradigma y traducción.
2.1. Abstracción
El nivel de abstracción del lenguaje (cercanía al natural) distingue lenguajes máquina, ensamblador y de
alto nivel. El lenguaje máquina es el que interpreta el microprocesador. Es la programación primitiva,
engorrosa, al usar código binario e instrucciones imperativas. Al ser diferente para cada máquina, es difícil
de portar. La solución es programar a alto nivel y traducir.
El ensamblador es una primera abstracción del lenguaje máquina; usa nemónicos para las instrucciones.
Permite comentarios y etiquetas que definen puntos de control o rutinas. Los lenguajes de alto nivel son
más cercanos al humano, más fáciles de aprender y con sintaxis bien definida. Independizan el desarrollo
de la tecnología, liberándolo de detalles hw. Sus características son un conjunto de instrucciones amplio y
potente, uso de variables, sintaxis flexible y legibilidad. Deben traducirse o compilarse. El compilador marca
la eficiencia del código máquina traducido. Ejemplos de lenguaje de alto nivel son C, C++, Java, etc.
2.2. Objetivo
Este criterio distingue lenguajes de propósito general, específico y scripting. Los de propósito general
pueden usarse para programar cualquier tipo de aplicación. También marcan una especialización de facto;
así, C y C++ se usan mucho en programación de SO o Java en aplicaciones multiplatarfoma.
191
Los lenguajes de scripting automatizan tareas de
otro programa, que los ejecuta e interpreta. Los
lenguajes de shell scripting se ejecutan en el shell
del SO para tareas de gestión. Los más conocidos
son los de Unix o Linux como Bash. Otros
lenguajes de scripting, como JavaScript, se usan
en navegadores. El esquema actual de trabajo
puede resumirse en: PHP en servidores, datos en
XML y Javascript en navegador. PHP, PERL o
ASP son lenguajes de script interpretados.
Los lenguajes de propósito específico, se aplican a
problemas específicos como gráficos o sonido. Por
ejemplo el lenguaje MediaWiki, actuaría entre el
lenguaje natural y el HTML final, no usándose para
otro propósito.
2.3. Paradigma
El paradigma de programación es la filosofía usada en el desarrollo. El tipo de programa a desarrollar
marcará el paradigma más adecuado. Se distinguen paradigmas de programación:
Estructurada. Basada en la definición de métodos y funciones que representan subprogramas que facilitan
mantenimiento y reutilización de código. Desaconseja el uso de instrucciones incondicionales.
Modular. Es la evolución de la estructurada. La idea subyacente es la misma, estructurar el desarrollo en
módulos o subrutinas. Hace uso de bibliotecas.
Orientada a objetos (POO). Su esencia es similar; organiza el código de forma modular pero orientándose
más a la realidad. Un programa se denomina clase y posee una entidad diferenciada. Cada clase, puede
ser hija o padre de otra que la amplíe (herencia), reutilizando masivamente el código. El uso concreto del
código de la clase se hace con objetos que interactúan. C++, Java, o Pascal soportan POO.
Se considera a Simula (1967) el primer lenguaje OO, concebido para simulación. Con Smalltalk (1972) se
desarrolló el grueso de la teoría de POO. Otros ejemplos son ABAP, C++, Object Pascal, Eiffel, Java,
JavaScript, Perl, PowerBuilder, Python, Ruby, VB .NET o Scala (usado por Twitter). Algunos lenguajes
combinan la POO con otros paradigmas, como C++, Pascal u OOCOBOL. Un nivel más de abstracción lo
supone la metodología de Programación Orientada a Aspectos (POA), en desarrollo.
En tiempo real. Usada en sistemas con requisitos temporales críticas, como en centrales nucleares. Un
lenguaje de este tipo debe ofrecer agilidad para interactuar con dispositivos de E/S, tener medios de control
del tiempo, establecimiento de umbrales, prioridades, etc.
Otros paradigmas. Suelen identificarse programación imperativa, que detalla comandos de funcionamiento
(usada en todos los lenguajes); funcional, que define instrucciones con funciones matemáticas, como LISP;
declarativa, que enuncia tareas, no cómo realizarlas, como SQL; y la lógica, que modela la lógica humana
con parámetros y métricas. Se aplica en IA y sistemas expertos y lenguaje representativo es PROLOG.
2.4. Traducción
Los lenguajes de programación pueden ser compilados si se traducen por completo antes de su ejecución o
interpretados si se traducen instrucción a instrucción al ejecutarse, o compilados en tiempo de ejecución
(usados en máquinas virtuales). Otra clasificación más específica distinguiría lenguajes tipados y no tipados.
Los tipados declaran tipos de datos de forma estática; sus variables tienen el mismo tipo en ejecución. En
lenguajes no tipados las variables pueden cambiar su tipo de datos durante la ejecución, como JavaScript.
Preguntas de EXAMEN
Es un lenguaje orientado a objetos puro:
a) Smalltalk
b) C
c) Cobol
d) C++
Qué pareja lenguaje y paradigma de programación en que se basa es correcta:
a) Haskell-Lógico
b) Prolog-Funcional
c) Java-Imperativo
d) JavaScript-Prototipado
El lenguaje ensamblador es un lenguaje de programación de…
a) Alto nivel y correlación directa con código máquina
b) Alto nivel y correlación directa con Java
c) Bajo nivel y correlación directa con código máquina
d) Bajo nivel y correlación directa con Java
192
Son paradigmas de programación:
a) Programación imperativa y funcional
b) Programación imperativa y orientada a objetos
c) Programación funcional y lógica
d) Todas son correctas
3. REPRESENTACIÓN DE TIPOS DE DATOS
Se puede definir dato como la representación simbólica de un hecho cuantitativo o cualitativo, que lo
caracteriza. Un tipo de dato es la especificación del dominio, rango de valores y conjunto operaciones
válidas relativas al mismo. La representación de un tipo de dato especifica cómo se interpretan los símbolos
que lo dan significado. Por tanto, intrínsecamente define también el dominio y las operaciones sobre ellos.
3.1. Clasificación
Sean los criterios de clasificación mostrados en el
esquema. En función de quién define el tipo de dato
se identifican los estándares, definidos por el lenguaje
de programación o por el usuario.
Según la representación interna que interpreta la máquina, pueden distinguirse datos escalares o simples,
que se interpretan a partir de bits, o datos estructurados (estructuras de datos), que añaden otro nivel de
significado al organizar en estructuras datos simples.
3.2. Tipos de datos simples
Los tipos de datos simples son números enteros, reales, caracteres y direcciones de memoria (punteros),
que a veces se denominan datos derivados.
3.2.1. Direcciones (punteros)
Se representan como números naturales. Su dominio es 2n, siendo n el tamaño o número de líneas del bus
de direcciones. En la UCP, en general, los registros tienen n bits y suele coincidir con el tamaño de palabra.
En la MP suele poder accederse a palabras, medias palabras y bytes.
3.2.2. Enteros
Para la representar enteros, se suele considerar el formato
BCD, poco usado y con 2 variantes, decimal empaquetado,
de 16 bits y desempaquetado, de 32. El formato más usado
es el de coma fija, que interpreta el bit más significativo
como bit de signo, y los n-1 bits restantes como módulo,
complemento A1 o CA2, como se muestra en la figura.
3.2.3. Reales
La representación de números reales suele usar el formato de coma flotante. El número se interpreta con el
signo indicado por el BMS; exponente el exceso de 2e-1 (E=C-2e-1) de los siguientes ‘e’ bits y de mantisa el
valor decimal (M) de los siguientes ‘m’ bits, con base implícita, en general 2, como se muestra en la figura.
Se dan 2 variantes, la normalización entera, que interpreta
la base como tal y la normalización fraccionaria que
interpreta la base como 0,M. Para números negativos se
usa el signo y módulo o complemento de la mantisa. La
norma de referencia que suele usarse para coma flotante
es IE3 754. Interpreta un número real como X=±1,MꞏbE.
Los negativos se interpretan con signo y módulo. M sigue
la normalización fraccionaria (omitiendo el bMs, bit Más
significativo, 1), la base, b=2 y E, exceso de 2e-1-1, siendo
E=C-2e-1+1.
Debe tenerse en cuenta la interpretación según el almacenamiento en memoria. Se distinguen 2 convenios,
extremista mayor o “big endian” y menor o “little endian”. Little endian, interpreta como byte menos
significativo el de la posición de memoria más baja y big endian, como bMs. A la izquierda se muestra cómo
se almacena la cadena “$123” con ambos convenios y a la derecha, el ejemplo de almacen de un real.
193
3.2.4. Caracteres
La representación de caracteres, suele hacerse con 1 B, interpretado con un código ASCII, o ISO Latin 9, o
Unicode, que puede usar 1 o varios bytes.
4. OPERADORES
Los operadores son símbolos (+, *…) que permiten hacer operaciones sobre datos, generando un resultado.
Pueden ser unarios (aplican a un operando), binarios y ternarios.
4.1. Aritméticos
Son operadores binarios que aceptan operandos numéricos y devuelven otro número del mismo tipo.
Suelen intervenir en el lado derecho de una asignación. Si está en el lado izquierdo, primero se evalúa el
lado derecho y luego se asigna. El orden de evaluación (prioridad) es similar al matemático. Multiplicaciones
y divisiones tienen más prioridad que sumas y restas. Los operadores aritméticos típicos son:
+
suma
-
resta
*
multiplicación
/
división
%
módulo (resto división entera)
4.2. Relacionales e Incrementales
Los operadores relacionales comparan datos. En general se devuelve 0 si la relación es falsa y 1 si
verdadera. El resultado suele poder asignarse a variables de tipo entero, booleano o carácter. Los
operadores incrementales son unarios, incrementan o decrementan un operando numérico una unidad. En
función de la posición relativa del operador respecto al operando se definen operaciones de post y pre
incremento o decremento. Los operadores relacionales e incrementales típicos son:
<
menor que
>
mayor que
<=
menor o igual que
++
incrementa una unidad
>=
mayor o igual que
==
igual a
!=
distinto a
--
decrementa una unidad
4.3. Lógicos y de bits
Los operadores lógicos implementan operaciones booleanas. Actúan sobre operandos booleanos y dan un
resultado booleano. Las operaciones AND y OR son binarias (2 operandos) mientras que NOT es unaria.
Los operadores lógicos se usan para combinar operadores relacionales formando expresiones lógicas
complejas. Es posible que uno o ambos operandos sea una variable o constante numérica. En ese caso se
evaluara como falso (0) si su contenido es 0, y verdadero (1) si su contenido es otro.
&&
AND (Y lógico)
||
OR (O lógico)
!
NOT(negación lógica)
Los operadores de bits son operadores binarios, excepto la negación que es unario. Aplican a variables
enteras, resultando otro entero aplicando operaciones lógicas correspondientes a los bits de los operandos.
Al desplazar bits a la izquierda, suele rellenarse con 0 por la derecha y al desplazar a la derecha, se rellena
por la izquierda con 0 si el digito más significativo es 0, y con 1 si es 1. El tipo de variable es con signo.
|
OR de bits
>>
desplazamiento de bits a la derecha
^
XOR (O exclusivo) de bits
&
AND de bits
<<
desplazamiento de bits a la izquierda
~
NOT de bits
4.4. De asignación
El operador básico de asignación es el signo '=', ‘:=’ o similares. Se sule poder realizar operaciones
combinadas al aparecer el mismo operando en el lado izquierdo y derecho de la asignación. La expresión
m=z, por ejemplo, en C, se debe interpretar como "se asigna a m el valor de z". Los operadores dobles por
ejemplo x*=y equivaldría a x=x*y. Es el nivel semántico del lenguaje que se trate.
194
=
asignación
+=
incremento y asignación
-=
decremento y asignación
%=
módulo y asignación
*=
multiplicación y asignación
/=
división y asignación
^=
XOR lógico de bits y asignación
>>=
desplazar bits a dcha. y asignación
&=
AND lógico de bits y asignación
|=
OR lógico de bits y asignación
<<=
desplazar bits a izqda. y asignación
5. INSTRUCCIONES CONDICIONALES
Las instrucciones de programación pueden clasificarse en secuenciales, condicionales e iterativas.
Secuenciales. Estructura natural de un programa. Consiste en enumerar las instrucciones en orden,
limitadas con algún identificador como el ‘;’. Tras él es habitual un retorno de carro para mejorar la
legibilidad. En algún lenguaje el limitador es el “Enter”.
Condicionales, de bifurcación o selección. Permiten cambiar la secuencia en función de condiciones, que
pueden anidarse o usar estructuras que seleccionen una operación según el valor de cierto parámetro. Se
suelen delimitar con palabras reservadas como BEGIN o END. Estructuras típicas se definen con
instrucciones IF... THEN... ELSE, CASE...OF o SWITCH. Se esquematizan en la figura.
Iterativas o repetitivas. Implementan bucles, que repiten un proceso hasta que se cumpla una condición. El
proceso puede repetirse como poco una vez, o ninguna, según la estructura que se use. Las 3 instrucciones
habituales son FOR... TO, WHILE y DO...UNTIL.
Subprogramas y subrutinas. Agrupan código que realiza un proceso, distinguiendo una funcionalidad.
Incrementan legibilidad y reutilización. La subrutina se declara en algún lugar del programa, en general al
principio, tras la declaración de variables, constantes y tipos de datos. Para identificarlas se nombran.
Operaciones comunes de las estructuras es la asignación, definición de constantes y tipos de datos. La
asignación consiste en dar valor a variables. Al definirlas se reserva espacio en memoria que podrá tener
cualquier valor, denominado basura. Hasta que no se inicializa, su valor no está controlado.
6. BUCLES Y RECURSIVIDAD
La recursividad consiste en la llamada de un programa a si mismo. Para evitar problemas existen 2
condiciones a verificar: que en cada invocación se reduzca la complejidad del problema y que exista al
menos una condición de fin de recursividad. Otro tipo de recursividad es la permitida por tipos de datos, en
que al definirse, incluyen su mismo tipo de datos. Es el caso de listas y variantes.
El diseño de un algoritmo recursivo es diferente al de uno iterativo. El recursivo soluciona el problema
suponiendo una solución atómica, con lo que al aplicarlo, usa esas suposiciones hasta alcanzarla. En ese
momento se realiza el camino inverso para completar la solución total. Un algoritmo iterativo soluciona una
parte del problema y repite el proceso para el resto, hasta completar la solución total.
Un ejemplo clásico de comparación entre recursividad e iteratividad es el cálculo del factorial de un número.
El algoritmo iterativo multiplica el número por su anterior n-1 veces. El recursivo multiplica por el factorial de
n-1, hasta la solución atómica 1!=1 y devolviendo las invocaciones hasta completar n!. Un algoritmo
recursivo puede tranformarse en uno iterativo. La elección dependerá del coste computacional, legibilidad
del código u otros. El cálculo de la complejidad de un algoritmo iterativo es más simple que el de uno
recursivo porque aumenta el coste en memoria para cada llamada recursiva. La recursividad se usa en
recorrido de grafos, orden de vectores o fuerza bruta, que intentan probar todas las soluciones. En este
caso se requiren controles si el algoritmo no converge a la solución. Son los algoritmos de backtracking.
195
7. PROCEDIMIENTOS, FUNCIONES Y PARÁMETROS
Funciones y procedimientos son estructuras que implementan subprogramas. La diferencia es que una
función, tras ejecutarse, devuelve un valor que puede usarse para asignarlo a una variable. Un
procediminento no devuelve valores a su salida, sólo realiza un proceso, como una presentación en pantalla
o la impresión de un archivo. En lenguajes orientados a objetos, funciones y procedimientos reciben el
nombre de métodos.
Las ventajas de usar subrutinas son la reducción de redundancia del código, modularidad, mejora de
legibilidad, localización de puntos de fallo, reutilización de código y ocultación de la implementación, lo que
permite que distintos programadores se centren en la función y se “comuniquen” mediante interfaces.
Las funciones y los procedimientos tienen una estructura definida. Suelen declararse con una cabecera que
indica su nombre, para identificarlos, que incluye los datos de entrada (y salida si es una función). El cuerpo
se programa siguiendo las normas del lenguaje de programación. El valor devuelto por una función se
indicará con alguna palabra tipo ‘return’ o similar.
La especificación de los datos que usa el subprograma se hace en su interfaz, a veces llamado prototipo. Al
dato genérico se le denomina parámetro y al dato concreto que se ofrece, argumento.
En el paso de variables entre programas, puede usarse el paso por valor o por referencia. El paso por valor
copia el valor de la variable y se usa de forma privada en el subprograma. Es una forma de ocultación. En el
paso por referencia, la variable es modificada directamente por el subprograma, con el riesgo que conlleva.
En principio, lo recomendable es usar el paso por valor.
8. VECTORES Y REGISTROS
Un array o vector es una estructura estática en que los datos son homogéneos (del mismo tipo) y se
organizan de forma lineal. Para referenciar un dato se usa un índice que indica su posición en el array. La
cantidad de datos que contendrá el array, su tamaño, se define al crearlo. P.e. en Pascal: vector1: array
[1..10] of integer; definiría un array de 10 posiciones, en la que cada posición contendrá un entero.
Un registro es una estructura estática heterogénea (contiene datos de distinto tipo). Otra estructura similar a
los registros son las uniones. La diferencia estriba en que en una unión todos los datos internos ocupan el
mismo espacio, igual al tamaño del mayor elemento. Si se modifica un elemento los demás se modifican. En
C se declaran con la palabra union.
9. ESTRUCTURA DE UN PROGRAMA
Añadido al tema del epígrafe se desarrollan aspectos adicionales que suelen ser materia de examen.
9.1. Proceso de creación de un programa ejecutable
La figura representa el proceso de creación de un programa ejecutable.
Preprocesado.
El código es un fichero de
texto. La máquina no lo entiende. Así, lo
primero es preprocesar el código (fuente),
realizando tareas como sustituir partes del
código por otras equivalentes o analizar
dependencias entre ficheros generando una
versión del código también en texto.
Compilado. Transforma el archivo preprocesado
en uno binario (código máquina). En general
visible en disco (con extensión .obj en WS y .o
en Linux). Estos archivos ya son parte del
programa binario que ejecutaría el equipo. Son
una parte, no el total.
Enlazado estático (linkado). Es normal necesitar código adicional, ya compilado en, por ejemplo, bibliotecas
estáticas (.liben en WS y .a en Linux). El enlazador o linker realiza la composición del archivo ejecutable
(.exe en WS, sin extensión en Linux). El código de biblioteca necesario, queda embebido en el ejecutable,
por lo que no es necesario adjuntar la biblioteca para su ejecución.
Enlazado dinámico. Al ejecutar un programa puede necesitarse más código no enlazado en la etapa anterior
y que se encuentre en bibliotecas dinámicas (o compartidas, extensión .dll en WS y .so en Linux).
Cuando estas bibliotecas se encuentran en el SO (caso general) no es necesario distribuirlas con el
ejecutable. Al enlazar dinámicamente el SO, el programa se convierte en un proceso completo en ejecución.
196
9.2. Estructura
La estructura de un programa suele identificar 3 partes,
al igual que en un texto. Si el texto consta de una
introducción, cuerpo del escrito y conclusión, un
programa constaría de cabecera, declaraciones y
cuerpo del programa; y fin, que será una instrucción
END o similar.
Las declaraciones incluyen las de unidades externas,
variables, constantes, tipos de datos y subprogramas.
Como ejemplo, la estructura de un programa sencillo en
C sería la mostrada en el cuadro.
Declaración de importaciones. Indica al compilador qué
acciones y funciones de las mencionadas en el
programa no se encuentran implementadas en este.
Estos procedimientos externos al programa se hallan
disponibles en estructuras llamadas módulos que tienen
asociado un fichero con extensión.h que contiene la
lista de procedimientos importables.
Para incluirlos se escribe una instrucción del tipo
#include.
declaración de importaciones
definición de constantes
definición de tipos
prototipos de función
declaración de variables globales
void main (void) {
declaración variables locales
instrucciones ejecutables}
implementación de funciones
/* Archivos de cabecera de funciones. */
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
void main() {
/* programa principal*/
clrscr();
// Borra la pantalla
printf("Hola World"); // Imprime
getch();
// espera pulsación teclado
}
Definición de constantes. Se asigna a un identificador un valor. Un proceso previo a la compilación
substituirá el identificador por la cadena de caracteres cada vez que lo encuentre. Se usa #define.
Macros. La directiva #define también permite definir macros,
operaciones sobre datos o variables. Se muestra su sintaxis.
#define nombre_macro (param1, ... , paramN)
[código]
En la sintaxis, “código” son sentencias en C y los parámetros (param1,…,paramn) son símbolos en código.
El preprocesador sustituye cada llamada a la macro por su código y los parámetros por los valores que
tengan cuando se llama a la macro. Como en el ejemplo.
En el código ejemplo, se define la macro CUBO con el parámetro "x". Cuando el
preprocesador encuentra la macro, la sustituye por su código sustituyendo la "x" por el
parámetro entre paréntesis (en el código, variable “a”). Usar macros se desaconseja a
menos que sea necesario. En caso de uso se recomienda que el código sea sencillo.
Definición de tipos. Permite definir tipos de datos que serán usados en todo el
programa. El ejemplo muestra un ejemplo de definición de booleano en C.
Prototipos de función. Indican las funciones a las que hacer referencia más adelante
en el código.
#include <stdio.h>
#define CUBO(x) x*x*x
void main () {
float a=3,b;
b=CUBO (a);
printf("b= %f\n",b);
}
Declaración de variables globales. Se declaran las variables que podrán usarse por cualquier procedimiento
del programa, sin necesidad de pasarlas como parámetro.
La función principal o main(). Es el único requisito indispensable
del programa. Es el cuerpo del programa.
typedef enum {FALSE=0, TRUE=1} booleano;
Declaración de variables locales. Se declaran variables que se
usarán dentro del programa.
char c;
Instrucciones. Comandos de proceso de datos.
booleano error;
Implementación de funciones. Se define el cometido de cada
función que usa el programa. Coincide con el prototipo de función.
...
void main (void) {
int larg, num_palabras;
}
9.3. Factores y métricas de calidad
Los factores de calidad permiten establecer criterios de control y medida de la garantía del código. Su
definición, como la idea de calidad, posee una parte subjetiva, originando diversas clasificaciones como la
de McCall, que distingue tres grupos, operativos, de mantenimiento y evolutivos.
Factores operativos. Afectan al uso del sw. Distingue factores de corrección (se cumplen especificaciones),
fiabilidad (libre de errores), eficiencia, seguridad (acceso a sw y datos) y facilidad de uso.
197
Factores de mantenimiento. Aplicados a la capacidad de modificación o adaptación del sw. Destacan 3;
flexibilidad (esfuerzo para modificar un programa), facilidad de prueba y facilidad de mantenimiento
(esfuerzo de localización y reparación de errores).
Factores evolutivos. Indican posibilidad de ejecutar sw de forma eficiente en distintas plataformas. Pueden
ser la portabilidad o facilidad de migración entre entornos, capacidad de reutilización o la interoperabilidad.
Las métricas de calidad son técnicas aplicadas en la valoración cuantitativa de un factor de calidad sw. Toda
métrica debe poseer al menos la propiedad de ser empírica, objetiva, simple, fácil de calcular, independiente
del lenguaje y tal que proporcione información útil. Algunos ejemplos de métricas representativas en cada
fase del ciclo de vida sw son las siguientes.
Análisis. El alto nivel conceptual y la dificultad de cuantificación limitan la existencia de métricas. Suelen
medir el tamaño del sw a desarrollar.
Ejemplos son la métrica punto-función (PF de Albrecht, contemplada como técnica en Métrica v3), usada
para cuantificar la funcionalidad de un sistema a partir de su descripción y basada en la ponderación de las
E/S de usuario, peticiones, archivos e interfaces externas.
La métrica bang, propuesta por DeMarco, calcula el tamaño del sw a desarrollar a partir del análisis. La
métrica de calidad de especificación, propuesta por Pressman, mide la calidad del análisis y la captura de
requisitos. Aunque sean factores cualitativos, se intentan cuantificar, midiendo p.e. el número de requisitos
donde los revisores coinciden. Encajan en la fase de Estudio de Viabilidad del Sistema (EVS) en Métrica v3.
Diseño. En esta fase, las métricas son morfológicas; suelen trabajar con parámetros de la estructura de los
programas o medidas del grado de cohesión, acoplamiento y complejidad de los algoritmos. Son de caja
negra en el sentido que no se evalúa la estructura de los módulos.
Ejemplos son la métrica de complejidad de Card y Glass, basada en complejidad estructural (número de
módulos que controla uno dado) y de dados (variables de E/S en relación a la complejidad estructural),
calculados para cada módulo a partir del diagrama de estructuras. La complejidad total es la suma de la
estructural y la de datos de cada módulo.
La métrica de cohesión y acoplamiento pretende cuantificar esos aspectos en módulos a partir de factores
como los parámetros de E/S o variables globales. Las métricas usadas en POO, se basan en clases y
miden aspectos como la profundidad de herencia (jerarquía de herencias; cuanto mayor profundidad, menor
calidad) o el acoplamiento entre clases (cuanto mayor, menor calidad).
Codificación. Las métricas en codificación intentan medir la complejidad sw. Ejemplos son la medida de la
complejidad ciclomática y esencial, desarrolladas por McCabe para medir la complejidad lógica. Se basan
en la representación del flujo de control como un grafo. La complejidad ciclomática mide el número de ciclos
del grafo de control. La esencial, el máximo anidamiento de las estructuras de control. Las métricas usadas
en POO se usan para medir parámetros como el número de métodos de la clase o similares.
Por fin, la documentación es otro parámetro de calidad difícil de medir. Lo que demuestra la experiencia, es
que es crucial para en el desarrollo y mantenimiento sw.
9.4. Estrategias de prueba
Las pruebas sw aplican una estrategia centrífuga, desde las partes internas del código al sistema completo.
Se distinguen pruebas unitarias, de integración, de validación y de sistema.
La figura pretende trazar el tipo de prueba con el ciclo de vida
del desarrollo sw para facilitar su comprensión.
Pruebas unitarias. Se refieren a partes del código. Se puede
trazar a la etapa de codificación. Se prueban caminos de
control importantes, interfaces, estructuras y coherencia de
datos, condiciones límite y aspectos del estilo.
Pruebas de integración. Se refieren al funcionamiento del código en conjunto, en su totalidad, de la
arquitectura sw. Se traza con la fase de diseño del ciclo de vida. Se buscan errores de interacción,
interfaces. La táctica es la integración incremental, de sólo algunos módulos e ir avanzando a la totalidad.
Las pruebas de sistema se refieren a requisitos funcionales y están ligadas muchas veces a entornos de
operación. Por tanto se trazan con la fase de análisis del ciclo de vida. Siguen una táctica general de prueba
de caja negra, que oculte la implementación pero muestre la conformidad con los requisitos.
Las pruebas de validación se refieren al cumplimiento de requisitos de usuario. Se podría trazar con el
análisis de viabilidad del sistema. El objetivo es la prueba real del sistema. Algunas pruebas pueden ser las
de recuperación, forzando el fallo del software, las de seguridad, resistencia, rendimiento, etc.
198
Las pruebas de validación y de sistema pueden entenderse como una misma cosa. El matiz estaría en que
las de sistema tendrían un carácter interno a la organización que desarrolla el SI y las de validación las hace
el peticionario. También se distinguen pruebas alfa y beta. Las primeras, en un entorno de preproducción
(maqueta) y las segundas en uno de producción o no controlado por el equipo de desarrollo. Las pruebas
beta pueden incluir otro tipo de pruebas como las de vulnerabilidades. Métrica v3, en su interfaz de
aseguramiento de la calidad también identifica estos tipos de pruebas.
Métrica v3 indica que en el proceso de
construcción del SI, el grupo de
calidad es el encargado de revisar los
estándares
de
nomenclatura
y
normativa aplicada en la generación
del código de componentes, la
evaluación de los resultados de las
pruebas, los manuales de usuario y el
esquema de formación.
Con respecto a las pruebas, se revisa
que se han llevado a cabo las pruebas
unitarias, de integración y del sistema
según los criterios de selección de
verificaciones y casos de prueba
asociados fijados en el plan de
aseguramiento de la calidad. El
esquema muestra la correspondencia
entre las actividades del proceso CSI
(Construcción del SI) y las del interfaz
de Aseguramiento de la Calidad.
Preguntas de EXAMEN
En relación a las técnicas de prueba del sw, el análisis de valores límite:
a) Es una métrica que proporciona una medición cuantitativa de la complejidad lógica de un programa
b) Pertenece a las pruebas de caja negra
c) Pertenece a las pruebas de caja blanca
Según MÉTRICA 3, las pruebas de Regresión:
a) Tratan de evitar que los cambios provocados por una petición no introduzcan un comportamiento no
deseado en otros componentes no modificados
b) Se especifican durante el estudio de viabilidad del Sistema de Información
c) Nunca implican la repetición de pruebas que ya se han realizado previamente
d) Son definidas por los usuarios del sistema y su objetivo es conseguir la aceptación final del sistema
Según MÉTRICA 3, el objetivo de las pruebas de implantación es:
a) Permitir que el usuario determine, desde el punto de vista de operación, la aceptación del sistema
instalado en su entorno real
b) Verificar la correcta implantación del sistema conforme a las verificaciones del plan de pruebas para el
nivel de pruebas unitarias
c) Verificar si los componentes interactúan correctamente a través de sus interfaces internos y externos
d) Comprobar la integración de los componentes del sistema de información y su interacción con otros
199
Descargar