1 Cambios

1.1 Versión 1.0.0

  • Enunciado original.

2 Objetivos

Son varios los objetivos de esta práctica 0:

  1. Habituarnos al estilo de los enunciados de las prácticas, p.e. observa que al principio del mismo hay una etiqueta de versión: 1.0.0, la cual cambiará si se corrige algún fallo o se modifica por algún motivo el enunciado.

    De este modo siempre sabrás con qué versión del enunciado estas trabajando.

  2. Conocer el formato de entrega de prácticas.

  3. Probar el sistema de entregas en pracdlsi y asegurarnos de que podemos entregar prácticas allí sin problemas.

  4. Aprender a usar algunas herramientas muy útiles a la hora de programar:

  5. Repasar algunos conceptos de Programación I.

  6. Hacer uso de tipos de datos numéricos enteros de tamaño fijo.

  7. Conocer algunas de las nuevas características que nos ofrece C++ (el lenguaje de programación que usaremos en modo C++-17).

  8. Dadas los objetivos anteriores y características especiales de esta práctica no se realizará corrección de la misma.

3 Enunciado

  • Esta práctica consiste en una colección de ejercicios que se describen a continuación.

  • Deben aparecer todos en el mismo fichero fuente, p0.cc, y en dicho fichero no debe haber una función main en el momento de la entrega. Este fichero se debe poder compilar con la orden make y un fichero Makefile.

  • Puedes crear un programa principal de prueba en un fichero aparte (mainp0.cc). Para compilar este programa principal con el fuente p0.cc puedes utilizar esta orden: ‘g++ -g -Wall --std=c++17 mainp0.cc p0.cc -o p0’, pero lo lógico es que prepares un archivo Makefile para que realice compilación separada.

  • Las opciones empleadas en la llamada al compilador son:

    • -g : Está relacionada con el uso del depurador a nivel de código fuente.

    • --std=c++17 : Hace que el compilador funcione en modo C++17.

    • -Wall : Activa la generación de un gran número de advertencias. No son fallos de compilación pero debes prestar atención a lo que te dicen ya que pueden dar lugar a errores en tiempo de ejecución.

  • La corrección de la práctica se realizará probando cada ejercicio por separado, añadiendo para ello una función main (en otro fichero fuente, parecido al mainp0.cc).

4 Herramientas

4.1 Make

  • Make lee su configuración de un archivo de texto normalmente llamado Makefile.
  • En estos archivos encontraremos (entre otras cosas) una colección de reglas:
    objetivo : dependencia1 dependencia2 ... dependenciaN
    (TAB)   orden1
    (TAB)   orden2
    ...
    (TAB)   ordenN
    
  • Los objetivos suelen ser archivos que generamos con las órdenes asociadas a cada regla, pero también pueden ser otros objetivos definidos en el archivo Makefile.
  • Veamos algunos ejemplos.

4.2 Gdb

  • Es un depurador a nivel de código fuente.
  • Nos permite ejecutar nuestra aplicación paso a paso, consultar el valor de variables, tomar el control de ella en cualquier momento.
  • Para que Gdb funcione correctamente necesitamos compilar todo nuestro código con la opción ‘-g’ de g++ o gcc.
  • Funciona en modo texto, pero es muy sencillo.
  • Prueba la interfaz que tiene al lanzarlo con la opción ‘--tui’.
  • Usa gdb así: gdb tu-programa
  • Para salir teclea: q + ENTER o quit + ENTER
  • Disponemos de otros interfaces sobre gdb, p.e.:
    1. Uno en modo gráfico: Nemiver.
    2. Otro en modo texto: cgdb.
  • Veamos un ejemplo.
#include <iostream>
#include <cstring>

using namespace std;

int busca_caracter(char cadena[],char c)
{
  int i, posicion=-1;
  for (i=0;i<strlen(cadena) || cadena[i]!=c;i++) {  // debe ser con '&&'
     cout << cadena[i];
  }
  if (i<strlen(cadena)) posicion=i;
  return posicion;
}

