Après plusieurs mois d'inactivité, voici les cinq premiers chapitres de la future troisième partie du cours sur le C. Ceux-ci portent sur :
Le préprocesseur.
La représentation des types.
La manipulation des bits.
Les limites des types.
Les jeux de caractères et les encodages.
Ces derniers composeront sans doute les chapitres les plus volumineux de cette partie. En effet, les autres chapitres adopteront sans doute un style plus court et porteront sur les unions, les énumérations, les pointeurs de fonctions et les fonctions à nombre variable d'arguments. À cela s'ajoutera sans doute un T.P. portant sur la réalisation d'un petit allocateur mémoire.
N'hésitez pas à nous faire part de toutes vos remarques, particulèrement sur la facilité ou non de lecture, les sujets abordés étant plus complexes que dans les parties précédentes.
J'ai lu cette nouvelle partie avec beaucoup d'intérêt.
Je connaissais déjà la plupart des choses présentées, alors mon avis ne sera peut-être pas le même que celui d'un néophyte, mais j'ai trouvé ces chapitres très clairs et bien expliqués. Surtout grâce aux nombreux exemples qui permettent de bien comprendre.
Nous y avions effectivement pensé au début de la rédaction du cours, mais nous avions finalement décidé qu'il n'y aurait pas de telle partie, en tous les cas pas dans un cours général sur le C. L'objectif ici étant de présenté le langage, il nous a semblé que cela ne serait pas le bon endroit pour aborder cette thématique. Par ailleurs, avec déjà trois parties et des annexes à venir, le cours deviendrait vraiment trop long, je pene.
Mainenant, ce serait effectivement un cours intéressant à réaliser.
je donne vite fait mon avis sur le chapitre sur les encodages, comme on me l’a
demandé.
remarque terminologique préliminaire : j’ai réalisé qu’en fait, « encodage »
est un anglicisme un peu moche, le mot français étant tout simplement
« codage ». le problème, c’est que c’est vague (tout comme encoding en
anglais). la forme complète est « codage de caractères » mais c’est plus
long… en plus des gens se battent pour déterminer ce qu’est un « caractère »
exactement et, pour commencer, si c’est une notion pertinente. une bien
meilleure expression serait « codage de texte » ou « codage textuel », mais
je ne l’ai pas vraiment croisée. bref, je vais ptêt revoir mon tuto, mais
vous faites comme vous voulez.
je sais que ce n’est plus le moment de dire ça vu que vous êtes partis pour
écrire tout le cours comme ça (et que je n’ai pas la motivation pour
développer la discussion), mais je ne suis pas emballé par cette approche
aussi normative. le C99 est très bien supporté de nos jours (on est en 2016
tout de même) et il y a en supplément plein de bons standards de facto, je
ne vois vraiment pas l’intérêt de s’en priver au prétexte que ça n’a pas été
validé par le consortium en 1989.
je pense que vous pouvez passer plus rapidement sur la notion de jeu source
en expédiant sa présentation au début du chapitre : « voilà les caractères
autorisés en principe dans un code source (liste du jeu basique), il peut y
en avoir d’autres mais ceux‐ci sont les seuls qui sont garantis d’être
acceptés par un compilateur. » même pas besoin de nommer le « jeu source »
ni la distinction « jeu basique / étendu » pour le jeu source. d’ailleurs,
en y réfléchissant, le jeu d’exécution peut subir le même traitement
(« voilà les caractères dont l’existence sur la machine hôte est
garantie »). la norme est déjà bien assez tordue, autant faire le plus clair
et le plus efficace possible.
certes, une lettre accentuée dans un code source, en théorie, c’est « mal ».
franchement, on s’en moque éperdument au moins dans les commentaires.
puisque vous‐mêmes ne vous en privez pas, pas la peine de troubler le
lecteur en le laissant croire qu’il s’expose à tous les maux de la Terre. à
la rigueur, le jeu d’exécution peut causer des problèmes (donc attention
dans les chaînes littérales), mais de nos jours, on se moque du jeu source.
connaissez‐vous des compilateurs dont le jeu source n’est pas Unicode ou à
la rigueur latin‐1 ?
Les jeux de caractère étendus
> La norme ne précise rien à leur sujet, leur contenu est laissé au soin des
> compilateurs et systèmes d'exploitation.
en termes d’info, c’est sec ! quels sont les jeux usuels ? au moins,
mentionnez que le jeu étendu contient le jeu basique…
le jeu étendu dépend aussi de la locale (donc dynamique), il me semble
En fait, il s'agit du comportement par défaut des fonctions de la
> bibliothèque standard. Chaque char est supposé représenter un caractère
> […].
faux ! un char est censé représenter un multiplet. la norme ne considère
plus que char est un caractère, puisqu’elle supporte l’idée que char*
est du texte codé selon le multibyte encoding1 (à mentionner). il
est vrai que la terminologie obsolète reste et prête à confusion : ainsi
strlen devrait être documentée comme calculant la longueur en multiplets,
strchr comme recherchant un multiplet, etc·. le multibyte encoding est
le codage utilisé par wctomb et sa clique. il est donc déterminé par la
locale, contrairement au codage des caractères larges puisque ceux‐ci sont
censés être un codage à taille fixe capable de stocker tout le jeu
d’exécution (étendu). d’ailleurs, si je me souviens bien, le multibyte
encoding n’a pas à représenter tout le jeu d’exécution (étendu), il peut
n’en coder qu’un sous‐ensemble. par exemple, sous Linux qui supporte
l’Unicode, je peux utiliser ma localefr_BE.UTF-8 et décider soudain de
passer à fr_BE.latin1 ; le jeu d’exécution est Unicode (il ne change pas,
lui), mais dans ma deuxième locale, le multibyte encoding ne supporte
que le sous‐ensemble du latin‐1. au fait, il me semble (à vérifier) que la
norme précise que le multiplet nul est réservé pour la fin de chaîne et ne
peut pas apparaître au milieu du code d’un autre caractère.
élégamment
L'objectif recherché est de traduire une chaîne de caractères classique
> recourant à un encodage sur plusieurs multiplets (comme l'UTF-8 ou
> l'ISO-2022) […].
et surtout sur un nombre variable de multiplets.
Nous ne attarderons toutefois que sur LC_CTYPE et LC_NUMERIC dans
> cette section, les autres étant plus anecdotiques.
LC_TIME est loin d’être anecdotique ! dans mon expérience d’usager, les
affichages de date foireux sont la manifestation la plus visible et la plus
agaçante d’une mauvaise internationalisation.
débrouillez-vous pour traduire ça (le « codage multi‐multiplet »…) ↩
Pour ce qui est du terme « caractère », je considère dans ce chapitre qu'il s'agit du symbole associé à un point de code dans le jeu de caractères source ou d'exécution. Ni plus, ni moins et je me fous de savoir si un ou plusieurs de ces caractères peuvent être combiner pour former un autre symbole.
Je vais être honnête sur ce point : je n'ai vraiment pas envie de rédiger plusieurs chapitres sur le sujet en détaillant les ajouts du C99 et, éventuellement, du C11. Ce faisant, j'ai trouvé qu'un chapitre d'introduction au sujet pouvait être un bon compromis, celui-ci permettant de présenter et de sensibiliser le lecteur à la problématique de l'internationalisation et de la localisation sans perdre pour autant mes cheveux.
Le soucis, c'est qu'il faut que j'explique ensuite les fonctions de transcodages et les locales qui, elles, fonctionnent avec le jeu étendu…
Ok, je vais tâcher d'adoucir ce passage.
C'est noté, je vais modifier cela.
En effet.
Cela me paraît pour ma part correct : par défaut, donc dans la locale C et par conséquent dans le jeu de caractères d'exécution basique, chaque caractère est censé être encodé sur un multiplet, soit sur un char.
Si tu changes ta locale pour fr_BE.latin1, ton jeu d'exécution étendu devient le Latin-1 et ne reste donc pas l'Unicode. Tu peux le constater en vérifiant la valeur de la macro MB_CUR_MAX.
C'est exact.
Arf ! Je vais corriger ça.
Exact, ce sera modifié.
Une mauvaise localisation.
Sinon, le soucis c'est qu'à ce stade la fonction strftime() n'a pas encore été présentée…
J'ai laissé tomber aussi, je me contente de l'expression « sur plusieurs multiplets ».
Je vais être honnête sur ce point : je n'ai vraiment pas envie de rédiger
plusieurs chapitres sur le sujet en détaillant les ajouts du C99 et,
éventuellement, du C11. […]
mais il n’y a pas à faire une présentation par ajouts avec (Chapitre Ⅰ) le C89
(Chapitre Ⅱ) les modifications de C99 (Chapitre Ⅲ) les modifications de C11… ni
détailler tous les ajouts de chaque norme… tout simplement présenter l’état
actuel du langage, éventuellement en précisant de quelle version date un truc
quand c’est important, ce serait très bien.
Le soucis, c'est qu'il faut que j'explique ensuite les fonctions de
transcodages et les locales qui, elles, fonctionnent avec le jeu étendu…
oui, tout fonctionne avec le jeu étendu, du coup pas la peine de le nommer ! le
jeu basique est juste un ensemble de caractères à propos desquels on a des
garanties (existence dans ce jeu, codage). il suffit de dire : « les programmes
s’exécutent avec un jeu de caractères qui dépend de la locale ; ce jeu
contient au moins les caractères suivants : […] »
En effet.
arf non c’était une erreur de copié‐collé dans mon message, je disais le
contraire plus loin :
Si tu changes ta locale pour fr_BE.latin1, ton jeu d'exécution étendu
devient le Latin-1 et ne reste donc pas l'Unicode. Tu peux le constater en
vérifiant la valeur de la macro MB_CUR_MAX.
hum autant pour moi (donc en fait j’avais raison dans mon erreur :p). après
vérification de la norme, celle‐ci n’est vraiment pas claire (comme d’habitude
quoi) sur si l’interprétation d’un wchar_t dépend aussi de la locale ou pas,
mais il semblerait que oui. déjà qu’elle confond jeu de caractères et codage…
tout ceci n’est vraiment pas pratique, vu que non seulement la locale est
dynamique, mais qu’en plus, pour commencer, elle est déterminée pendant
l’exécution, donc parfaitement inconnue à la compilation… du coup, je me demande
bien quelle locale le compilateur utilise pour coder une constante comme
L"bépo" ?
Cela me paraît pour ma part correct : par défaut, donc dans la locale C et
par conséquent dans le jeu de caractères d'exécution basique, chaque
caractère est censé être encodé sur un multiplet, soit sur un char.
ah, peut‐être pour la localeC, je n’avais pas compris le passage comme ça,
le « par défaut » m’avait échappé. peut‐être insister davantage dessus, et
préciser « c’est‐à‐dire dans la localeC » ?
Sinon, le soucis c'est qu'à ce stade la fonction strftime() n'a pas encore
été présentée…
reformuler alors, parce qu’« anecdotique » signifie que c’est sans importance.
Une mauvaise localisation.
de ce que je comprends de vos définitions, c’est bien d’internationalisation
qu’il s’agit. je ne sais pas si ces définitions sont vraiment intéressantes,
mais si c’est le cas, elles mériteraient d’être plus claires (je viens juste de
comprendre la différence).
autres trucs que j’avais oublié de relever :
le caractère d'appel
je ne sais pas vraiment ce que c’est… c’est le nom officiel ?
c’est bien d’illustrer l’usage des fonctions de conversion, mais les
exemples sont un peu limités. à quoi bon traiter des chaînes complètes si
c’est pour n’en regarder que le premier caractère à chaque fois ? moi
néophyte, en lisant ça, je ne vois pas bien la véritable portée de ces
fonctions. ce serait bien d’itérer sur les caractères en avançant à chaque
étape du bon nombre de multiplets, ça montrerait l’usage typique. vous
pourriez donner en code d’exemple un affichage étape par étape (comme dans
vos exemples actuels) pour bien voir ce qu’il se passe, et en exercice
proposer au lecteur d’implémenter mbstowcs.
Je ne pensais pas forcément à faire un chapitre par norme, mais vu le nombre de fonctions et notamment les ajouts du C99 comme fgetws(), wscanf(), etc. ça risque d'être très long pour un seul chapitre. Bref, je ne m'en sens pas le courrage pour l'instant.
Hmm… Ça me paraît plus simple en effet, ok va pour cette reformulation.
Tu connais déjà la réponse : ça dépend.
Je vais essayer de préciser cela.
Ça va si je dis simplement que nous n'en parlerons pas parce que les fonctions affectées n'ont pas été présentées ?
L'internationaliation est le fait de rendre un programme capable de s'adapter aux locales. Autrement dit, un programme C utilisant la bibliothèque standard est internationalisé (du moins quant aux fonctions de la bibliothèque standard qui le sont). Du coup, quant à la date, si elle est mal affichée, c'est un problème de localisation (parce que la bonne locale n'est pas employée).
Ça va si je dis simplement que nous n'en parlerons pas parce que les fonctions
affectées n'ont pas été présentées ?
oui c’est bien.
L'internationaliation est le fait de rendre un programme capable de s'adapter
aux locales. Autrement dit, un programme C utilisant la bibliothèque standard
est internationalisé (du moins quant aux fonctions de la bibliothèque standard
qui le sont). Du coup, quant à la date, si elle est mal affichée, c'est un
problème de localisation (parce que la bonne locale n'est pas employée).
ah non, je parle de programmes qui affichent la date avec par exemple "%a %b
%d" au lieu de "%c" ; on aura beau les exécuter dans une locale
francophone, ils afficheront les morceaux dans le mauvais ordre…1
1
2
3
4
5
6
7
8
$LC_ALL=C date +'%a %b %e %H:%M:%S %Z %Y'# bon pour l’anglais…Fri Aug 26 17:13:39 CEST 2016$LC_ALL= date +'%a %b %e %H:%M:%S %Z %Y'# … mais pas pour le françaisven. août 26 17:13:42 CEST 2016$LC_ALL=C date +'%c'# ce qu’il faut faireFri Aug 26 17:20:41 2016$LC_ALL= date +'%c'ven. 26 août 2016 16:51:05 CEST
note que le programme date lui‐même affiche par défaut un truc moisi sur un
bon nombre de systèmes (LC_ALL= date tout court me donne ven. août 26
16:50:41 CEST 2016), à cause des locales francophones de la glibc qui sont
effectivement bancales, à savoir copiées‐collées de en_US et seulement à
moitié traduites. ↩
Je ne suis pas certain que ton exemple soit correct. Si tu précises une suite de format particulier, la commande date va les afficher dans l'ordre que tu lui fournis, cela me paraît logique.
Voici la version mise à jour de la bêta.
Le chapitre sur les encodages a été modifié suivant les propositions de Maëlan et une erreur rapportée par PG06 a été corrigée dans le chapitre sur les limites des types.
Encore merci à tous les deux.
Ok, du coup peut-être vaudra t-il mieux renommer les tutos "Partie 1" et "Partie 2" pour que ça prête moins à confusion? Ou alors tout simplement découper en trois partie distinctes?
Super boulot en tout cas, comme d'hab les tutos ZdS même en bêta envoient du steak sur pluton par chronopost!
Ok, du coup peut-être vaudra t-il mieux renommer les tutos "Partie 1" et "Partie 2" pour que ça prête moins à confusion? Ou alors tout simplement découper en trois partie distinctes?
En fait, il n'y a qu'un seul tuto en ligne avec deux parties : « Les bases du langage C » et « Agrégats, mémoire et fichiers ». La partie ici en beta viendra donc à la suite. Nous avons préféré faire comme cela pour pouvoir continuer à effectuer les corrections ponctuelles sur le tuto en ligne pendant le développement de la troisième partie.