Main menu:

Site search

Categories

septembre 2025
L M M J V S D
1234567
891011121314
15161718192021
22232425262728
2930  

Archive

C++ – Constantes

Lorsque l’on veut préciser que la valeur d’un objet ne varie pas, on peut le faire précéder du mot-clé const.

const int i = 10;

L’objet étant constant, il doit être initialisé au moment de sa déclaration.

const int i;

Erreur i n’est pas initialisé.

const int i = f();

Ok même si la valeur d’initialisation n’est pas connue au moment de la compilation.

void g(const int i)
{
  // garantit que i ne sera pas modifié
  // par la fonction
}

Dans le cas où l’objet est initialisé avec une valeur connue au moment de la compilation, le compilateur n’a pas besoin d’allouer de la mémoire pour cet objet.

const int i = 10;

Le compilateur connait la valeur de i et remplace directement dans le code à chaque fois que i est utilisé, i par sa valeur 10. Aucune mémoire n’est allouée à i. Aucune adresse n’existe contenant i.
Examinons le code assembleur produit par g++ pour le petit programme suivant en le compilant avec l’option -S (g++ -S test.cpp).

int main()
{
  const int i = 10;
  int j = i;
  return 0;
}

Le code assembleur généré pour 1 et 2 est le suivant:

movl  $10, -12(%ebp)      // const int i = 10;
movl  $10, -8(%ebp)       // int j = i

Le compilateur attribue à j la valeur 10 directement sans utiliser i.

Comparons avec ce code-ci:

int main()
{
  int i = 10;
  int j = i;
  return 0;
}

Le code assembleur généré par g++ est celui-ci:

movl  $10, -12(%ebp)    // int i = 10;
movl  -12(%ebp), %eax   // on charge eax avec la valeur de i
movl  %eax, -8(%ebp)    // on copie eax dans j

On remarque que ce code est moins efficace que la version avec i constant puisque le compilateur doit d’abord chargé un registre du processeur (eax) avec la valeur de i et ensuite copier ce registre dans j. C’est donc plus lent et moins efficace que dans le cas de i constant.

Donc, déclarez vos objets constants chaque fois que possible. Vous gagnerez en vitesse, en efficacité et votre code sera plus sûr, le compilateur garantissant que vos objets ne pourront en aucun cas être modifiés (const correctness). Une constante non-déclarée const et modifiée dans le code est toujours un bug difficile à déceler dans un code source un peu important.

Trois remarques supplémentaires:
Premièrement, les objets constants ont une liaison interne (internal linkage) sauf si ils sont déclarés extern. Cela veut dire que vous pouvez déclarer vos objets constants dans les fichiers d’entête sans risque que le compilateur vous donne une erreur du type « déclaration multiple », ces objets étant locaux à chaque fichier.

Deuxièmement, le mot-clef const modifie l’objet qui est à sa gauche. S’il n’y a rien à sa gauche alors il modifie l’objet à sa droite. Autement dit:

const string& s

et

string const & s

sont identiques. Dans le premier cas, il n’y a rien à la gauche du const et donc il modifie string et s est donc une référence vers un string constant. Dans le deuxième cas, const modifie ce qui est à sa gauche c’est à dire string et donc s est une référence vers un string constant. C’est bien la même signification dans les deux cas.

Troisièmement, examinons les fonctions suivantes:

void f(string s);
void g(string& s);
void h(const string& s);

string str;
f(str);
g(str);
h(str);

Dans le cas de la fonction f(str), str est passé par valeur et s est une nouvelle instance de str. Toute modification de s dans la fonction est perdue.
Dans g(str), s est le même objet que str et donc toute modification de s entraine la modification de str.
Dans h(str), s est aussi le même objet que str mais sa modification dans la fonction n’est pas permise parce que déclaré const.

Effectuer une même opération sur un ensemble de fichiers

Imaginons que vous désiriez effectuer une même tâche sur tous les fichiers d’un répertoire comme par exemple les compresser avec tar. Imaginons que vous veniez de ripper votre dernier CD pour pouvoir en mettre les chansons en mp3 sur votre baladeur.

Effectuer ces opérations à la main sur chaque fichier peut vite devenir fastidieux à force de chaque fois retaper la même commande dans un terminal. Et bien, il y a moyen de simplifier l’opération avec quelques commandes simples en bash.

Prenons l’exemple du répertoire ~/music dans lequel se trouvent les fichiers .wav que vous voulez transcoder en mp3. Cette simple commande va rechercher tous les fichiers et les transformer en mp3 grâce à lame (encodeur mp3):

$  find ~/music -type f | while read x; do lame -b 192 -h "$x"; done