int main()
{
  int p;
  char holamundo[]="hola, mundo";

  cout << "---------------------" << endl;
  p=busca_caracter(holamundo,'o');
  cout << endl << "(o) p=" << p << endl;;

  cout << "---------------------" << endl;
  p=busca_caracter(holamundo,'u');
  cout << endl << "(u) p=" << p << endl;;

  cout << "---------------------" << endl;
  p=busca_caracter(holamundo,'A');
  cout << endl << "(A) p=" << p << endl;;
}

4.3 Valgrind

  • No es una única herramienta, son varias, según lo uses de una manera u otra tienes acceso a una u otra herramienta.

  • Estas herramientas analizan la ejecución de nuestra aplicación.

  • Nosotros lo emplearemos para saber si nuestra aplicación se deja bloques de memoria sin liberar.

  • Es muy útil que compilemos nuestro código con la opción ‘-g’.

  • Ejecuta valgrind con la opción ‘--help’ para saber todas las opciones con las que lo puedes invocar.

  • La forma más sencilla de emplearlo es así: valgrind tu-programa

  • Veamos algunos ejemplos.

int main(int argc, char *argv[])
{
  int* p;
  int* q = new int;

  //*p = 3;
  return 0;
}

5 Ayuda de C++

5.1 Compilador de C++

  • El compilador de C++ del proyecto GNU se llama: g++.
  • Los ficheros de C++ suelen tener la extensión .cc o .cpp o .C. Nosotros emplearemos siempre .cc.
  • Los archivos de cabecera propios de C++ no tienen extensión .h. Si en algún momento necesitas incluir un archivo de cabecera de C, estos se llaman quitándoles la extensión .h y anteponiéndoles el prefijo c, p.e. math.h pasa a llamarse cmath:
    #include <cmath>
    
  • Lo usaremos con la opción --std=c++17 para asegurarnos que trabajamos en modo C++17.

5.2 Tipo bool

C++ define el tipo bool en el lenguaje. Son palabras reservadas los valores true y false.

5.3 Entrada/salida en C++

  • En C++ es recomendable usar la E/S proporcionada por los iostreams.

  • Para poder emplear cout debes incluir la cabecera <iostream> y , de momento, llamarlo como en el ejemplo anterior:

    std::cout << "n = " << n << std::endl;
    
  • O emplear la construcción using namespace std y así poder emplear cout sin el prefijo:

using namespace std;

// Fíjate que ahora ya no hay prefijo 'std::'
cout << "n = " << n << endl;

5.4 Mini tutorial sobre la clase std::string de C++

  1. Para usar el tipo string debemos incluir la cabecera <string>, de lo contrario es posible que tengamos un error de compilación.

  2. Las cadenas de C++ se pueden concatenar con el operador +, p.e.: std::string s = "hola"; s = s + " mundo";

  3. Las cadenas de tipo std::string no acaban en un \0. Si necesitas una cadena de C (acabada en \0) puedes usar el método std::string::c_str así: const char* cstr = s.c_str();.

  4. En esta práctica-0 (salvo en los lugares donde explícitamente se usen cadenas de C) puedes emplear cadenas de C++ si las necesitas.

#include<string>
using namespace std; // Para no usar std::string

// Creación de una cadena  vacía, "")
string s;
// Creación de una cadena  no vacía)
string s2 = "Programación-II";

// Lectura hasta el final de la línea
getline(cin,s);
// Mostrar por pantalla
cout << s << endl;

s="hola"; // Asignación

// Recorrido, s.length() es la longitud
for (uint i=0u; i < s.length(); i++)
   cout << s[i];  // imprimir un carácter

s[3] = 'A';       // Asignación de un carácter
s = s + ',';      // concatena un carácter  "holA,"
s = s + " mundo"; // concatena una cadena "holA, mundo"
if (s <= "hola")  // Comparacion de strings
if (s == "adios")
...

