tete du loic

 Loïc YON [KIUX]

  • Enseignant-chercheur
  • Référent Formation Continue
  • Responsable des contrats pros ingénieur
  • Référent entrepreneuriat
  • Responsable de la filière F2 ingénieur
  • Secouriste Sauveteur du Travail
mail
loic.yon@isima.fr
phone
(+33 / 0) 4 73 40 50 42
location_on
ISIMA
  • twitter
  • linkedin
  • viadeo

[++C] TP 1

Read in English

Date de première publication : 2013/06/10

Ce premier TP est là pour vous montrer les spécificités de C++ par rapport au C. Nous n'abordons pas encore la notion d'objet.

Nous utilisons un compilateur "récent" qui supporte nativement la norme 2014 (2011 améliorée).

Du C au C++

"Hello world"

Voici votre premier programme C++ :

#include <iostream>

int main(int argc, char ** argv) {
  int i;

  for(i=0; i< 120; ++i)
    std::cout << "Bonjour les ZZ" << 2 << std::endl;

  return 0;
}

Si l'on compare avec un programme C :

Le reste est similaire au C. Effectuez les opérations suivantes :

Tous les warnings doivent être pris en compte, comme en C.

En C++, il est relativement courant de déclarer les variables de boucle dans la structure elle-même (attention, cela peut aller à l'encontre d'un guide de style) :


  for(int i=0; i< 120; ++i)
    std::cout << "Bonjour les ZZ" << 2 << std::endl;

Cette manière de faire peut aller à l'encontre d'un guide de style et certains compilateurs (notamment MS C++) peuvent avoir une gestion par défaut de la durée de vie de telles variables)

Entrée standard...

#include <iostream>
#include <string>

int main(int, char **) {  // parametres muets
  std::string prenom;     // type chaines de caracteres"
  int age;

  std::cout << "Quel est votre prénom ?" << std::endl;
  std::cin  >> prenom;
  std::cout << "Quel est votre age ?" << std::endl;
  std::cin  >> age ;
  std::cout << "Bonjour "<< prenom << std::endl;

  return 0;
}

Demander également le nom de la personne et affichez successivement le nom et le prenom, puis la concatenation du nom, du prénom séparé par un :

... et chaînes de caractères

std::string est un type qui représente la chaîne de caractères en C++. Vous ne devriez plus JAMAIS avoir besoin (sauf TP à vocation casse-pied) de faire appel au char *. La fonction main() vient du C, c'est pour cela que son prototype n'utilise pas std::string mais char *

Manipuler les "objets" standards avec le préfixe std:: peut être un peu pénible MAIS c'est obligatoire si vous avez TP avec Alexis et Jeremy. On peut ajouter une instruction qui permet de s'en débarasser : using namespace std;(C'est le U word) ou using std::cout; (c'est mieux).

Petits exercices sur les chaînes

Tableaux et constantes

Vous pouvez compiler ce programme en C et en C++, en mettant de côté les appels à std::cout.

Si vous souhaitez conserver l'affichage à l'écran, il est possible d'utiliser la fonction printf car tout programme C++ peut faire appel aux bibliothèques C, seuls les noms des fichiers entête changent : par exemple, stdlib.h devient cstdlib.

#include <iostream>

using std::cout;
using std::endl;

/* on peut utiliser le mot clé const pour définir la taille d'un tableau statique en C++ */
/* Jamais de #define pour cela */
const int TAILLE = 10;

int main(int, char **) {
  int tab[TAILLE];
  
  for (int i=0; i < TAILLE; ++i) {
    tab[i] = i %2;
    cout << tab[i] << " ";
  }

  cout << endl;

  return TAILLE;
}

Surcharge de fonction

Placer ce code dans un fichier d'extension .c et compiler avec gcc. Observer les erreurs.

#include <stdio.h>

void afficher(int a) {
  printf("%d", a);
}

void afficher(double a) {
  printf("%lf", a);
}

int main(int, char **) {
  afficher(1);
  afficher(2.0);

  return 0;
}

Si le fichier est d'extension .cpp et compilé avec gcc ou bien si le fichier d'extension .c est compilé avec g++, vous n'aurez pas ces messages d'erreurs !

Chaines de caractères

Une fiche sur les chaînes de caractères est disponible.

Exécutez le code suivant et regardez ce que cela donne avec les différentes versions de s :

#include <iostream>

int main() {

  char s[10];
  // std::string s;
  // char *      s;

  std::cin >> s;

  std::cout << "#" << s << "#" << std::endl;
  for (int i = 0; i< 10; ++i)
    std::cout << "@" << s[i] << "@" << (int)s[i] << "@" << std::endl;

  return 0;
}

Exécutez avec valgrind pour relever les éventuelles erreurs de contexte (vous pouvez également résoudre ces erreurs de contexte).

