Quel (s) avantage (s) des littéraux de chaîne étant en lecture seule justifient (-ies / -ied):
Encore une autre façon de se tirer une balle dans le pied
char *foo = "bar"; foo[0] = 'd'; /* SEGFAULT */
Incapacité à initialiser avec élégance un tableau de lecture-écriture de mots sur une seule ligne:
char *foo[] = { "bar", "baz", "running out of traditional placeholder names" }; foo[1][2] = 'n'; /* SEGFAULT */
Compliquant la langue elle-même.
char *foo = "bar"; char var[] = "baz"; some_func(foo); /* VERY DANGEROUS! */ some_func(var); /* LESS DANGEROUS! */
Économiser de la mémoire? J'ai lu quelque part (je n'ai pas pu trouver la source maintenant) il y a longtemps, lorsque la RAM était rare, les compilateurs ont essayé d'optimiser l'utilisation de la mémoire en fusionnant des chaînes similaires.
Par exemple, "more" et "regex" deviendraient "moregex". Est-ce encore vrai aujourd'hui, à l'ère des films numériques de qualité Blu-ray? Je comprends que les systèmes embarqués fonctionnent toujours dans un environnement de ressources limitées, mais la quantité de mémoire disponible a quand même considérablement augmenté.
Problèmes de compatibilité? Je suppose qu'un programme hérité qui tenterait d'accéder à la mémoire morte planterait ou continuerait avec un bogue non découvert. Par conséquent, aucun programme hérité ne devrait essayer d'accéder au littéral de chaîne et, par conséquent, autoriser l'écriture dans le littéral de chaîne ne nuirait pas aux programmes hérités valides, non piratés et portables .
Y a-t-il d'autres raisons? Mon raisonnement est-il incorrect? Serait-il raisonnable d'envisager une modification des littéraux de chaîne en lecture-écriture dans les nouvelles normes C ou au moins d'ajouter une option au compilateur? Cela a-t-il été envisagé avant ou mes "problèmes" sont-ils trop mineurs et insignifiants pour déranger qui que ce soit?
Réponses:
Historiquement (peut-être en réécrivant certaines parties), c'était le contraire. Sur les tout premiers ordinateurs du début des années 1970 (peut - être PDP-11 ) exécutant un C embryonnaire prototypique (peut-être BCPL ), il n'y avait pas de MMU ni de protection de mémoire (qui existaient sur la plupart des ordinateurs centraux IBM / 360 plus anciens ). Ainsi , chaque octet de mémoire (y compris ceux qui manipulent des chaînes littérales ou code machine) pourrait être remplacé par un programme erroné (imaginez un programme changeant certains
%
à/
une printf (3) chaîne de format). Par conséquent, les chaînes et constantes littérales étaient accessibles en écriture.Adolescent en 1975, j'ai codé au musée du Palais de la Découverte à Paris sur des ordinateurs anciens des années 1960 sans protection mémoire: IBM / 1620 n'avait qu'une mémoire centrale - que vous pouviez initialiser via le clavier, il fallait donc taper plusieurs dizaines de chiffres pour lire le programme initial sur des bandes perforées; Le CAB / 500 avait une mémoire à tambour magnétique; vous pouvez désactiver l'écriture de certaines pistes via des commutateurs mécaniques près du tambour.
Plus tard, les ordinateurs ont obtenu une forme d'unité de gestion de la mémoire (MMU) avec une certaine protection de la mémoire. Il y avait un périphérique interdisant au CPU d'écraser une sorte de mémoire. Ainsi, certains segments de mémoire, notamment le segment de code (aka
.text
segment) sont devenus en lecture seule (sauf par le système d'exploitation qui les a chargés à partir du disque). Il était naturel pour le compilateur et l'éditeur de liens de placer les chaînes littérales dans ce segment de code, et les chaînes littérales sont devenues en lecture seule. Lorsque votre programme a essayé de les écraser, c'était mauvais, un comportement non défini . Et avoir un segment de code en lecture seule dans la mémoire virtuelle offre un avantage significatif: plusieurs processus exécutant le même programme partagent la même RAM ( mémoire physiquepages) pour ce segment de code (voir l'MAP_SHARED
indicateur pour mmap (2) sous Linux).Aujourd'hui, les microcontrôleurs bon marché ont une mémoire en lecture seule (par exemple leur Flash ou ROM) et y conservent leur code (et les chaînes littérales et autres constantes). Et les vrais microprocesseurs (comme celui de votre tablette, ordinateur portable ou de bureau) ont une unité de gestion de mémoire sophistiquée et un mécanisme de cache utilisé pour la mémoire virtuelle et la pagination . Ainsi, le segment de code du programme exécutable (par exemple dans ELF ) est mappé en mémoire en tant que segment en lecture seule, partageable et exécutable (par mmap (2) ou execve (2) sous Linux; BTW vous pouvez donner des directives à ldpour obtenir un segment de code accessible en écriture si vous le vouliez vraiment). L'écrire ou en abuser est généralement un défaut de segmentation .
La norme C est donc baroque: légalement (uniquement pour des raisons historiques), les chaînes littérales ne sont pas des
const char[]
tableaux, mais uniquement deschar[]
tableaux dont l'écrasement est interdit.BTW, peu de langues actuelles autorisent l'écrasement des littéraux de chaîne (même Ocaml qui, historiquement et mal) avait des chaînes littérales inscriptibles, a récemment changé ce comportement en 4.02, et a maintenant des chaînes en lecture seule).
Les compilateurs C actuels sont capables d'optimiser et d'avoir
"ions"
et de"expressions"
partager leurs 5 derniers octets (y compris l'octet nul final).Essayez de compiler votre code C dans un fichier
foo.c
avecgcc -O -fverbose-asm -S foo.c
et regardez à l'intérieur du fichier assembleur généréfoo.s
par GCCEnfin, la sémantique de C est suffisamment complexe (en savoir plus sur CompCert et Frama-C qui essaient de le capturer) et l'ajout de chaînes littérales constantes inscriptibles le rendrait encore plus obscur tout en rendant les programmes plus faibles et encore moins sécurisés (et avec moins comportement défini), il est donc très peu probable que les futures normes C acceptent des chaînes littérales inscriptibles. Peut-être au contraire en feraient-ils des
const char[]
tableaux comme ils devraient être moralement.Notez également que pour de nombreuses raisons, les données mutables sont plus difficiles à gérer par l'ordinateur (cohérence du cache), à coder, à comprendre par le développeur, que les données constantes. Il est donc préférable que la plupart de vos données (et notamment les chaînes littérales) restent immuables . En savoir plus sur le paradigme de programmation fonctionnelle .
Dans les anciens jours Fortran77 sur IBM / 7094, un bogue pouvait même changer une constante: si vous
CALL FOO(1)
et s'il vousFOO
arrivait de modifier son argument passé par référence à 2, l'implémentation aurait pu changer d'autres occurrences de 1 en 2, et c'était vraiment un bug vilain, assez difficile à trouver.la source
const
dans la norme ( stackoverflow.com/questions/2245664/… )?1
se comportent soudainement comme des2
s et un tel plaisir ...let 2 = 3
fonctionnait). Cela a eu pour résultat beaucoup de FUN (dans la définition du mot Dwarf Fortress), bien sûr. Je n'ai aucune idée de la façon dont l'interprète a été conçu pour permettre cela, mais c'était le cas.Les compilateurs ne pouvaient pas se combiner
"more"
et"regex"
, parce que le premier a un octet nul après lee
moment où le second en a unx
, mais de nombreux compilateurs combineraient des littéraux de chaîne qui correspondaient parfaitement, et certains correspondraient également à des littéraux de chaîne partageant une queue commune. Le code qui change un littéral de chaîne peut donc changer un littéral de chaîne différent qui est utilisé à des fins entièrement différentes mais qui contient les mêmes caractères.Un problème similaire se poserait dans FORTRAN avant l'invention de C. Les arguments étaient toujours transmis par adresse plutôt que par valeur. Une routine pour ajouter deux nombres serait donc équivalente à:
Dans le cas où l'on voudrait passer une valeur constante (par exemple 4.0) à
sum
, le compilateur créerait une variable anonyme et l'initialiserait4.0
. Si la même valeur était transmise à plusieurs fonctions, le compilateur transmettrait la même adresse à toutes. Par conséquent, si une fonction qui modifiait l'un de ses paramètres passait une constante à virgule flottante, la valeur de cette constante ailleurs dans le programme pourrait être modifiée en conséquence, conduisant ainsi au dicton "Les variables ne le seront pas; les constantes ne sont pas 't ".la source