5.5 Mini tutorial sobre la clase std::vector de C++

  1. Para usar el tipo vector debemos incluir la cabecera <vector>, de lo contrario es posible que tengamos un error de compilación.

  2. Para declarar una variable de tipo vector debemos indicar también el tipo de los elementos del vector, esto se hace poniendo ese tipo entre ángulos, p.e: std::vector<float> fv; // vector de numeros reales.

  3. Los vectores de tipo std::vector:

    • Son dinámicos, pueden crecer y decrecer en tiempo de ejecución.

    • Saben cuantas componentes tienen, para ello se emplea la función size que, al igual que con el tipo string se utiliza con la notación: mi_vector.size().

    • Una de las maneras de añadir elementos a un vector por el final es usando la función push_back, como puedes ver en el ejemplo siguiente.

#include <cstdint>
#include <iostream>
#include <vector>
#include <string>

void print_int8_vector(const std::vector<int8_t> &v) {
  for (auto &e : v) {
    std::cout << "· " << (int)e << '\n';
  }
}

void print_string_vector(const std::vector<std::string> &v) {
  for (auto &e : v) {
    std::cout << "· " << e << '\n';
  }
}

int main(int argc, char *argv[]) {
  std::vector<int8_t> iv;
  std::vector<std::string> sv;

  for (int8_t i = 0; i < 10; i++)
    iv.push_back(i);

  for (int8_t i = 0; i < 15; i++) {
    std::string s = "cadena-";
    s += std::to_string(i);
    sv.push_back(s);
  }

  std::cout << "vector<int8>: elementos: " << iv.size() << '\n';
  print_int8_vector(iv);

  std::cout << "vector<string>: elementos: " << sv.size() << '\n';
  print_string_vector(sv);

  return 0;
}

5.6 Mini tutorial sobre memoria dinámica en C++

  • Tal y como hemos visto en clase de teoría la gestión de la memoria dinámica (reserva y liberación) corre a cargo del lenguaje y no de funciones de biblioteca como en C ( malloc y free ).

  • La reserva de memoria la realizan los operadores new y new[] mientras que la liberación la hacen los operadores delete y delete[], es decir, cuando reservas memoria con new debes liberarla posteriormente con delete y cuando lo haces con new[] debes liberarla con delete[].

  • Ejemplos:

// iv es un vector de 4 enteros, empleamos 'new[]'
int n = 4;
int* iv = new int[n];
...
// Liberamos la memoria con 'delete[]', los '[]' van vacios
delete[] iv;

// Reservamos un puntero a un caracter, empleamos 'new'
char* cp = new char;
...
// Liberamos la memoria, empleamos 'delete'
delete cp;
  • El lenguaje incorpora el literal nullptr que representa un puntero nulo de cualquier tipo. En C++ no debemos emplear la macro NULL usada en lenguaje C.
int* v = nullptr;

6 Ejercicio

  • En este ejercicio puedes crear las funciones auxiliares que necesites. Para ello debes definirlas en el archivo p0.cc, recuerda que los archivos de cabecera entregados como p.e. p0.h no deben ser modificados.

6.1 Extracción de palabras de una frase.

  • Nos piden que devolvamos las palabras quen componen una frase. Una cadena vacía no se considera una palabra.

  • La frase se representa como un dato de tipo std::string y el separador entre palabras es un carácter, un dato de tipo char.

  • Para ello nos piden que escribamos el código de una función con el siguiente prototipo

    std::vector<std::string> split_on(const std::string& s, char c);
    
  • Esta función:

    1. Devuelve como resultado un vector de cadenas en el que cada componente del mismo es una palabra de la frase original.

    2. Si la frase original es una cadena vacía, devuelve un vector con cero componentes. El carácter separador nunca será un carácter vacío.

    3. Si la frase original no contiene ningún carácter separador se considera que toda la frase es una única palabra.

7 División del código fuente en archivos

  • Tendremos un único archivo de cabecera (p0.h) con los prototipos de las funciones además de los #include necesarios para que no de errores de compilación.

  • Este archivo hará uso de la guarda para protegerlo de su inclusión más de una vez que hemos visto en el tema-1.

  • Tendremos un único archivo de implementación (p0.cc, trabajamos en C++) con la implementación de todas las funciones además de los #include necesarios para que no de errores de compilación (también incluirá a p0.h). Este archivo no contendrá ningún programa principal (main).

  • Para poder probar tus funciones puedes crearte la función main que quieras en otro archivo, p.e. mainp0.cc (el cual incluirá a p0.h) y compilarlo y enlazarlo con el anterior así: g++ -g -Wall --std=c++17 mainp0.cc p0.cc -o p0. No es necesario entregarlo, si se hace no pasa nada.

  • También puedes emplear el archivo Makefile para compilar y enlazar. Recuerda que lo tienes en la carpeta de la práctica.

  • Todos los archivos `.h' y `.cc' se encontrarán dentro de una carpeta llamada irp2-p0.

