Files
etsii-vault/TERCERO/SS/SS 24-25.md
2024-10-17 12:20:31 +02:00

13 KiB

TEMA 1: Introducción

Aunque la sintaxis de Java derive de C, NO son tan similares. Hay que entender el ámbito del lenguaje: por ejemplo C (Software de Sistemas, sustitución de assembly, etc) o Java (aplicaciones, etc). Existen palabras reservadas con más de un uso (static o const).

C++ amplia el ámbito de C

1. Introducción y características

Estandarización

Los estándares más usados para C y C++ son el 99 y 98 respectivamente.

#include "archivo.h"
// "pega" archivo.h y sigue compilando
void main() {

}

Dependiendo del compilador se pueden especificar directivas como #pragma once para solo incluir el archivo una vez.

⚠ Incongruencia

C/C++ es portable para aplicaciones pero para Software de Sistemas NO \rightarrow siempre habrá problemas de portabilidad. que es portable desde el punto de vista de los 80:

  • Hay compilador C para cualquier arquitectura.
  • Si sé C, puedo programar en cualquier arquitectura.
  • El código fuente es compatible.

Nivel de abstracción

Varía de un lenguaje a otro (C tiene menor nivel de abstracción que los lenguajes modernos). Las estructuras del lenguaje y funcionamiento general son lo más parecido al código máquina. C++ tiene mayor nivel de abstracción pero aumenta la sobrecarga.

Runtime, librerías, frameworks

Runtime es el código que añade el compilador, que no está asociado al código de usuario sino al lenguaje. En C es mínimo, por eso se prefiere para sistemas empotrados.

Relación C/C++

Compatible C -> C++. Las extensiones mas importantes de C++ son POO, tipado, librerías... La mayor parte de compiladores son duales. La extensión del archivo determina si hay que compilar C (.c, .h) o C++ (.cpp, .hpp ó .hh).

Java VS C/C++

Java C C++
El programa se compone de clases (propiedades y métodos). El programa es un conjunto de datos y funciones, no existen clases.
Para ejecutar un programa, se empieza por el punto de entrada (main) y se van instanciando, modificando, destruyendo objetos. Para ejecutar un programa, se empieza por el punto de entrada (main), se inicializan las variables globales y se van creando, modificando variables y ejecutando funciones.
Base de programación: Objetos No hay base de programación
La unidad de compilación es el fichero (o clase pública). La unidad de compilación es el fichero.
Básicamente, típico lenguaje orientado a objetos Básicamente, típico lenguaje imperativo (procedural)

2. Programar en C/C++

A diferencia de Java, que declarar y definir se realiza a la vez:

public class Persona {
	private String name;

	...

	public String getName() {
		return this.name;
	}
}

En C/C++ se debe declarar primero la función para que al compilar, la primera pasada la "registre", y luego a la hora de ejecutar, se pueda ejecutar dicha función.

void funcion(); // declaración

int main()
{
	funcion();
	return 0;
}

void funcion() 
{
	// definición
}

En C/C++ se pueden usar sentencias #include para "importar" archivos que contienen declaraciones (de variables, funciones, etc)

Tipos de datos

Variables:

  • char = 8 bits (-128, 127)
    • unsigned char = 8 bits (0, 255)
  • short int = 16 bits
  • int = 32 bits
  • long int = 32 bits
  • long long int = 64 bits En cuanto a punteros:
  • <tipo>* 32 bits (x86) 64 bits (x64)

TEMA 2: Depuración

1. Introducción

El depurador es una herramienta para diseccionar el código y poder analizarlo para arreglar los errores. En ingeniería se sigue la regla "Para matar moscas mejor usar un matamoscas que un cañón. "

2. Tipos de error

Los errores son comportamientos no deseados del código causados por la inexperiencia (al principio) o falta de atención del programador.

Errores sintácticos

Son los errores más fáciles de detectar y corregir. El compilador suele detectar todos y decirte dónde están y qué los causa (pero no siempre). Suelen provocar más errores en cadena.

Errores lógicos/semánticos

Más difíciles de detectar (ninguna herramienta los detecta del todo bien) pero relativamente fáciles de corregir.

Errores de diseño

Todos los componentes del sistema funcionan pero el sistema no hace lo esperado. Problemas asociados a la Ing. del Software. Muy difíciles de detectar y costosos de arreglar.

3. Fases del tratamiento de errores

Hay una serie de pasos comunes (pero no infalibles):

  • Detectar el problema
  • Recolectar datos
  • Diagnóstico
  • Reparación
  • Verificación

Detección del problema

Hay varias herramientas:

  • Warnings: muchas veces son la posible causa de errores futuros. Se deberían arreglar siempre que se pueda, pero OJO: algunas veces son falsos positivos.

Controlar la ejecución

Los computadores actuales no son capaces de determinar la información importante para dar con un error. Para eso se usa el depurador, que aunque no te da la causa del error como tal, si que se puede descubrir "pidiéndole" la información adecuada.

Inspección de estado

