« République bananièreChampagne ! »

extern inline

11.12.10 | par Le Grincheux | Catégories: Mauvaise humeur, Je hais l'informatique

Le mieux est l'ennemi du bien. Cela fait bien vingt-cinq ans que je programme tout et n'importe quoi dans des langages bizarres et je me fais encore surprendre par certaines hérésies assumées du C. Je viens de passer plusieurs jours à chercher pourquoi un programme qui fonctionnait parfaitement au mois de septembre dernier plantait assez rapidement aujourd'hui avec des erreurs étranges résultant visiblement d'une corruption du tas. Les plantages différents selon les architectures matérielles et les systèmes d'exploitation utilisés ont fini par me mettre la puce à l'oreille. J'avoue, j'ai aussi utilisé valgrind qui ne m'a été d'aucun secours dans ce cas précis puisque d'une part l'erreur ne provenait pas d'accès aberrants en mémoire et que, d'autre part, la pile était corrompue assez gravement pour que valgrind perde totalement les pédales.

Le langage C, même s'il est rempli de choses dépendantes de l'implantation, est pourtant un langage normalisé par un comité d'experts. Parmi ces experts, il se trouve un type supérieurement intelligent qui a décidé que la construction « extern inline » était une construction valide et surtout que cette construction avait un intérêt certain. Il faut tout de même être assez bizarre pour prétendre qu'une fonction inaccessible lors de la compilation puisse avoir le statut de fonction « inline ». Cette déclaration ne devrait strictement rien faire. Or c'est faux, elle influe sur le passage de paramètres en violation des règles habituelles utilisées sur l'architecture matérielle cible. Le processeur empile quelque chose alors qu'il relit dans les registres, voire fait exactement le contraire. Au final, il fait n'importe quoi et le résultat est une corruption généralisée de la pile ou du tas, voire dans les cas les plus chanceux des deux.

J'aime ce genre d'erreurs qui dépend essentiellement du couple architecture matérielle et système d'exploitation. Ce sont les plus amusantes à corriger puisqu'il faut aller fouiller au fin fond du code assembleur généré par la compilation pour trouver ce qui cloche. Lorsque l'exécutable fait plusieurs dizaines de mégaoctets, autant chercher une aiguille dans une botte de foin.

Il serait assez amusant que le responsable de ce « extern inline » se démonce publiquement et qu'il justifie ses positions. J'admets que « static inline » ait un intérêt certain puisque cette constuction permet de s'affranchir des macros ou de faire des choses plus propres que celles qu'on pourrait faire avec ces macros, mais je ne comprends pas qu'on puisse mettre des fonctions « inline » accessibles depuis plusieurs fichiers sources ailleurs que dans un fichier d'en-tête. Pourquoi vouloir à tout prix utiliser une construction aussi bizarre que « extern inline » et surtout qu'apporte-t-elle de parfaitement indispensable au paradisme du C99 ?

Certains jours, je comprends pourquoi je préférerais toujours le Fortran au C et à ses dérivés. Le Fortran a été conçu par des mathématiciens pour les mathématiciens. Le langage est propre, fonctionnel et parfaitement structuré. Il est quasiment impossible d'écrire avec lui des choses laides. En revanche, le C a été proposé par des informaticiens pour des informaticiens. C'est un langage laid avec des tas de constructions parfaitement ignobles voire totalement impossibles qui permettent de faire des choses prétendûment intéressantes et qui s'avère dangereux même pour ceux comme moi qui l'utilisent tous les jours. Le C peut même assez rapidement devenir un langage en lecture seule comme le prouve le petit programme suivant parfaitement valable. À vous de me dire sans le compiler ce qu'il fait…

int m = 754974721, N, t[1 << 22], a, *p, i, e = 1 << 22, j, s, b, c, U;
f (d)
{
for (s = 1 << 23; s; s /= 2, d = d * 1LL * d % m)
if (s < N)
for (p = t; p < t + N; p += s)
for (i = s, c = 1; i; i--)
b = *p + p[s], p[s] = (m + *p - p[s]) *
1LL * c % m, *p++ = b % m, c = c * 1LL * d % m;
for (j = 0; i < N - 1;)
{
for (s = N / 2; !((j ^= s) & s); s /= 2);
if (++i < j)
a = t[i], t[i] = t[j], t[j] = a;
}
}

main ()
{
*t = 2;
U = N = 1;
while (e /= 2)
{
N *= 2;
U = U * 1LL * (m + 1) / 2 % m;
f (362);
for (p = t; p < t + N;)
*p++ = (*p * 1LL ** p % m) * U % m;
f (415027540);
for (a = 0, p = t; p < t + N;)
a += (6972593 & e ? 2 : 1) ** p, *p++ = a % 10, a /= 10;
}
while (!*--p);
t[0]--;
while (p >= t)
printf ("%d", *p--);
}

Et encore, je ne parle que du C parce que mon thérapeute qui craint pour ma santé mentale m'a interdit d'utiliser des langages comme Java ou le C++. Ne lui dites pas, mais de temps en temps, je touche au C++, mais en faisant fi du côté objet de la chose. Le C++ purement fonctionnel est un concept assez intéressant pour ne pas lui adjoindre l'aberration de la programmation abjecte, pardon, objet, puisqu'on on y trouve un typage fort et la surcharge des opérateurs.

 

Aucun commentaire pour le moment


Formulaire en cours de chargement...