-type f indique à find que vous recherchez des fichiers. Ceux que find a trouvés vont apparaître sur sa sortie standard (celle qui est normalement affichée à l’écran), un nom de fichier par ligne. La sortie standard de find est redirigée par le pipe vers l’entrée standard de read. Ces lignes, contenant les noms fichiers trouvés par find, vont être lues par read dans une boucle while qui sera exécutée tant qu’il y a des noms de fichiers à lire à l’entrée. A chaque fois que la boucle est parcourue, le nom du fichier qui se trouve dans $x sera encodé en mp3 par lame.

Si dans votre répertoire, il risque d’y avoir autre chose que des fichiers .wav, il serait mieux de dire à find que ce sont des fichiers .wav que vous désirez. Il suffit d’une petite modification:

$  find ~/music -name \*.wav | while read x; do lame -b 192 -h "$x";done

Au lieu de spécifier à find que vous rechercher tel type de fichier, vous pouvez lui dire que ce sont les fichiers avec un certain nom qui vous intéressent. C’est ce qui est fait avec l’option -name \*.wav.

Une autre méthode pour encoder vos fichiers en une seule fois est d’utiliser une boucle for tout simplement:

$ for i in *.wav;do lame -b 192 -h "$i";done

Pour généraliser ces commandes, il suffit de remplacer la commande lame -b 192 -h par ce que vous voulez. Vous pouvez donc utiliser ce principe pour plein de choses différentes.

Graphisme – Deux sites remarquables

Si le graphisme vous intéresse, si vous appréciez The Gimp et Inkscape, je ne peux que vous recommander d’aller visiter ces deux sites. Leur particularité? Ils proposent tous les deux des tutoriels de haut niveau sous forme de screencasts. C’est en anglais évidemment.

The Gimp : Meet The Gimp

Inkscape : screencasters.heathenx.org

pyRenamer – Renommer des fichiers

En cherchant sur le Web une librairie python pour faire des essais avec les données EXIF de mes photos, je suis tombé sur un programme écrit en python et qui permet de renommer des fichiers. Son nom : pyRenamer.

Ces principales caractéristiques sont les suivantes:

  • utilisation de patterns pour renommer les fichiers
  • autorise les substitutions et remplacements
  • autorise les insertions et suppressions
  • patterns spécialisés pour les fichiers musicaux et les images.

Pour l’installer, rien de plus simple, il se trouve dans les repositories d’Ubuntu. Il suffit donc de lancer Synaptic et de rechercher pyRenamer. Ou bien, si vous préférez la ligne de commande, ouvrez une console et tapez:

$ sudo aptitude install pyrenamer

C++ – Un tableau comme argument d’une fonction

char *c;
char c[16];

Ces deux déclarations déclarent deux variables de types différents. La première déclare un pointeur de caractère. La seconde déclare un tableau de caractères. Pour s’en convaincre, il suffit de voir que sizeof() appliqué aux deux variables donne des résultats différents. 4 pour le premier et 16 pour le second (sur ma machine, ça pourrait être différent sur la vôtre).

Par contre, ces quatre fonctions déclarent exactement la même fonction. Il n’y a aucune différence entre les quatre.

void f(char *c);
void f(char c[]);
void f(char c[16]);
void f(char c[32]);

A l’intérieur des crochets, le nombre ne sert à rien. Il peut être considéré juste comme une indication pour l’utilisateur de la fonction.
Un tableau en paramètre d’une fonction est converti en pointeur automatiquement. C’est pourquoi, il n’y a pas de différence entre les quatre fonctions.

Vous pouvez même passer à la fonction une chaîne littérale parce qu’il existe une conversion de tableau de caractères constants vers pointeur de caractère. En réalité, vous passez à la fonction l’adresse de la chaîne de caractères.

f("une chaîne de caractères");    // OK

A l’intérieur de la fonction f, c est vraiment un pointeur. Les trois fonctions ci-dessous donnent le même résultat.

void f1(char *c)
{
  std::cout<<"size of c in f1: "<<sizeof(c)<<std::endl;
}

void f2(char c[])
{
  std::cout<<"size of c in f2: "<<sizeof(c)<<std::endl;
}

void f3(char c[32])
{
  std::cout<<"size of c in f3: "<<sizeof(c)<<std::endl;
}

int main()
{
  char c[10];
  std::cout<<"size of c[10]: "<<sizeof(c)<<std::endl;
  f1(c);
  f2(c);
  f3(c);

  return 0;
}

ping, traceroute et nmap

Lorsque vous devez tester un réseau, il y a plusieurs commandes que vous pouvez utiliser.

Ping

