Tout à fait d'accord pour ces deux conditions.
Concernant la longueur ; 128 caractères.
Cet exemple concernait une question sur l'impression de chaine de caractère en hexa.
128 fois 5 caractères (ex.: 0x3f) c'est déjà pas mal sur une seule ligne.
Mais ceci ne m'empêche pas d'utiliser aussi des AnsiString (type assez proche de std::string en Borland)
L'omnipotence des pointeurs C correspond exactement à ce qu'elle est en assembleur (tout ce qui n'est pas pointeur est direct, en registre ou sous forme de macro texte).
C'est peut-être elle qui a justifié qu'on remplace le C par autre chose..
En revanche, je me demande s'il est possible d'écrire un parser autrement qu'en C. Le parser est au coeur de tous les langages, arbres binaires (huffmann (zip)), des expressions régulières et, pour certains, de tout le schedule de leurs software (remplace les [boucles infinies / switch case || if...elseif])
Il est réputé pour être l'un des algoritmes les plus solides du génie logiciel.
En télécom , remplacer le parser en C exposerait celui qui s'y risque à des problèmes de sécurité inextricables.
En fait le parser est partout. Il est même accessible dans Windows sous forme d'API.
Le code du parser (une vingtaine de lignes) est une invraisemblable gestion de pointeurs C sur code et sur données.
Cela dit, je comprends qu'on préfère moderniser la gestion mémoire pour les applications. Les systèmes parser-driven ont des codes difficiles à lire.
Un exemple : le navigateur web ouvre autant de sockets ip qu'il y a d'objets à charger. Chaque socket entre en lecture bloquante et renvoie ce qu'il reçoit à un parser configuré au préalable par le mime de l'objet lu. Si le serveur est saturé, la lecture bloque n'importe où, et surtout là où il ne faut pas... si c'est du javascript, en plein milieu d'une boucle... Le parser permet de dire ce qui est opérationnel et ce qui ne l'est pas, c'est le parser javascript (état courant) qui détermine si la boucle est exécutable ou non. Si elle n'est pas complètement chargée l'exécution javascript sera stoppée au bon endroit jusqu'à ce que la routine soit exécutable (dés qu'il rencontre l'accolade fermante).. Il en va de même pour les gif qui s'affichent partiellement en cours de chargement...
Le parser est synchrone avec la lecture, lorsqu'elle s'arrète, il s'arrète aussi mais le flux est interprété jusqu'au dernier byte reçu ! Aucun autre algoritme ne permet un tel synchronisme.
En plus de ce processus multithread, il faut pouvoir détruire tous ces sockets et leur instance de parser si l'utilisateur change de page (pex clic sur url ou history-1,...)
On peut comprendre que mozilla ou chrome tirent partie de cette synchronicité dont dépend la réactivité du navigateur, et s'il va geler (commandes inactives) jusqu'à ce que la page soit chargée en totalité ! ce gel serait une hérésie à notre époque, un tel navigateur serait noté 0 sur 10 par les'utilisateurs
Nous sommes donc , en tant qu'utilisateurs de navigateurs, très exigeants sur la fluidité de notre navigateur, fluidité qui dépend directement d'un subtil agencement de C et de C++ !
Entre 94 (mosaic) et 2001 (xp+ie) les mises à jour du socket windows étaient constantes, les memory leaks très fréquents et les pages en chargement gelaient même le desktop ! Winsock32 a mis au moins 7 ans à devenir stable, c'est dire si ces sujets sont sensibles pour le no1 mondial ! (on imagine les moyens mis en oeuvre pour stabiliser tcp/ip). Sous unix en revanche , les sockets marchent depuis 40 ans et le système n'a jamais gelé à cause de tcpip depuis sa naissance (tcpip a été conçu pour unix). Les langages C/C++ ne sont donc absolument pas en cause dans les problèmes qu'a rencontré µsoft qui avait une culture de lecture non bloquante incompatible avec la culture tcpip
Dernière modification par Invité ; 25/07/2010 à 19h51.
"std::vector<unsigned char /*ou autre typedef*/> MBR".
Je ne manipules jamais des trucs qui ne sont pas des chaines sous forme de string.
Et si vraiment je veux voir la version hexa d'une string (chose dont je n'ai jamais éprouvé le besoin jusqu'à présent), j'ai une classe utilitaire sous la main qui m'aurait permis de voir autrement ma chaine:
array_view<byte> view(reinterpret_cast<byte>(&ch[0]), ch.length());
Oui, c'est votre droit, mais je vous assure qu'on peut très bien manipuler les deux en toute tranquillité et en toute sécurité.Je ne manipules jamais des trucs qui ne sont pas des chaines sous forme de string.
Petite remarque tout de même, un std::string[], c'est à dire en utilisant le transtypage de [] est un char, donc sur 1 octet.
Sémantiquement, srd::string est faite pour manipuler des chaines. Pour des tableaux ... bof.
On pourrait aussi utiliser std::basic_string<unsigned char>, mais je crains quelques effets de bords (je ne m'en suis jamais servi)
Voila, c'est ça dont je parlais du "mélange des paradigmes". Ce n'est pas tant le mélange des styles de programmation (la partie syntaxique), mais plus la partie sémantique que je trouve complexe.
L'absence de bonnes pratiques dans ce domaine + l'absence de contraintes du langage donne beaucoup de liberté, le prix a payer étant le manque de cohérence possible (probable ?) du résultat.
Oui. En C++, la sémantique est plus critique qu'ailleurs. C'est un langage dont la parfaite connaissance de la syntaxe ne permet pas de dire que l'on ait compris quoique ce soit.
(mais bon ici ... suis-je le seul à être gêné par l'utilisation de string dans un contexte hors chaine de caractères, ou de ne pas voir l'intérêt d'un passage en hexa d'une chaine de caractères ?)
Exemple ceci
ou cela000131 00000305 | 3D 34 35 38 2E 30 32 36 | 20 20 59 3D 31 35 36 2E | =458.026 | Y=156. |
000141 00000321 | 30 30 30 20 20 5A 3D 35 | 36 2E 30 30 30 0D 0A 52 | 000 Z=5 | 6.000 R |
000151 00000337 | E9 73 75 6C 61 74 20 50 | 74 36 20 28 2D 31 35 2E | ésulat P | t6 (-15. |
000161 00000353 | 30 30 30 20 35 32 2E 30 | 30 30 20 2D 34 30 2E 30 | 000 52.0 | 00 -40.0 |
000171 00000369 | 30 30 29 20 58 3D 34 37 | 36 2E 33 30 38 20 20 59 | 00) X=47 | 6.308 Y |
000181 00000385 | 3D 31 34 30 2E 37 38 38 | 20 20 5A 3D 33 32 2E 32 | =140.788 | Z=32.2 |
000191 00000401 | 32 36 0D 0A 52 E9 73 75 | 6C 61 74 20 50 74 37 20 | 26 Résu | lat Pt7 |
0001a1 00000417 | 28 2D 31 35 2E 30 30 30 | 20 37 33 2E 35 33 30 20 | (-15.000 | 73.530 |
0001b1 00000433 | 34 2E 31 31 38 29 20 58 | 3D 34 36 32 2E 36 30 30 | 4.118) X | =462.600 |
Le premier lit un fichier et imprime par ligne l'ofset hexa, l'ofset décimal, 16 octets en hexa, et le mêmes en caractère.‰PNG(0D)
(0A)(1A)(0A)(00)(00)(00)(0D)
Le second imprime les caractères jusqu'à un séparateur désigné (0D) dans le cas présent, et à la ligne du dessous, la chaine correspondante en hexa.
Il est certain que si j'ai fait ces outils, c'est que j'en avais besoin.
Je ne comprend pas ce que vous voulez dire par là.Oui. En C++, la sémantique est plus critique qu'ailleurs. C'est un langage dont la parfaite connaissance de la syntaxe ne permet pas de dire que l'on ait compris quoique ce soit.
Quant à être le seul ... certainement pas, mais cela ne signifie pas pour autant que j'ai tord.(mais bon ici ... suis-je le seul à être gêné par l'utilisation de string dans un contexte hors chaine de caractères, ou de ne pas voir l'intérêt d'un passage en hexa d'une chaine de caractères ?)
Qu'on peut techniquement faire n'importe quoi, sans que ce soit une bonne "idée".Je ne comprend pas ce que vous voulez dire par là.
Comme pour ton exemple.
De même.(mais bon ici ... suis-je le seul à être gêné par l'utilisation de string dans un contexte hors chaine de caractères, ou de ne pas voir l'intérêt d'un passage en hexa d'une chaine de caractères ?)
Ca m'apparait comme un "code smell".
Je n'en doute pas un seul instant. Mais ... j'ai déjà un outil très bien pour cela : xxd qui s'intègre nativement à vim (c'est un sous-projet)
Par contre, je persiste que pour ce genre d'applications, ce que je vois, ce sont des fichiers binaires. Et j'évite toujours de manipuler des octets via des char, même s'il s'agit du type utilisé dans l'interface native des fonctions d'I/O du C et du C++. Et dans cette continuité, je ne manipule pas des std::string, mais des std::vector qui m'offrent une interface plus bas niveau, ou plus exactement une sémantique plus proche de celle des tableaux, d'octets ici.
En fait, c'est un résumé rapide de mes idées sur le sujet.
Une bonne utilisation du C++ requiert de distinguer les sémantiques d'entité et de valeur (cf FAQ), de comprendre des aspects de conception OO (ou comment bien choisir son héritage parmi 3), etc. Ce n'est pas parce que l'on connait la syntaxe que l'on va comprendre que copier des classes tirées de hiérarchies ne sert à rien (hormis complexifier le code et faire perdre du temps à tout le monde), ou que les tableaux de dégradation des accès (pri, pro, pub) sont moins importants que la dichotomie "utilisé en place de" Vs "importation de code".
Non.
Mais justement dans le cas d'un dump comme ici, on ne cherche généralement pas à afficher en hexadécimal le contenu d'une chaîne de caractère mais à afficher le contenu d'un tableau de byte sous sa représentation hexadécimal et, pour les valeurs pour lesquelles cela à un sens, le caractère correspondant à cette valeur.
Pour le second exemple, je ne vois pas vraiment le cas concret qui est derrière donc je ne serais pas aussi affirmatif, mais vu le contenu de l'exemple, il me semble que c'est également le cas.
Pour les deux exemples, il est bien évident, en tout cas pour moi, que ce type d'impression n'a aucun intérêt pour les fichiers choisis. J'ai pris le premier venu.
Pour expliquer le type d'utilisation, dans l'un et l'autre cas, quand on a un fichier binaire qui représente des articles, exemple shp, dbf, mid et Cie, si vous connaissez une meilleure méthode pour essayer de les comprendre, je suis preneneur (mais voila que j'essaye de me justifier, horreur)
De toute façon, il n'y a que moi qui puisse juger si cette méthode d'affichage est justifiée.
Concernant l'impression en Hexa. Cette question a été posée sur un forum de developpez.com (mais un autre sous-forum que celui-ci) Cet exemple m'a paru intéressant dans ce débat "défauts du C++". Mais cela ne va pas plus loin. Tout le monde a bien remarqué que j'avais mis les deux codes et que je n'avais ajouté aucun commentaire : je n'ai pas pris parti.
Mais apparemment, cette comparaison n'a pas plu à tout le monde. J'ai demandé des exemples pour compléter les affirmations, je n'en ai pas eu.
Enfin, il faudrait m'expliquer la différence entre
etafficher en hexadécimal le contenu d'une chaîne de caractère
Cela sous-entendrait-il qu'une chaine de caractère n'est pas un tableau de bytes ?afficher le contenu d'un tableau de byte sous sa représentation hexadécimal
De toute façon, comme je l'ai dit au début, cet outil est utilisé pour visualiser des fichiers, j'ai simplement répondu à une observation : afficher en hexa ne sert à rien.
Sémantiquement ce n'est pas du tout la même chose, tu ne manipules pas le même concept dans les deux cas.
Qu'un langage comme le C utilise un tableau de char terminé par un 0 pour représenter une chaîne de caractères n'est qu'un choix d'implémentation. Ca ne signifie pas qu'une chaîne de caractères et un tableau de bytes sont conceptuellement identiques.
En C la différence de sémantique entre ces deux notions est certes peu flagrante dans le conteneur utilisé (en gros simplement char vs. unsigned char) mais transparait par contre dans les fonctions de manipulations (utiliser les fonctions strXXXX sur autre chose qu'une chaîne de caractères n'est généralement pas une bonne idée).
En C++ la différence est plus évidente puisque des types (std::string, std::wstring) existent pour gérer les chaînes de caractères alors qu'un tableau de byte sera plutôt représenté par un std::vector<unsigned char> ou par un boost::array<unsigned char, X>.
1 char = 1 byte . Donc utiliser le type char pour les tableau de bytes est tout à fait normal...
Le mieux est cependant d'utiliser une classe adéquate (cf. les bytes arrays in Qt), cela simplifira largement les choses .
Fondamentalement, ils le sont .
Il faut préciser la taille, alors que pour les chaines de caractères, ce n'est pas nécessaire (c'est pour cela que les fonctions dont vous parlez ont toujours 2 versions (dont une permet de spécifier la taille )).
Ce n'est pas l'usage de char qui est choquant ici (quoique unsigned char serait certainement plus approprié) mais le fait d'utiliser une chaîne de caractère.
Ben justement non. En C, une chaîne de caractère est représenté par un tableau de char, mais ce sont bien deux notions différentes (tout tableau de char n'est pas une chaînes de caractères et d'autres langages implémentent les chaînes d'une autres manière).
Il ne faut pas confondre le concept et la représentation.
C'est plus compliqué que ça. Il faut certes fournir la taille mais il faut surtout utiliser une fonction n'ayant pas de sémantique de chaîne.
Tu peux prendre strncpy() qui a bien la taille en paramètre, tu risques d'avoir du mal à copier un tableau de byte qui contient un 0.
Et au passage les fonctions de manipulations de chaîne de caractères n'ont pas toutes une version dont on spécifie la taille, je n'ai par exemple pas encore vu de fonction strnlen() (qui aurait amha un intérêt assez limité), ou plus sérieusement strstr, strchr, etc.
Je maintiens que le type char peut être utilisé aussi bien pour les chaines de caractères que pour du code octet (aussi appelé byte code).
Vous connaissez memcpy ? C'est à ça que je pensais (en l'occurence ).
Je maintiens que fondamentalement, c'est la même chose .
1 caractère = 1 byte = 1 char (si vous préférez qu'on dise les choses ainsi ^^)
Pour être très complet, on pourrait rajouter 1 char = 1 unsigned char (c'est exactement chose, et la représentation en mémoire est la même ).
1 char égal à -1, c'est aussi 1 unsigned char égal à 255 (ce qui nous amène à l'équation -1 = 255 ^^ (et ainsi de suite)).
Justement, memcpy ne travaille qu'au niveau de la mémoire, sur des... bytes.
C'est, justement, ce que gl essaye de mettre en évidence: si memcpy et str(n)cpy existent tous les deux, ce n'est pas pour faire joli, ni pour avoir le plaisir de se compliquer la vie en devant choisir l'une des deux:
str(n)cpy va considérer le premier caractère nul '\0' comme... la fin de la chaine significative, et donc arrêter la copie des données.
memcpy va considérer un byte nul comme... ce qu'il est : un byte dont la valeur est 0, qui peut être une valeur tout à fait valide et non différente de 1 ou de 251.
Non...Je maintiens que fondamentalement, c'est la même chose .
C'est toujours un problème de sémantique.
une chaine de caractères représente... une succession de caractères imprimables susceptible de prendre une signification pour celui qui la lit.
un tableau de byte peut n'avoir strictement aucune signification particulière pour l'humain qui l'aurait devant les yeux tout en ayant une signification bien clair pour le programme qui l'utilise, par exemple, parce que 32 bytes pourraient en réalité représenter... 8 valeur numériques codées chacune sur 4 bytes.
on est bien d'accord qu'un caractère, c'est un char et qu'un char a la même taille qu'un byte, c'est d'ailleurs clairement dit dans la norme.1 caractère = 1 byte = 1 char (si vous préférez qu'on dise les choses ainsi ^^)
Mais un byte peut parfaitement être autre chose qu'un caractère: cela peut, tout simplement, représenter une valeur numérique comprise dans la plage de valeurs représentables grâce aux nombre de bits qui composent le byte (tu remarquera d'ailleurs que je ne précise pas ce nombre de bits, essentiellement parce qu'il n'est pas forcément égal à 8 )
La représentation en mémoire est identique, la valeur que tu donne à cette représentation est différente.Pour être très complet, on pourrait rajouter 1 char = 1 unsigned char (c'est exactement chose, et la représentation en mémoire est la même ).
1 char égal à -1, c'est aussi 1 unsigned char égal à 255 (ce qui nous amène à l'équation -1 = 255 ^^ (et ainsi de suite)).
Et c'est bien là tout le problème:
Quand tu travailles sur une chaine de caractères, tu va t'attendre à avoir:
- une ou plusieurs des 26 lettres minuscules
- une ou plusieurs des 26 lettres majuscules
- un ou plusieurs des dix symboles représentant les chiffres
- un ou plusieurs caractères plus ou moins ésotérique, comme les symboles mathématiques, monétaires, signe de ponctuation, espace ou autres caractères accentués.
Mais c'est oublier un peu vite que le premier caractère représentable a la valeur 32 (en décimal) et représente l'espace, et qu'il y a donc aussi les caractères ayant les valeurs comprises entre 0 et 31 qui ne sont absolument pas représentables (dont le 0 qui... représente la fin d'une chaine de caractères à sa première occurrence).
C'est aussi oublier un peu vite que le caractère nul peut aussi être... n'importe quel byte d'un type primitif codé sur plus d'un byte et donc, simplement, être à considérer comme... 8 bits mis à 0 (du moins, sur les architectures "grand public" classiques)
C'est pour le cas où il ne faut, justement, pas considérer un byte comme s'il s'agissait d'office d'un caractères qu'il faut... se donner la possibilité d'en maintenir une collection autrement que sous la forme d'une chaine de caractères.
Toute manipulation de fichier au format "binaire" mérite que l'on fasse cette distinction
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager