précédent  index  suivant

16. Autres


16.1 Comment rendre un programme plus rapide ?

Il y a deux raisons possibles à la « lenteur » d'un programme.

La première vient de l'écriture du code lui-même, les entrées/sorties, les allocations dynamiques, de nombreux appels de fonctions, des boucles, etc. Le programmeur a généralement peu intérêt à modifier son code, tout au plus pourra-t-il remplacer les petites fonctions le plus souvent appelées par des macros et tenter de limiter les boucles. On pourra aussi améliorer les entrées/sorties et les allocations si c'est possible. Il reste enfin les options de compilation sur lesquelles on peut jouer.

L'autre raison vient de la complexité théorique des algorithmes utilisés. Dans ce dernier cas, il faut chercher un meilleur algorithme. Cet aspect est développé par exemple dans www.enseignement.polytechnique.fr/profs/informatique/Jean-Jacques.Levy/poly/polyx-cori-levy.ps.gz

Il existe des outils de profilage de programmes. Il s'agit de compiler les sources avec une bibliothèque puis de lancer le programme. On lance alors un lociciel associé à la bibliothèque. Le résultat est un fichier où est détaillé le temps passé dans chaque fonction, le nombre d'appels, etc. Sur Unix-like, le projet GNU propose gprof (cf. 4.6).

Rappelons tout de même que la vitesse d'exécution d'un programme (hors problèmes d'algorithmique) est peu souvent critique, et qu'il est bien plus important de fournir un code lisible.

16.2 Quelle est la différence entre byte et octet ?

L'unité de mémoire élémentaire du C est le byte. Le byte au sens anglo-saxon et donc pas l'octet au sens francophone (caractère).

En fait un byte correspond à un caractère non-signé (unsigned char), lequel peut prendre plus (ou moins) de 8 bits. En principe, en français, on parle dans ce cas de multiplet (et peut-être bientôt de codet) comme traduction officielle de byte dans ce sens.

(Merci à Antoine Leca).

16.3 Peut-on faire une gestion d'exceptions en C ?

Oui, c'est possible, en utilisant le couple setjmp()/longjmp().

	#include <stdio.h>
	#include <stdlib.h>
	#include <setjmp.h>

	jmp_buf env;

	long fact(long x)
	{
		long i, n;

		if (x < 0)
			longjmp(env, 1);
		for (n = 1, i = 2; i <= x; i ++)
			n *= i;
		return n;
	}

	long comb(long k, long n)
	{
		if (k < 0 || n < 1 || k > n)
			longjmp(env, 2);
		return fact(n) / (fact(k) * fact(n - k));
	}

	int main(int argc, char *argv[])
	{
		if (argc < 3) {
			fprintf(stderr, "pas assez d'arguments\n");
			return EXIT_FAILURE;
		}
		if (setjmp(env)) {
			fprintf(stderr, "erreur de calcul\n");
			return EXIT_FAILURE;
		}
		printf("%ld\n", comb(strtol(argv[1], 0, 0),
			   strtol(argv[2], 0, 0)));
		return 0;
	}
		

Voilà un programme qui calcule le coefficient binomial des deux arguments ; main() appelle comb() qui appelle fact(). Ces fonctions vérifient un peu les arguments, et on voudrait renvoyer une erreur en cas de problème ; mais :

setjmp() sauvegarde l'état du programme au moment de l'appel, et renvoie 0. longjmp() remplace le contenu de la pile d'exécution par la sauvegarde, et le programme se trouve à nouveau à l'endroit de l'appel à setjmp(). Celle-ci renvoie alors une valeur passée en paramètre de longjmp() (dans l'exemple, 1 pour une erreur dans fact() et 2 pour une erreur dans comb()).

La méthode présentée ici est assez rustique. Il existe des mécanismes de POO (Programmation Orienté Objet) en C bien plus évolués. Vous pouvez à ce sujet allez voir l'excellent document : ldeniau.home.cern.ch/ldeniau/html/oopc/oopc.html. Allez voir aussi le document suivant : cern.ch/Laurent.Deniau/html/exception/exception.html. Voir aussi la question 3.10.

16.4 Comment gérer le numéro de version de mon programme ?

Serge Paccalin propose la chose suivante :

	#define STR_(a) #a
	#define STR(a)  STR_(a)
	#define VERSION 4
	printf("This is version " STR(VERSION) " of the program\n");
		

En effet, quelque chose comme :

	#define STR(a) #a
	#define VERSION a
	printf("This is version " STR(VERSION) " of the program\n");
		

fait intervenir la concaténation des chaînes trop tôt ce qui fait que le résultat de cette dernière séquence renvoie :

This is version VERSION of the program

16.5 Pourquoi ne pas mettre de `_' devant les identifiants ?

Well well well, ce n'est pas si facile.

Les vrais identificateurs réservés sont :

Ensuite, il y a les headers standards et la bibliothèque. Les headers sont libres d'utiliser des identificateurs commençant par un `_' et suivis d'une lettre minuscule, comme `_liste', mais c'est pour définir quelque chose qui a un « file scope », c'est-à-dire une portée globale à la « translation unit » (le fichier source C et les fichiers qu'il inclut). Donc, globalement, on ne doit pas s'en servir pour définir quelque chose qui a ce « file scope ».

Ça veut dire quoi ? Que les choses suivantes sont interdites :

	typedef int _foo;
	struct _bar {
		int x;
		char * y;
	};
	void _f(void);
		

En revanche, les choses suivantes sont autorisées :

	void f(int _x[])
	{
		typedef int _foo;
		struct _bar {
			int x;
			char *y;
		};
		extern void _g(void);
	}
	struct qux {
		long _truc;
	};
		

J'attire l'attention du public ébahi sur les quatre points suivants :

16.6 À quoi peut servir un cast en (void) ?

Il y a principalement deux utilités à caster une expression en (void).

La première utilisation est pour indiquer explicitement au compilateur qu'une valeur est ignorée, comme au retour d'une fonction. Par exemple, il arrive souvent d'utiliser la fonction printf() sans utiliser ni même tester la valeur de retour. Ecrire l'appel à printf() ainsi

	(void)printf("%s\n", "Un message à la con") ;
		

est une manière de dire au compilateur, et aux lecteurs du code, que je sais que printf() renvoie une valeur, mais que j'ai décidé de l'ignorer. Cela peut être utile pour des utilitaires de vérification de code, comme lint.

La seconde utilisation est dans des définitions de macro. Voici un exemple :

	#undef the_truc
	#ifdef __TRUC__DEPENDANT__
	#  define the_truc(a) ((void)0)
	#else
	#  define the_truc(a) /* du code */
	#endif
		

Ainsi, the_truc(a) est utilisable là ou une expression est requise, comme ici :

	i = (the_truc(a), 5);
		

Avec une définition de the_truc comme ceci,

	#  define the_truc(a)
		

il y aurait une erreur à la compilation.


précédent  index  suivant