Contexto: datos accesibles en medio de la ejecución (variables locales o globales, parámetros...).

Pila de llamadas y marco de pila

La pila de llamadas es un bloque de memoria que el S.O. asigna al programa para llamadas y retornos de funciones. Si este se llena, se produce un "stack overflow". El marco de pila es básicamente la región de la pila marcada por los punteros ESP (límite superior de la pila) y EBP (inicio de marco de pila), que pertenece a una función.

!Pasted image 20241003113528.png

TEMA 3: Generación de programas

1. Introducción

Es conveniente conocer como se genera el código:

  • qué es un programa ejecutable
  • cómo se trata desde el punto de vista del SO y del IDE y qué elementos intervienen en la generación del ejecutable:
  • compiladores y linkers
  • archivos objeto, runtime, bibliotecas estáticas y dinámicas

2. Archivos ejecutables

!Pasted image 20241010091834.png El ejecutable se genera a partir de:

  • el código fuente
  • el proceso de compilación La gran parte del ejecutable la añade el IDE y no es generada del código fuente.

3. Transformación de formatos

!Pasted image 20241010092102.png Hay al menos tres formatos:

  • Código fuente de alto nivel: creado por el programador y compilado por el compilador del lenguaje X.
  • Código objeto: creado por el compilador, procesado por el linker.
  • Ejecutable: creado por el linker, procesado por el cargador del SO.

4. Tipos de archivo ejecutable

Hay tantos tipos de ejecutable como mecanismos de carga distintos. Los detalles del mecanismo de carga dependen del SO.

Imágenes binarias

  • El ejecutable es un mapa de memoria.
  • Es el formato más simple, sin información de carga/reubicación.
  • En el archivo están las zonas de memoria que usa el programa (el SO lo carga y se ejecuta).
  • Se usan en computadores donde el SO o es muy simple o no existe (embedded).

Ejecutables con información de carga/reubicación

  • El ejecutable también es un mapa de memoria, pero más complejo
  • Hay varios formatos muy utilizados:
    • COFF (Common Object File Format): UNIX y Windows (sólo para archivos .obj y .lib).
    • PE (Portable Executable): variante del COFF para Windows con código ejecutable (.exe y .dll).
    • ELF (Executable and Linkable Format): variante del COFF usado en UNIX.

¿Qué contiene un ejecutable?

  • Descripción de los recursos iniciales del programa
    • código
    • datos inicializados
    • atributos estáticos
    • constantes
    • tamaño de datos no inicializados y pila
  • Instrucción inicial donde comienza el código
  • Tablas de reubicación de datos y código
  • Información de debug (opcional)

Las secciones mínimas son: !Pasted image 20241010115957.png

5. Generación del ejecutable: enlace (link)

Es el proceso final de la construcción del ejecutable. Hay dos tipos:

  • Estático: posible en todos los IDE.
  • Dinámico: posible en algunos SO/IDE. En el dinámico, el SO completa parte del enlace en la carga del programa y/o durante su ejecución. !Pasted image 20241010120358.png Al linker se le da ese nombre ya que el .exe es el resultado de unir secciones de cada uno de los .obj/lib. !Pasted image 20241017112904.png Una vez construidas las secciones, el linker resuelve las referencias pendientes (relocalización de código). Para este proceso, el linker necesita saber qué instrucciones quedan por rellenar, que lo encuentra en la tabla de reubicación del archivo objeto. !Pasted image 20241017113303.png

6. Bibliotecas

Una biblioteca es básicamente un contenedor de archivos objeto. !Pasted image 20241017113556.png

Enlace dinámico

A diferencia del enlace estático, donde el ejecutable se construye totalmente durante el linkado, en el enlace dinámico algunas bibliotecas se enlazan cuando el ejecutable se carga en memoria (por lo que se encarga el SO).

  • El principal objetivo de esto es ahorrar espacio en disco y memoria.
  • En linkado estático, si una función (por ejemplo printf) está en una biblioteca estática, printfestá en el ejecutable y en todos los ejecutables del disco que usen la función. Si varios de los ejecutables están en ejecución, todos tendrán una copia exactamente igual de printf.
  • El linkado dinámico trata de minimizar esta situación almacenando printf (al igual que todas las funciones de la biblioteca estándar de C/C++) en un archivo DLL, y luego sigue estos pasos:
    • El SO carga el programa que usa funciones en DLL, para esto localiza todos los .dll que necesita.
      • Si falta alguno, para la carga
      • Si están todos, sigue la carga
    • Si continúa la carga, para cada función en DLL del programa comprueba si hay algún programa usando la función (usando como ejemplo printf):
      • Si no lo hay, carga en memoria el módulo donde está printf en el DLL y la enlaza.
      • Si lo hay, localiza la función en memoria y la enlaza. Además crea una nueva sección de datos para printf en el espacio de memoria virtual del nuevo programa.
    • Al cerrarse todos los programas que usan ese código, se descarga de memoria. El objetivo es que todo programa en ejecución comparta el código de printf.obj pero que cada uno tenga una copia privada de los datos definidos en este.