Pourquoi un code compilé pour l'optimisation n'est-il pas
exploitable dans un outil de débogage ?
Le code exécutable optimisé est tellement profondément remanié
que l'enchaînement des opérations élémentaires qui sont effectivement
exécutées ne correspond plus du tout à la séquence d'opérations et aux
variables qui sont exprimées dans le code source.
(voir
cette section et
cette section)
Quel est le comportement de la
macro assert() lorsque la
macro NDEBUG n'est
pas définie ?
Si la
macro NDEBUG n'est pas définie, c'est que nous sommes
en phase de mise au point.
Dans ce cas, la condition exprimée dans
assert() est évaluée
et si elle est fausse le programme est brutalement arrêté
avec un message d'erreur explicite.
(voir
cette section)
Quel est le comportement de la
macro assert() lorsque la
macro NDEBUG est définie ?
Si la
macro NDEBUG est définie, c'est que nous ne sommes
pas en phase de mise au point, nous cherchons à obtenir un code
optimisé.
Dans ce cas, la macro
assert() n'a aucun effet, comme si elle n'avait
pas du tout été utilisée, donc elle n'a aucun coût sur le temps
d'exécution.
(voir
cette section)
Quel est le rôle des
sanitizers qui sont utilisés lorsque le
code exécutable est généré pour la mise au point ?
Donner des exemples.
Ces outils ajoutent, lors de la génération du code exécutable, des
instructions qui tentent de détecter au plus tôt des incohérences.
Par exemple, la sortie d'un tableau lors de son parcours, des
incohérences d'allocation dynamique et de libération de mémoire, des
fuites mémoire, des dépassements de capacité sur les types utilisés
dans des calculs...
(voir
cette section)
Quelle est la précaution la plus évidente à prendre lorsqu'on
souhaite optimiser une boucle de traitement ?
Il faut repérer dans cette boucle tous les calculs pour lesquels
nous savons qu'il donneront systématiquement le même résultat
à chaque itération, les calculer une fois pour toutes avant la
boucle et réutiliser ces résultats dans la boucle.
(voir
cette section)
Quel est l'intérêt principal des fonctions qualifiées de
inline static ?
Le fait que le compilateur connaisse les détails du code de la
fonction appelée lui permet d'utiliser ces connaissances pour
éliminer des précautions superflues au niveau du code appelant.
(voir
cette section)
Lorsqu'on cherche à minimiser les risques de perte de performance,
quelle précaution doit-on prendre à propos de l'usage répété
d'une donnée désignée par un pointeur ?
Si nous savons que cette donnée n'a pas d'autres raisons de changer
de valeur, il vaut mieux travailler sur une copie locale de cette
donnée plutôt que d'y accéder à de multiples reprise avec le
pointeur.
(voir
cette section)
Lorsqu'on cherche à minimiser les risques de perte de performance,
a-t-on intérêt à économiser les variables d'une fonction en
réutilisant les mêmes variables pour des calculs distincts ?
Expliquer pourquoi.
Non, il faut au contraire utiliser des variables distinctes pour
des calculs distincts.
La réutilisation de variables peut faire croire au compilateur
que des calculs distincts sont dépendants (parce qu'ils reposent
sur les mêmes variables) ce qui peut réduire ses possibilités de
reformulation des calculs.
(voir
cette section)
Lorsqu'on cherche à minimiser les risques de perte de performance,
a-t-on intérêt à utiliser des entiers signés ou non-signés si aucune
spécificité applicative n'impose l'usage des uns ou des autres ?
Expliquer pourquoi.
Il vaut mieux utiliser des entiers
signés.
Le dépassement de capacité sur les entiers signés a un comportement
indéfini selon la norme du langage C, contrairement aux entiers
non-signés.
Avec des entiers signés, le compilateur n'envisage donc pas l'éventualité
d'un rebouclage de la plage de valeur ce qui simplifie l'analyse des
algorithmes et offre l'opportunité d'une formulation plus efficace et
plus systématique du code exécutable (notamment en ce qui concerne
les boucles).
(voir
cette section)