Outils d'utilisateurs

Outils du Site


hello_world

Ceci est une ancienne révision du document !


Todo : tabulation, retour à la ligne, quoted. Std::endl. Inteprétation de “1+2” ou std::cout « “std::endl”; comme une chaîne et pas comme une expression à évaluer ou un code C++. Caractère spéciaux sous forme hexa. Accents avec coliru = ok, mais cela ne sera pas toujours le cas

Le programme "hello world"

Dans le chapitre précédent, vous avez vu la structure de base d'un programme C++ et un aperçu du processus de compilation. Cependant, le code présenté ne faisait rien, ce n'était pas très intéressant. Dans ce chapitre, vous allez voir comment afficher un message.

Pour illustrer cette fonctionnalité en C++, nous allons prendre le programme “hello world” comme exemple. Ce programme permet simplement d'afficher le message “hello, world!”. Il est traditionnellement utilisé pour montrer la syntaxe de base d'un langage informatique, ce qui explique qu'il possède son propre nom. Vous pouvez voir sur Wikipédia ce programme dans différents langages : Wikipédia.

Le programme hello world en C++ est assez proche du programme minimal présenté dans le chapitre précédent. Vous pouvez ouvrir ce code dans Coliru et copier-coller le code dans l'éditeur de votre choix.

main.cpp
#include <iostream>
 
int main() {
    std::cout << "Hello, world!" << std::endl;
}

Par rapport au code minimal, vous pouvez voir que l'on a ajouté deux lignes. La première ligne, qui contient la directive #include, permet de spécifier des fonctionnalités utilisées par le programme. “iostream” fournit les fonctionnalités de base pour afficher un message et récupérer les textes saisies par l'utilisateur.

La quatrième ligne, commençant par std::cout, permet l'affichage proprement dit. On voit sans problème le message à afficher “hello, world!” en clair dans le code. Vous pouvez vous amuser à changer le texte et voir ce que cela donne.

Ces deux lignes de code permettent d'utiliser le flux de sortie std::cout de la bibliothèque standard.

La bibliothèque standard

Lorsque l'on parle du C++, il faut en fait distinguer deux choses : le langage C++ et la bibliothèque standard. Le langage C++ proprement-dit propose uniquement les fonctionnalités fondamentales indispensables pour écrire un programme. La conséquence est que de nombreuses fonctionnalités ne sont pas intégrées directement dans le langage (même l'affichage d'un message ne fait pas partie du langage).

Heureusement, lorsqu'une fonctionnalité n'existe pas dans le langage, cela ne veut pas dire que l'on ne peut pas utiliser cette fonctionnalité. Il est possible d'écrire des bibliothèques, qui fournissent de nouvelles fonctionnalités utilisables en C++ (ou dans d'autre langages, mais cela sort du cadre de ce cours).

Cela veut dire aussi que si vous créer un programme qui propose des fonctionnalités intéressantes, vous pouvez également créer une bibliothèque pour que d'autres développeurs utilisent vos fonctionnalités (ou que vous puissiez vous même utiliser ces fonctionnalités dans plusieurs de vos programmes. La création d'une bibliothèque sera vu par la suite.

La bibliothèque standard fait partie intégrante du C++, il est indispensable d'apprendre à l'utiliser en même temps que le langage, l'un ne va pas sans l'autre.

Pour utiliser une fonctionnalité de la bibliothèque standard, il faut dans un premier temps le spécifier au compilateur, en utilisant la directive de pré-processeur #include. L'une des syntaxes de cette directive est la suivante (il en existe d'autres, mais qui ne seront pas utilisée avec la bibliothèque standard) :

#include <nom_fichier>

Les directives de pré-processeur

Une directive de pré-processeur permet de paramétrer le comportement du pré-processeur lors de la compilation. Il existe différentes directives, vous en verrez plusieurs dans ce cours. Une directive s'écrit toujours avec un dièse suivi de la directive et d'éventuels paramètres optionnels.

Il n'est pas possible d'expliquer le fonctionnement de la directive #include sans expliquer avant le fonctionnement en détail du pré-processeur. Cela sera vu dans un chapitre dédié à la compilation en profondeur.

Pour afficher un message, on utilise un objet particulier de la bibliothèque standard, nommé std::cout. Si vous regardez dans la documentation, vous voyez au début de la page qu'il est écrit : “Defined in header <iostream>”.

Cela vous indique quel fichier il faut inclure pour utiliser std::cout : le fichier iostream :

#include <iostream>

“iostream” correspond à Input/Output stream, ce qui signifie “flux d'entrée et sortie”. “Entrée” et “Sortie” doivent être compris du point de vue du programme : “entrée” d'information depuis l'extérieur vers l'intérieur du programme (par exemple saisie d'un texte par l'utilisateur ou la lecture d'un fichier) et “sortie” d'information depuis le programme vers l'extérieur (par exemple afficher un message à l'écran ou enregistrer dans un fichier). Vous verrez juste en dessous pourquoi on parle de “flux”.

Lorsque vous utiliserez une fonctionnalité de la bibliothèque standard que vous ne connaissez pas, vous pourrez de la même manière aller rechercher dans la documentation quel fichier inclure.

L'espace de nom std

En C++, chaque chose doit avoir un nom unique, pour permettre au compilateur de les identifier correctement. Donner un nom n'est pas très compliqué, vous verrez par la suite les quelques règles à respecter. Lorsque l'on a un petit programme de quelques centaines ou milliers de ligne, cela pose pas trop de problème pour trouver des noms uniques. Mais dans le cas d'un programme de plus grande taille ou utilisant différentes bibliothèques, cela peut devenir très compliqué.

Pour éviter cette contrainte, le C++ permet de regrouper les noms dans un espace dédié : les espaces de noms (namespace). En créant un espace de noms, vous évitez les conflits entre les noms, ce qui peut simplifier vos codes. La bibliothèque standard utilise un espace de noms appelé std. Vous apprendrez par la suite à créer des espaces de noms, mais pour l'instant, voyons comment utiliser l'espace de noms de la bibliothèque standard.

Pour utiliser l'objet cout de la bibliothèque standard, il faut donc préciser que celui-ci provient de l'espace de noms std. Plusieurs solutions sont possibles, selon le contexte. Premièrement, vous pouvez déclarer l'espace de noms std à chaque utilisation d'une fonctionnalité de la bibliothèque standard, en utilisant l'opérateur :: (comme vous l'avez vu dans les codes précédents) :