La plus simple des commandes mais sans doute aussi la plus utile pour tester si une connexion existe entre deux machines. ping vous dira si la connexion est établie pour autant que la machine que vous pingez soit configurée pour y répondre.

$ ping 192.168.8.1
PING 192.168.8.1 (192.168.8.1) 56(84) bytes of data.
64 bytes from 192.168.8.1: icmp_seq=1 ttl=64 time=0.733 ms
64 bytes from 192.168.8.1: icmp_seq=2 ttl=64 time=0.719 ms
...

Traceroute

Une deuxième possibilité est d’utiliser la commande traceroute qui vous donne le chemin parcouru pour arriver jusqu’à une machine ou adresse. Cela correspond à une liste de machine, routeurs par où on passe pour arriver à la machine demandée. Par exemple voici le résultat d’un traceroute sur www.ubuntu-fr.org:

$ traceroute www.ubuntu-fr.org
traceroute to www.ubuntu-fr.org (213.95.41.13), 30 hops max, 40 byte packets
1  192.168.8.1 (192.168.8.1)  1.755 ms  2.491 ms  3.158 ms
2  1.114-67-87.adsl-dyn.isp.belgacom.be (87.67.114.1)  10.724 ms  12.454 ms  14.002 ms
3  93.251-244-81.adsl-static.isp.belgacom.be (81.244.251.93)  17.766 ms  19.006 ms  20.737 ms
4  ge0-0.intlstr1.isp.belgacom.be (194.78.0.46)  23.415 ms ge1-0.intlstr1.isp.belgacom.be (194.78.0.146)  23.968 ms  25.596 ms
5  80.84.20.34 (80.84.20.34)  28.274 ms  29.891 ms  31.404 ms
6  80.84.18.26 (80.84.18.26)  38.934 ms  15.355 ms  15.928 ms
7  80.84.18.24 (80.84.18.24)  23.469 ms  24.521 ms  25.561 ms
8  gec1-rt1-ffm2.core.noris.net (80.81.192.88)  28.011 ms  29.210 ms  31.862 ms
9  vl604-rt3-nbg3.core.noris.net (213.95.0.197)  36.367 ms  39.095 ms  39.801 ms
10  vl31-rt1-nbg3.access.noris.net (62.128.25.137)  41.825 ms  43.582 ms  44.712 ms
11  lisa.ubuntu-eu.org (213.95.41.13)  46.736 ms  50.008 ms  51.416 ms

On part de mon PC pour arriver d’abord sur mon routeur (192.168.8.1). Ensuite on a un ensemble de machines appartenant à mon FAI, d’autres encore pour finalement arriver sur lisa qui est la machine hébergeant le serveur web de Ubuntu-fr.org.

Traceroute donne des renseignements intéressants dans ce sens que si la connexion est coupée, il nous indique où.

Nmap

Il y a une autre commande moins connue mais très intéressante, nmap. C’est un scanner de ports. il est capable d’utiliser plusieurs méthodes pour cela. Il vous dira quels sont les ports ouverts sur quelle machine et peut même vous dire le système d’exploitation utilisé. Mais tout d’abord, il faut l’installer parce que cet outil ne l’est pas par défaut dans Ubuntu:

$ sudo aptitude install nmap

Pour faire son scan des ports c’est à dire détecter les ports qui sont ouverts qur une machine, nmap utilise les caractéristiques des connexions TCP. TCP étant orienté connexion, un échange est entamé avec le serveur chaque fois qu’on désire se connecter sur ce serveur.

Connexion TCP

Pour débuter la connexion, le client envoie au serveur un paquet IP contenant le flag SYN. Le serveur lui répond en renvoyant un paquet IP et mettant à 1 les flags SYN et ACK de ce paquet. ACK signifie qu’il a compris la demande. Il indique toujours une confirmation. Le client ensuite envoie lui-aussi un ACK et l’échange de données peut commencer avec le serveur. Lorsque le client en a terminé et veut arrêter la connexion avec le serveur, il envoie un paquet au serveur contenant les flags FIN et ACK. Le serveur répond par un ACK disant qu’il a compris et envoie à son tour la demande de de fin de la connexion en envoyant au client un paquet contenant les flag FIN et ACK. Le client lui répond par un ACK et la connexion est terminée.

nmap se base sur cet échange pour proposer plusieurs type de scan:

  • scan complet avec établissement de la connexion
  • scan SYN, seul un paquet contenant le flag SYN est envoyé au serveurscan FIN, un paquet est envoyé au serveur. La norme spécifie qu’un port fermé doit répondre à ce paquet alors que les ports ouverts l’ignorent.
  • scan ACK, même principe que pour le scan FIN