Pour exécuter avec valgrind, il faut que le code à profiler ait été compilé avec l'option -g

Références

En C, il y a deux manières d'accéder à une variable : par sa valeur ou par son adresse en la déréférençant. Le C++ ajoute une nouvelle manière d'accéder à une variable en la référençant, c'est-à-dire en en faisant un alias.


int main(int, char**){ 
   int  a = 5;
   int &r = a;

   std::cout << a << " " << r << std::endl;
   std::cin  >> a;
   std::cout << a << " " << r << std::endl;
   std::cin  >> r;
   std::cout << a << " " << r << std::endl;
}

Premières manipulations

void fonction1(int a) {
  std::cout << &a << std::endl;
}

void fonction2(int& a) {
  std::cout << &a << std::endl;
}

int main(int, char **) {
  int age = 45;

  std::cout << &age << std::endl;
  fonction1(age);
  fonction2(age);

  return 0;
}

La référence n'est pas un type différenciant pour le compilateur pour déterminer la surcharge

Échange de variables

Essayer le code suivant :

int  a = 3;
int  b = a;
int& c = a;

std::cout << a << " " << b << " " << c << std::endl;
b = 4;
std::cout << a << " " << b << " " << c << std::endl;
c = 5;
std::cout << a << " " << b << " " << c << std::endl;
a = 6;
std::cout << a << " " << b << " " << c << std::endl;

Écrire une fonction qui permet l'échange de deux variables de type int

Comparer la facilité d'écriture et de compréhension avec les références.

Une référence référence ...


int main(int, char**){ 
   int  a;
   int &r = a;

   std::cout << a << " " << r << std::endl;
}

Que se passe-t-il avec ce bout de code ? Comment détecter l'erreur à tous les coups ?

mais pas n'importe comment


int main(int, char**){ 
   int  a = 1;
   int  b = 2;
   int &r = a;

   std::cout << a << " " << b << " " << r << std::endl;

   r = b;
   std::cout << a << " " << b << " " << r << std::endl;

   b = 4;
   std::cout << a << " " << b << " " << r << std::endl;

   r = 5;
   std::cout << a << " " << b << " " << r << std::endl;
}

Une référence "constante"

Que se passe-t-il avec le code suivant ?


int main(int, char**){ 
   int        a = 1;
   const int &r = a;

   std::cout << a << " " << r << std::endl;

   a = 2;
   std::cout << a << " " << r << std::endl;

   r=3;
   std::cout << a << " " << r << std::endl;
}

La référence est dite "constante", ce qui est un abus de langage. En fait, la variable référencée est considérée comme constante : accessible en lecture mais pas en écriture.

Une référence sur une constante

Que se passe-t-il avec le code suivant ?


int main(int, char**){ 
   const int a = 1;
   int &r      = a;

   std::cout << a << " " << r << std::endl;

Il faut une référence "constante" pour référencer la constante

Corriger l'erreur !

Pointeurs et allocation mémoire

En C++, on n'utilise plus les fonctions malloc() et free() mais les opérateurs new et delete. L'opérateur delete existe en deux versions suivant que le pointeur est associé à une zone mémoire simple ou une zone mémoire contiguë.

Pour initialiser un pointeur, le C++ introduit une valeur spéciale nullptr :

int   a = 4;
int * p = nullptr;

p = &a;
std::cout << *p << " " << p;

Nous verrons plus tard comment gérer une allocation mémoire impossible

Exécutez valgrind sur les deux exemples suivants et corrigez-les en décommentant la libération mémoire ! Voici le premier :

int main(int, char**) {
   int * p = new int;

   *p = 3;
   cout << *p << endl;

   // delete p;

   return 0;
}

Voici le second :

int main(int, char**) {
   const int TAILLE = 500;

   int * p = new int[TAILLE];

   for(auto i = 0; i< TAILLE; ++i ) p[i] = i;
   for(auto i = 0; i< TAILLE; ++i ) cout << p[i] << endl;

   // delete p;      
   // delete [] p;

   return 0;
}

Que se passe-t-il dans ce dernier exemple si vous oubliez les crochets ?

Notez la présence du mot-clé auto dont le sens a été modifié depuis le C qui permet de demander au compilateur de déduire le bon type de variable ! Ne surutilisez pas cet opérateur car cela peut nuire à la lisibilité du code.

C++, un langage objet ?

Finalement, on va faire un tout petit peu d'objet... Compiler et exécuter le programme suivant :

#include <iostream>

class Exemple {
 public:
  void afficher() {
     std::cout << "hello" << std::endl;
  }
};

int main(int, char **) {
  
  Exemple e;
  
  e.afficher();

  return 0;
}

Regarder les erreurs obtenues si vous faites les choses suivantes :