main.cpp
#include <iostream>
 
int main() {
    std::cout << "Hello, world!" << std::endl;
}

Dans ce cas, il faut faire précéder chaque utilisation de cout et de endl avec l'espace de noms.

La deuxième solution est de déclarer que vous allez utiliser une fonctionnalité d'un espace de noms en utilisant le mot-clé using. Lorsque le compilateur rencontre ensuite cout, il saura qu'il faut utiliser l'objet cout :

main.cpp
#include <iostream>
using std::cout;
 
int main() {
    cout << "Hello, world!" << std::endl;
}

Vous pouvez remarquer ici que seule l'objet cout est déclaré en utilisant using. endl n'étant pas déclaré de cette manière, il faut l'écrire en utilisant la première syntaxe.

Pour terminer, il est possible d'activer un espace de noms globalement, en utilisant using namespace.

main.cpp
#include <iostream>
using namespace std;
 
int main() {
    cout << "Hello, world!" << endl;
}

Vous remarquez ici qu'il n'est plus nécessaire d'écrire std:: devant cout et endl (ou n'importe quelle autre fonctionnalités de la bibliothèque standard).

Cette syntaxe semble intéressante, puisque cela fait gagner du temps. Cependant, cela signifie que l'on perd l'intérêt des espaces de noms : si deux espaces de noms proposent le même identifiant, il y aura un conflit. Le message d'erreur généré par le compilateur ne sera pas forcement explicite, puisqu'il ne détectera pas le conflit d'espace de noms. Les erreurs générés seront du type “déclaration ambiguë” ou “déclaration multiple”.

Il est donc préférable de limiter l'utilisation de cette troisième syntaxe et préférer les deux premières.

Les flux standards

Si vous avez regardé un peu la documentation de cout, vous avez peut-être remarqué qu'il existe d'autres flux de sortie :

  • cout et wcout pour les messages standard ;
  • cerr et wcerr pour les messages d'erreur ;
  • clog et wclog pour les messages de log.

Par défaut, ces flux s'affichent tous dans le terminal, vous pouvez utiliser n'importe quel flux. Le programme suivant :

main.cpp
#include <iostream>
 
int main() {
    std::cout << "le flux cout" << std::endl;
    std::wcout << "le flux wcout" << std::endl;
    std::cerr << "le flux cerr" << std::endl;
    std::wcerr << "le flux wcerr" << std::endl;
    std::clog << "le flux clog" << std::endl;
    std::wclog << "le flux wclog" << std::endl;
}

affiche :

le flux cout
le flux wcout
le flux cerr
le flux wcerr
le flux clog
le flux wclog