La commande nmap accepte un certain nombre de switches dont les principaux sont les suivants:

  • -sP ping scan
  • -sL n’envoie rien, fait juste un reverse DNS sur l’adresse spécifiée
  • -sS SYN scan
  • -sT TCP connect scan. Etablit une connexion complète avec le serveur
  • -sA ACK scan
  • -sO protocol scan
  • -sU scan en UDP
  • -O détection du système d’exploitation
  • -Tx timing. La valeur x (de 1 à 5) détermine le temps de réponse accepté
  • -A détection du système d’exploitation de la version et traceroute
  • -p x spécifie le port

Il y a bien d’autres possibilités. Je vous renvoie à la man page de nmap pour une description de toutes les possibilités de nmap.

A titre d’exemple voici le résultat de la commande suivante appliquée à mon réseau chez oi (scan des adresses 192.168.8.1 à 192.168.8.20):

$ sudo nmap -A -T4 192.168.8.1-20

Starting Nmap 4.53 ( http://insecure.org ) at 2008-03-08 13:22 CET
SCRIPT ENGINE: rpcinfo.nse is not a file.
SCRIPT ENGINE: Aborting script scan.
Interesting ports on 192.168.8.1:
Not shown: 1712 filtered ports
PORT   STATE SERVICE VERSION
23/tcp open  telnet  Cisco or Edge-core switch telnetd
80/tcp open  http    Apache httpd 0.6.5
MAC Address: 00:xx:xx:xx:xx:xx (Aceex)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: broadband router
Running: Aceex embedded
OS details: Aceex BR41 broadband router
Network Distance: 1 hop
Service Info: Device: switch

Interesting ports on 192.168.8.2:
Not shown: 1712 filtered ports
PORT    STATE SERVICE      VERSION
139/tcp open  netbios-ssn
445/tcp open  microsoft-ds Microsoft Windows XP microsoft-ds
MAC Address: 00:xx:xx:xx:xx:xx (Asustek Computer)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running: Microsoft Windows XP
OS details: Microsoft Windows XP SP2
Network Distance: 1 hop
Service Info: OS: Windows

SCRIPT ENGINE: rpcinfo.nse is not a file.
SCRIPT ENGINE: Aborting script scan.
Interesting ports on 192.168.8.3:
Not shown: 1711 closed ports
PORT    STATE SERVICE     VERSION
139/tcp open  netbios-ssn Samba smbd 3.X (workgroup: HOME)
445/tcp open  netbios-ssn Samba smbd 3.X (workgroup: HOME)
631/tcp open  ipp         CUPS 1.2
Device type: general purpose
Running: Linux 2.6.X
OS details: Linux 2.6.22 - 2.6.23
Uptime: 0.125 days (since Sat Mar  8 10:22:31 2008)
Network Distance: 0 hops

SCRIPT ENGINE: rpcinfo.nse is not a file.
SCRIPT ENGINE: Aborting script scan.
Interesting ports on 192.168.8.5:
Not shown: 1711 filtered ports
PORT    STATE SERVICE     VERSION
135/tcp open  msrpc       Microsoft Windows RPC
139/tcp open  netbios-ssn
445/tcp open  netbios-ssn
MAC Address: 00:xx:xx:xx:xx:xx (Quanta Computer)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running: Microsoft Windows Vista
OS details: Microsoft Windows Vista, Microsoft Windows Vista Home Basic
Uptime: 0.059 days (since Sat Mar  8 11:57:45 2008)
Network Distance: 1 hop
Service Info: OS: Windows

All 1714 scanned ports on 192.168.8.10 are closed
MAC Address: 00:xx:xx:xx:xx:xx (Apple)
Device type: phone|media device|general purpose|web proxy|specialized
Running: Apple embedded, Apple Mac OS X 10.2.X|10.3.X|10.4.X|10.5.X, Blue Coat SGOS 5.X, FreeBSD 4.X, VMWare ESX Server 3.0.X
Too many fingerprints match this host to give specific OS details
Network Distance: 1 hop
OS and Service detection performed. Please report any incorrect results at http://insecure.org/nmap/submit/ .
Nmap done: 20 IP addresses (5 hosts up) scanned in 60.653 seconds

Outre les ports ouverts pour le partage de fichiers et d’imprimantes, on voit sur le routeur qui a l’adresse 192.168.8.1 que les ports 23 telnet et 80 web sont ouverts. Ce sont les deux ports qui permettent la gestion du routeur.
La commande révèle aussi les MAC Address des PC et le système d’exploitation. Vous pouvez voir que mon réseau est constitué d’une machine sous Linux, d’une machine sous Windows XP, d’une autre sous Windows Vista et finalement d’une quatrième sous Mac OSX (Apple). Même le routeur est détecté correctement.