8 Requisitos técnicos

  • Requisitos que tiene que cumplir este trabajo práctico para considerarse válido y ser evaluado (si no se cumple alguno de los requisitos la calificación será cero):

  • El archivo entregado se llama irp2-p0.tgz (todo en minúsculas), no es necesario entregar ningún programa principal, sólo son necesarios tus archivos `.h' y `.cc' en una carpeta llamada irp2-p0. A su vez, esta carpeta estará comprimida en el fichero llamado irp2-p0.tgz. Este archivo lo puedes crear así en el terminal:

    tar cfz irp2-p0.tgz irp2-p0

  • También puedes emplear el archivo Makefile que tienes en la carpeta de la práctica para generar el fichero de la entrega tal y como hemos visto en clase de prácticas.

  • Al descomprimir el archivo irp2-p0.tgz se crea un directorio de nombre irp2-p0 (todo en minúsculas).

  • Dentro del directorio irp2-p0 estará al menos el archivo p0.cc (todo en minúsculas).

    Recuerda que el fichero ‘p0.hno se debe modificar, de lo contrario tus ficheros ‘.cc’ no compilarán con el corrector. No es necesario entregarlo, si se hace no pasa nada.

  • Las clases, métodos y funciones implementados se llaman como se indica en el enunciado (respetando en todo caso el uso de mayúsculas y minúsculas). También es imprescindible respetar estrictamente los textos y los formatos de salida que se indican en este enunciado.

  • Al principio de todos los ficheros fuente (`.h’ y `.cc’) entregados y escritos por ti se debe incluir un comentario con el nombre y el NIF (o equivalente) de la persona que entrega la práctica, como en el siguiente ejemplo:

    // NIF: 12345678-Z
    // NOMBRE: GARCIA PEREZ, LAURA
    
  • Un error de compilación/enlace implicará un cero en esa parte de la práctica.

    Aunque esta práctica no cuenta para la nota final, su entrega es obligatoria y sirve para comprobar que vuestro usuario para la entrega de prácticas posteriores funciona correctamente, además de permitirte evaluar cuál es tu nivel real de conocimientos de programación adquiridos en Programación-I. Si tienes cualquier problema con su entrega comunícaselo a tu profesor lo antes posible.

  • Lugar y fecha de entrega : Recuerda que las entregas se realizan siempre en (y sólo en) https://pracdlsi.dlsi.ua.es en las fechas y condiciones allí publicadas. Puedes entregar la práctica tantas veces como quieras, sólo se corregirá la última entrega (las anteriores no se borran). El usuario y contraseña para entregar prácticas es el mismo que se utiliza en UACloud.

9 Detección de plagios/copias

  • En el Grado en Ingeniería Robótica, la Programación es una materia muy importante. La manera más efectiva de aprender a programar es programando y, por tanto, haciendo las prácticas correspondientes además de otros programas (más sencillos o más complicados, eso da igual).

  • Tratar de aprobar las asignaturas de programación sin programar (copiando) es complicado y obviamente, te planteará problemas para seguir otras asignaturas así como en tu futura vida laboral.

  • En una asignatura como Programación-II es difícil que alguien que no ha hecho las prácticas supere los exámenes de teoría o el de recuperación de prácticas.

  • IMPORTANTE:

    • Cada práctica debe ser un trabajo original de la persona que la entrega.

    • En caso de detectarse indicios de copia de una o más prácticas se tomarán las medidas disciplinarias correspondientes, informando a las direcciones de Departamento y de la EPS por si hubiera lugar a otras medidas disciplinarias adicionales.