Cependant, il est possible de différentier ces sorties et d'appliquer un traitement différent (par exemple, enregistré le flux clog dans un fichier ou permettre à un éditeur d'afficher la sortie cerr en rouge, pour que les erreurs soient bien visibles).

Internationalisation

Le w signifie que le flux prend en charge les caractères étendus (“w” pour wide), c'est-à-dire les caractères avec accent ou provenant d'un alphabet différent de l'anglais. La gestion de l’internationalisation est un peu complexe en C++, en particulier à cause de la multiplicité des normes de codage et la prise en charge très variable selon le système d'exploitation. Cela fera l'objet d'un chapitre dédié.

Comment fonctionne un flux ? Imaginer un employé de bureau qui reçoit des dossiers. Il a une grande pile de dossiers, il prend le plus ancien, le traite, puis passe au suivant. Peut importe si les dossiers arrivent un par un ou en paquet, il prend toujours un par un.

Les flux standards fonctionne sur le même principe : il reçoivent des données (les caractères à afficher), ils prennent le premier arrivé, l'affiche puis passent au suivant. L'opérateur permettant d'envoyer des données à un flux est l'opérateur « :

main.cpp
#include <iostream>
 
int main() {
    std::cout << "hello";
}

Ce code ne doit pas être compris comme signifiant “afficher 'hello'”, mais comme “envoyer les caractères 'h', 'e', 'l', 'l' et 'o' dans le flux standard 'std::cout'”.

Il est possible d'envoyer plusieurs valeurs en série de données dans un flux, en les séparant par plusieurs opérateurs « :

main.cpp
#include <iostream>
 
int main() {
    std::cout << "hello" << "world";
}

Ce code signifie “envoyer 'hello' et 'world' dans 'std::cout'”, ce qui peut être développé en “envoyer les caractères 'h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd' dans 'std::cout'”. (Remarquez l'absence d'espace entre “hello” et “world”).

Il revient au même d'envoyer les données sur plusieurs lignes :

main.cpp
#include <iostream>
 
int main() {
    std::cout << "Hello";
    std::cout << "world";
}

Voire même d'envoyer les caractères un par un :

main.cpp
#include <iostream>
 
int main() {
    std::cout << 'h';
    std::cout << 'e';
    std::cout << 'l';
    std::cout << 'l';
    std::cout << 'o';
    std::cout << 'w';
    std::cout << 'o';
    std::cout << 'r';
    std::cout << 'l';
    std::cout << 'd';
}

Tous les codes précédent affichent :

helloworld

Les chaînes de caractères

Comme vous pouvez remarquer, on distingue en C++ les caractères et les chaînes de caractères. Un caractère sera écrit avec un guillemet simple droit, une chaîne de caractères sera écrite avec des guillemets double droits :

main.cpp
#include <iostream>
 
int main() {
    std::cout << "hello";  // une chaîne
    std::cout << 'h';      // un caractère
}

Si vous essayer de mettre plusieurs caractères entre guillemets droits, vous obtiendrez l'erreur suivante dans Coliru :

main.cpp
#include <iostream>
 
int main() {
    std::cout << 'hello, world!' << std::endl;
}

affichera :

main.cpp:4:18: warning: character constant too long for its type
     std::cout << 'hello, world!' << std::endl;
                  ^
1919706145

Remarquez bien que cela provoque un avertissement (warning) et non une erreur : le programme s'exécute quand même. La chaîne est interprétée comme un nombre, qui est affiché en dessous de l'avertissement. Les avertissements signifient que le compilateur ne sait pas si le code contient une erreur ou si c'est intentionnel de la part du développeur.

Vous ne devez jamais ignorer les avertissements. Dans ce cours, un programme qui affiche des avertissements ne sera pas considéré comme correct.

Les caractères spéciaux

Si vous vous êtes amusé à tester différents messages à afficher, vous avez peut être essayé d'afficher une barre oblique inversée \ (backslash) ou des guillemets . Si ce n'est pas le cas, essayez maintenant :

main.cpp
#include <iostream>
 
int main() {
  std::cout << "hello " world" << std::endl;
}

Le compilateur ne va pas du tout aimer ce code et produira des erreurs et avertissements :

main.cpp:4:30: warning: missing terminating " character
   std::cout << "hello " world" << std::endl;
                              ^
main.cpp:4:3: error: missing terminating " character
   std::cout << "hello " world" << std::endl;
   ^
main.cpp: In function 'int main()':
main.cpp:4:25: error: expected ';' before 'world'
   std::cout << "hello " world" << std::endl;
                         ^

L'erreur est simple à comprendre avec un peu de logique. En C++, une chaîne de caractères est délimité par des guillemets. Le compilateur va rencontrer le premier guillemet et va l'interprété comme le début d'une chaîne (et donc tout ce qui suit sera considéré comme des caractères faisant partie de la chaîne).

"hello " world"
^      ^      ^
1      2      3

Le compilateur continue de parcourir le code et va arriver au second guillemet. Le problème est qu'il ne sait pas qu'il y a encore un guillemet ensuite, il va considérer que ce guillemet est la fin de la chaîne. Tout ce qui suis sera donc considéré comme du code C++ et non comme des caractères à afficher. Or, comme la suite n'est pas du code C++ valide, le compilateur produit une erreur.

Pour résoudre ce problème, il faut pouvoir dire au compilateur que l'on souhaite utiliser le caractère guillemet et pas indiquer la fin de la chaîne. Pour cela, il faut utiliser un caractère spéciale, le caractère d'échappement \. Ce caractère sera interprété de la façon suivante pas le compilateur lorsqu'il est rencontré dans une chaîne : “attention, le caractère suivant est un caractère spécial”.

En particulier, le caractère '\”' est interprété comme étant le caractère guillemet et non la fin d'une chaîne. Avec cette correction, le code devient :

main.cpp
#include <iostream>
 
int main() {
  std::cout << "hello \" world" << std::endl;
}

affiche :

hello " world

Remarquez que \” est interprété comme étant un seul caractère et pas deux.

Un autre problème se présente. Puisque \ est considéré comme étant le caractère d'échappement, comment afficher le caractère \ dans une chaîne ? La solution est simple, il suffit d'utiliser le caractère '\\'.

Encore une fois, il s'agit bien d'une séquence d'échappement, qui correspond à un seul caractère dans la chaîne et qui est constitué du caractère d'échappement \ puis de l'identifiant \.

La liste des séquences d'échappement est donné dans la documentation : Escape sequences.

Les chaînes de caractères brutes

Dans certains cas, une chaîne va contenir de nombreux caractères spéciaux (par exemple, le chemin d'un fichier sous Windows, qui peut contenir plusieurs \ ou des expressions régulières). Dans ce cas, il devient fastidieux (c'est pas faux !) de devoir ajouter systématiquement le caractère d'échappement \ devant chaque caractère spécial.

Pour éviter cela, il est possible d'utiliser une chaîne brute, dans laquelle les caractères spéciaux sont ignorés.

#include <iostream>
using namespace std;
 
int main()
{
  cout << "Je fais des tests pour apprendre le C++ !" << endl;
  cout << R"(")" << endl;
  cout << R"(\)" << endl;
  return 0;
}

Je vous laisse faire le test pour vérifier que cela fonctionne. Maintenant que vous avez vu ces deux petites exceptions, vous êtes prêt à écrire tout ce qui vous passera par la tête dans la console. Voyons maintenant ce qui se passe à la fin de notre programme.

Exercices

Modifier le code suivant pour afficher les messages demandés. Faire l'exercice

main.cpp
#include <iostream>
 
int main() {
    // Modifier la ligne suivante pour afficher "Bienvenue tout le monde !"
    std::cout << "Hello, world!" << std::endl;
 
    // Ajouter UNE ligne pour afficher "Bienvenue !" et "Tout le monde !" sur DEUX lignes
    std::cout << @@@@
 
    // Ajouter DEUX lignes pour afficher "Bienvenue !" et "Tout le monde !" sur UNE ligne
    std::cout << @@@@
    std::cout << @@@@
 
    return 0;
}

Exos : include, recherche quel include pour utiliser vector ? array ? etc

Exos : utiliser tabulation.

Exos : Afficher un tableau avec bordures

main.cpp
#include <iostream>
 
int main() {
  std::cout << "┌────┬────┬────┐" << std::endl;
  std::cout << "│ 12 │ 34 │ 56 │" << std::endl;
  std::cout << "├────┼────┼────┤" << std::endl;
  std::cout << "│ ab │ cd │ ef │" << std::endl;
  std::cout << "└────┴────┴────┘" << std::endl;
}

affiche :

┌────┬────┬────┐
│ 12 │ 34 │ 56 │
├────┼────┼────┤
│ ab │ cd │ ef │
└────┴────┴────┘

Faire pareil, avec double bordure.

Aller plus loin

hello_world.1428802883.txt.gz · Dernière modification: 2015/04/12 03:41 (modification externe)