IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Joyeux anniversaire ANSI C : cela fait déjà 30 ans que le langage de programmation C a été normalisé
Petit tour d'horizon sur son évolution

Le , par Stéphane le calme

174PARTAGES

23  0 
Quels sont les éléments qui vous intéressent le plus dans C ?
Langage éprouvé (existe depuis les années 1970)
85 %
Portabilité
59 %
Robustesse
41 %
Langage dont se sont inspirés d'autres langages
39 %
Langage populaire
39 %
Modularité
34 %
Dynamique
17 %
Sensible à la casse
15 %
Sécurité
7 %
Autres (à préciser en commentaire)
15 %
Voter 41 votants
ANSI C est un nom commun désignant la norme du langage de programmation C et, bien que le document dont ce nom dérive ait été remplacé à plusieurs reprises depuis longtemps, le nom est encore parfois utilisé avec la norme actuelle. L'origine de ce nom standard (qui vient évidemment de l'American National Standards Institute), ainsi que le nom ISO C (également explicitement tiré de l'acronyme de l'Organisation internationale de normalisation), sont liés aux documents résultant du processus de consensus.

Le langage de programmation C est antérieur à toute normalisation officielle d'une décennie. C a été développé aux Laboratoires Bell en 1972 par Dennis Ritchie, et bon nombre des principes et des idées qu'il a incorporés dans le langage ont été empruntés aux ancêtres précédents du langage de programmation B et B, BCPL et CPL. En 1983, l'Institut national américain de normalisation (ANSI) a formé un comité de normalisation (X3J11) du langage qui a abouti en 1989 à la norme dite ANSI C ou C89 (formellement ANSI X3.159-1989). Le langage de programmation C a donc été ratifié le 14 décembre 1989. Ce standard originel a unifié les pratiques existantes avec quelques nouveaux ajouts. Il a fallu attendre le printemps 1990 pour que cette norme soit également adoptée par l'Organisation internationale de normalisation (C90, C ISO, formellement ISO/CEI 9899:1990). ANSI C est une évolution du C K&R qui reste extrêmement compatible. Elle reprend quelques idées de C++, notamment la notion de prototype et les qualificateurs de type.

À cette époque, la norme spécifiée dans le document ANSI X3.159-1989 est devenue ANSI C, mais elle a rapidement été remplacée, car elle a été adoptée en tant que norme internationale, ISO / IEC 9899: 1990, dans le cadre des travaux de l'ISO / IEC JTC 1. Bien que ce soit l'origine du nom ISO C, la norme nationale et la norme internationale ont également été différenciées respectivement C89 et C90. Au cours des années qui ont suivi l'établissement de la norme internationale ISO / CEI 9899, plusieurs révisions et plusieurs rectificatifs ont été publiés. La quatrième édition de la norme, ISO / IEC 9899: 2018 - Technologies de l'information - Langages de programmation - C définit le langage de programmation C actuel.

Quelques fonctionnalités de C qui en font toujours un langage populaire

C est un langage de programmation impératif et généraliste. Il est qualifié de langage de bas niveau dans le sens où chaque instruction du langage est conçue pour être compilée en un nombre d'instructions machine assez prévisible en termes d'occupation mémoire et de charge de calcul. En outre, il propose un éventail de types entiers et flottants conçus pour pouvoir correspondre directement aux types de données supportés par le processeur. Enfin, il fait un usage intensif des calculs d'adresse mémoire avec la notion de pointeur6.

Hormis les types de base, C supporte les types énumérés, composés, et opaques. Il ne propose en revanche aucune opération qui traite directement des objets de plus haut niveau (fichier informatique, chaîne de caractères, liste, table de hachage…). Ces types plus évolués doivent être traités en manipulant des pointeurs et des types composés. De même, le langage ne propose pas en standard la gestion de la programmation orientée objet, ni de système de gestion d'exceptions. Il existe des fonctions standards pour gérer les entrées-sorties et les chaînes de caractères, mais contrairement à d'autres langages, aucun opérateur spécifique pour améliorer l'ergonomie. Ceci rend aisé le remplacement des fonctions standards par des fonctions spécifiquement conçues pour un programme donné.


C'est un des langages les plus utilisés, car :
  • il existe depuis longtemps, le début des années 1970 ;
  • il est fondé sur un standard ouvert ;
  • de nombreux informaticiens le connaissent ;
  • il permet la minimisation de l'allocation mémoire nécessaire et la maximisation de la performance, notamment par l'utilisation de pointeurs ;
  • des compilateurs et bibliothèques logicielles existent sur la plupart des architectures ;
  • il a influencé de nombreux langages plus récents dont C++, Java, C# et PHP ; sa syntaxe en particulier est largement reprise ;
  • il met en œuvre un nombre restreint de concepts, ce qui facilite sa maîtrise et l'écriture de compilateurs simples et rapides ;
  • il ne spécifie pas rigidement le comportement du fichier exécutable produit, ce qui aide à tirer parti des capacités propres à chaque ordinateur ;
  • il permet l'écriture de logiciels qui n'ont besoin d'aucun support à l'exécution (ni bibliothèque logicielle ni machine virtuelle), au comportement prévisible en temps d'exécution comme en consommation de mémoire vive, comme des noyaux de système d'exploitation et des logiciels embarqués.

Nous pouvons également souligner d'autres éléments comme :
  1. Sa portabilité : il s'agit de l'utilisabilité du même fragment de code dans différents environnements. Les programmes C peuvent être écrits sur une plateforme et exécutés sur une autre avec ou sans aucune modification.
  2. Sa Modularité (langage structuré) : cette caractéristique du langage C permet au programme d'être divisé en unités plus petites et exécuté individuellement à l'aide de fonctions. La programmation modulaire fait donc référence à la technique de conception logicielle qui augmente le nombre de fragments du même code. Par exemple, vous voulez trouver l'aire d'un carré, d'un rectangle et d'un triangle. Au lieu d'écrire le code dans son ensemble, vous pouvez le diviser en fonctions distinctes, une pour trouver l'aire d'un carré, d'un rectangle et d'un triangle respectivement. Il garantit moins de risques d'erreurs et le rend visuellement attrayant et plus organisé.
  3. Sa simplicité et son efficacité : le style de syntaxe de la programmation C est facile à comprendre et peut être utilisé pour concevoir des applications qui étaient précédemment conçues par le langage d'assemblage.
  4. Sa vitesse : comme il s'agit d'un langage basé sur un compilateur, il est relativement plus rapide que d'autres langages de programmation comme Java ou Python, qui sont basés sur un interpréteur. Un compilateur considère l'ensemble du programme comme une entrée et génère ainsi un fichier de sortie avec le code objet tandis qu'un interpréteur prend instruction par instruction en entrée puis génère une sortie, mais ne génère pas de fichier.
  5. Sa popularité : c'est l'un des langages les plus utilisés dans le développement de systèmes d'exploitation et embarqués.
  6. Son dynamisme : il prend en charge la fonction DMA (Dynamic Memory Allocation), qui aide à l'utilisation et à la gestion de la mémoire. Parmi toutes les fonctionnalités de C, le dynamisme est unique. À l'aide de DMA, la taille d'une structure de données peut être modifiée pendant l'exécution à l'aide de certaines fonctions prédéfinies dans la bibliothèque C telles que malloc(), calloc(), free() et realloc().
  7. Le respect de la casse : il traite les caractères minuscules et majuscules différemment. Par exemple, si nous déclarons une variable «x» de type entier, cela signifierait une signification complètement différente si nous tapons «X» plutôt que «x».



Ses principaux inconvénients sont :
  • le peu de vérifications offertes par les compilateurs d'origine (K&R C), et l'absence de vérifications à l'exécution, ce qui fait que des erreurs qui auraient pu être automatiquement détectées lors du développement ne l’étaient qu’à l'exécution, donc au prix d’un plantage du logiciel ;
    • sous UNIX, on pouvait utiliser les utilitaires lint et cflow pour éviter de tels mécomptes ;
    • des vérifications sont ajoutées avec le temps, mais elles restent partielles ;

  • son approche de la modularité restée au niveau de ce qui se faisait au début des années 1970, et largement dépassée depuis par d'autres langages :
    • il ne facilite pas la programmation orientée objet ;
    • il ne permet pas de créer des espaces de noms ;

  • la gestion d'exceptions très sommaire ;
  • le support très limité de la généricité, malgré l’introduction des expressions à type générique en C11 ;
  • les subtilités de l'écriture de programmes portables, car le comportement exact des exécutables dépend de l'ordinateur cible ;
  • le support minimaliste de l'allocation de mémoire et des chaînes de caractères, ce qui oblige les programmeurs à s'occuper de détails fastidieux et sources de bugs ; il n'y a notamment pas de ramasse-miettes standard ;
  • les bogues graves qui peuvent être causés par un simple manque d'attention du développeur ; tel le dépassement de tampon qui constitue une faille de sécurité informatique exploitable par les logiciels malveillants ;
  • certaines erreurs ne peuvent être détectées automatiquement qu'à l'aide d'outils supplémentaires et non standardisés, comme lint et Valgrind.

Source : blog ANSI

Et vous ?

Développez-vous en C ?
Dans le cadre de développements personnels ou en entreprise ?
Quels sont les éléments qui vous intéressent le plus dans ce langage ?
Quels sont ceux qui vous posent le plus de problèmes ?

Voir aussi :

« Le langage J offre une meilleure approche de l'itération que le C et d'autres », d'après ses concepteurs
Xilinx propose Vitis pour faciliter la programmation de FPGA, avec un compilateur C et des bibliothèques faciles à l'emploi
« Pourquoi le C est mon meilleur choix pour programmer des jeux vidéo », d'après un travailleur de la filière qui s'appuie aussi sur le C++ pour ses projets commerciaux
« Rust est le futur de la programmation système et C le nouvel assembleur », d'après un ingénieur d'Intel qui explique pourquoi il est pertinent de passer à Rust
C2Rust : un outil qui permet de faire la traduction et la refactorisation de votre code écrit en langage C vers le langage Rust

Une erreur dans cette actualité ? Signalez-nous-la !

Avatar de Metalman
Membre expert https://www.developpez.com
Le 16/12/2019 à 18:52
Comme SofEvans...

Je ne comprends pas les arguments sur le pointeur NULL à 0...
En ASM et dans le processeur c'est ce qu'il se passe (il n'y a pas de valeur "magique"/exception/non-data qui détecte qu'un pointeur est alloué ou non).

J'ajouterais même : les arguments sur la sécurité sont un peu vides...
Ce n'est pas parce que les développeurs ne savent pas faire de C correctement, que le C est un langage qui ne devrait plus être utilisé. (et pas besoin de chercher les problèmes chez Microsoft pour trouver des failles bien pires chez Linux sur bash par exemple... ou descendre un poil dans les couches en dessous avec les "choix" de Intel...)

...enfin les arguments sur les headers, les chaînes de caractères... pareil : c'est creux. Ca correspond exactement au fait que c'est un langage juste au dessus de l'ASM, et donc il faut gérer soi-même beaucoup de choses. Ca tombe bien : quand on est juste au dessus du processeur, c'est exactement ce qu'il faut pouvoir faire.

Dire que le C n'est pas fait pour le codeur du dimanche, oui, ok, ça se tient.
Mais dire que le C est nul sur la gestion des pointeurs NULL, des chaînes de caractères (qui est plutôt un choix sur la philosophie "ligne à ligne" plutôt que "champ de taille fixe prédéfini", des headers, et de la sécurité... euuuuuuuh... non.
18  2 
Avatar de Bktero
Modérateur https://www.developpez.com
Le 16/12/2019 à 15:57
Perso, je n'utilise plus de ce langage. Je fais du C++ à la place. Je ne vois que 2 bonnes raisons à continuer à utiliser du C :
- pas de possibilité de faire du C++ (pas de compilateur dispo, interdiction sur le projet)
- l'équipe a la flemme de se former

Sa portabilité : il s'agit de l'utilisabilité du même fragment de code dans différents environnements. Les programmes C peuvent être écrits sur une plateforme et exécutés sur une autre avec ou sans aucune modification.
Ca relève un peu du troll ça aussi... Quand tu vois la quantité d'implementation defined behaviors, unspecified, et undefined behaviors (c'est pas bien on en fait tous, des fois sans faire exprès) dans la norme, faire un code vraiment portable, c'est souvent utopique.
10  1 
Avatar de Bousk
Rédacteur/Modérateur https://www.developpez.com
Le 16/12/2019 à 22:41
- Pour permettre des lib header-only et donc rien à installer et compiler en plus, juste une directive #include
- Pour pemettre de créer du code via le préprocesseur avec des fichiers de listing de valeurs via macro que l'on inclue et dont on modifie le comportement au besoin.
- C n'apporte pas de sécurité et fournit juste une syntaxe qui est aisément transcriptable en assembleur et code machine. Et il fait ça plutôt bien.
- Pourquoi elles seraient un mauvaix choix ? Ça permet de connaître la taille de la chaîne avec un surcoût fixe : 1 octet (pour des chaînes de char*). Sinon ta chaîne pourrait faire X octets si elle contient moins de 255 caractères ? Puis après X+1 ? Et comment/où se trouve l'octet supplémentaire ? Doit-on toujours allouer 4 octets avant la chaîne au cas où ? Pourquoi se contenter de 4 ?
- Oui la localisation est toujours un problème. Aujourd'hui on voit des chaînes de string<uint32_t> pour ça... mais est-on sûr que ça couvre enfin tous les cas ? Et pour combien de temps ?
- En quoi une valeur spécifique NULL est un problème ? Un pointeur, c'est fait pour pointer sur quelque chose. Pouvoir lui dire qu'il pointe sur rien ne me parait pas aberrant. Doit-on aussi interdire la valeur 0 pour un int ?
- Dès que tu as des inputs utilisateurs, l'impossibilité des crash est le travail du programmeur et du programme. Si l'utilisateur te donne 4 entiers puis demande que tu lui retournes le 5ème de la liste, je vois mal comment la compilation peut gérer ça.
9  1 
Avatar de Kannagi
Expert éminent sénior https://www.developpez.com
Le 16/12/2019 à 16:36
Le C , je l'utilise que pour mes projet personnel par exemple j'ai créer un SDK en C pour coder sur Neo Geo :

(Les routines bas niveau sont en asm).

Je l'utilise aussi pour créer une lib multiplateforme 3D pour la Dreamcast/PS2/GameCube/PC
(la lib es un mélange de C/ASM)
Screen PS2 :

Sreen DC:

Cela permet d'avoir un code unique qui permet de viser toutes ces plateformes ! :p

Mais je ne suis plus trop fan de ce langage , à cause des ces nombreux UB et d'une gestion de la mémoire source de bug.
6  0 
Avatar de rt15
Membre éclairé https://www.developpez.com
Le 17/12/2019 à 11:52
Concernant les pointeurs nulls, il y a deux sujets.

Le premier, c'est les pointeurs nulls et la "billion-dollar mistake" de les avoir inventés.
Cette erreur ne s'applique pas seulement au C, mais aussi au Java et au C# par exemple.
Le problème est que les pointeurs (ou références) nulls peuvent générer des erreurs (NullPointerException en Java, NullReferenceException en C#) à l'exécution.
Les compilateurs protègent mal contre ces erreurs même si on peut améliorer les choses avec les annotations (@CheckForNull...) + analyse statiques en Java (Sonar....) et C# 8 introduit les références nullables.

Cette erreur d'avoir introduit les pointeurs nulls s'appliquent surtout aux langages de haut niveau et a coûté effectivement très cher.
En C qui est plus bas niveau c'est très cohérent d'avoir ces pointeurs nulls.
Contrairement au Java/C#, le problème en C ne se résume pas aux pointeurs nulls. On peut aussi avoir très facilement des pointeurs qui pointent au mauvais endroit. (Après la fin d'un tableau, sur de la mémoire libérée...)

Le deuxième sujet sur le fait que null correspond à la valeur 0 je ne comprends pas trop.
Peut être que Astraya mélange avec un exploit qui se base sur le déréférencement de pointeurs nulls.
Cet exploit a été (est ?) utilisé par les hacker pour faire des élévations de privilège.

Comme ce n'est que pour de l'élévation de privilège, le hacker doit s'être déjà introduit dans un processus, par exemple en exploitant une faille de type stack buffer overflow.
Ensuite, le hacker map de la mémoire à l'adresse 0 dans ce processus. Ce n'est pas toujours possible, certains systèmes d'exploitations ne le permettent pas ou plus.
À l'adresse 0, le hacker met en place son shell code.
Ensuite, le hacker fait un appel système en lui donnant un pointeur null (qui ne doit pas être vérifié par le kernel) et qui doit être utilisé comme adresse d'une fonction à appeler par le noyau (de préférence, mais ça peut aussi être des données même si c'est moins pratique).
Le noyau va appeler le shell code à l'adresse 0. Ce shell code sera exécuté par le noyau avec tous les privilèges.
Cet exploit profite du fonctionnement interne du système d'exploitation, de la gestion de la mémoire et des appels systèmes.
Pour rappel, le noyau est mappé (mais inaccessible) dans tous les processus utilisateurs, "au début" de la mémoire (donc juste après la mémoire 0).
Quand on fait un appel système, le mapping du user space est conservé lorsque la fonction du noyau s'exécute, pour des raisons de performance.
Donc ce qui se trouve à l'adresse 0 dans le processus utilisateur se trouve à l'adresse 0 et accessible dans l'appel du noyau correspondant (mais si un autre processus fait un appel système, il n'y aura pas la même chose à l'adresse 0 dans le kernel).

Pour résumé, cette faille (grave) reste peu utilisée et ne fait que de l'élévation de privilège donc elle reste marginale par rapport au dépassement de tampon par exemple.

https://utcc.utoronto.ca/~cks/space/blog/linux/KernelPageZeroProblem
https://blog.xpnsec.com/hevd-null-pointer/
Je suis tombé sur ce message où Linus dit qu'il ne veut pas "corriger".

Concernant Rust comme remplaçant du C, je vous propose de jeter un oeil sur les dernières lignes de ce fichier :
https://github.com/rust-lang/rust/blob/master/src/libstd/sys/unix/fd.rs

On est dans le coeur de Rust, et ce qu'il se passe lors de l'appel d'un destructeur d'un descripteur de fichier.
Rust ne vérifie pas la valeur de retour !
C'est une erreur de programmation.

Mais le problème est beaucoup plus profond que ça.
Pourquoi la valeur de retour n'est elle pas vérifiée ?
Car Rust ne serait pas quoi faire de l'erreur !
C'est un problème similaire au RAII du C++ qui ne sait pas quoi faire des exceptions dans les destructeurs.

Rust a pris selon moi une très mauvaise décision avec la destruction implicite des ressources similaire au RAII du C++.
Ils auraient dû imposer une destruction explicite et imposer la gestion des erreurs. drop devrait retourner quelque chose comme Result<(), Error>.
Le compilateur devrait vérifier que l'on appelle les destructeurs. Pas les appeler pour nous.

À part ça ce qui m'intéresse dans le vieux C c'est qu'il n'évolue plus, ou du moins que l'on peut se passer de ses évolutions.
Cela fait qu'un très vieux code est encore au goût du jour, lol !
Ce qu'il manque dans le C, ce n'est pas du tout les dernière innovation à la mode, c'est les trucs bien bas niveau.
Les compilateurs compensent les manques mais du coup c'est chiant de faire du code portable, genre __sync_fetch_and_or de gcc, _InterlockedOr de VC ou encore __builtin_bswap16 de gcc et _byteswap_ushort de VC.
Genre l'asm dans le C sous gcc qui n'a rien à voir avec l'asm dans le C sous VC.
La librairie standard d'origine est un peu une usine à bug aussi genre scanf qui semble conçu pour permettre au hacker de faire des dépassements de tampons ou strtok qui ne supporte pas d'être appelée depuis plusieurs threads.

L'autre point génial du C c'est les performances bien sûr et la qualité du code machine généré qui contient peu (mais quand même trop !) de choses rajoutées dans le dos du développeur.
6  0 
Avatar de transgohan
Expert éminent https://www.developpez.com
Le 17/12/2019 à 13:55
Citation Envoyé par SofEvans Voir le message
Créer une pointeur NULL et le déréférencé crash aussi.
Je trouve que ton argumentaire est un peu de mauvaise foi, même si, comme pour Pyramidev, je suis d'accord sur le fait que sacrifier les avantages du C (rapidité, mémoire, simplicité) en faveurs de plus de garde-fous, de contrôle (etc etc) est un must-have.
Néanmoins, quand on fait du C, on doit être rigoureux. Plus que dans d'autres langage.
Donc quand on manipule des pointeurs, on test si ceux-ci peuvent être NULL.

Et une fois le programme fini, on déroule les tests.
Et dans les tests, il doit au moins y avoir valgrind ou Dr Memory histoire de voir si les pointeurs ont été correctement géré.

C'est sûr, c'est plus long, c'est moins adapté à l'industrie actuel, mais c'est vitale, parce que sinon il suffit d'une erreur d'inattention pour faire un sigsev.
Mais bon, c'est pas comme si on pouvait pas faire des erreurs d'innatentions dans d'autres langages.
Je pense que je vais m'arrêter avec cette quote d'une excellente réflexion sur ce débat stérile où tout le monde veut avoir raison en ne prenant en compte qu'un segment de marché.

Pour anecdote je ne compte plus le nombre de nouvel embauché ou prestataire qui vient me dire que ça serait bien de remplacer toute notre base de code par du Rust parce que c'est le mieux qui se fait et que faut se mettre à jour. Bah je leur répond qu'il serait temps qu'ils pensent bas niveau et qu'ils me trouvent comment compiler du Rust pour des vieux processeurs comme du Motorola 68000 et qu'on en reparlera. J'attends toujours, depuis bizarrement ils font du C et ils ne s'en plaignent pas.
J'ai un peu l'impression de voir la même chose sur ce forum, tout le monde pense à son petit jardin sans penser au marché mondial qui comporte bien plus d'architectures que ce qu'ils peuvent imaginer.
On est en 2019 et on utilise encore bien plus de technologies des années 1980 que ce que vous pouvez imaginer.
Et c'est aussi pour cette raison, avec ce matériel éprouvé, que vous pouvez prendre le train, l'avion, la voiture, avec une sécurité qui me semble accru par rapport à des technologies sorties l'an passé.

Le C a encore de beaux jours devant lui, car tous les nouveaux langages ne prennent pas en compte les vieilles architectures.
Or en industrie on utilise souvent des équipements qui nécessitent 15 à 20 ans de qualification avant d'être utilisé.
Je ne serai pas étonné que le C ait encore une grosse part de marché dans 20 ans.

Oui il y a toujours mieux. Mais critiquer un langage parce que d'autres font mieux c'est un peu se voiler la face...

Citation Envoyé par Astraya
Pour la partie sécurité, je ne pensais pas à du hacking mais plus a un truc du genre: tu programmes un logiciel pour les trains, même si ça crash et que tout est redondé, c'est un problème de sécurité des personnes physiquement. Et ne répondez pas, il faut des outils et des tests grrrrr
Il suffit de sérieux... Et ce peut importe la technologie utilisée, qu'elle te prenne par la main ou non...
Même en Rust on peut faire des conneries qui peuvent tuer des vies.

Bref...
Dans 50ans on aura une autre technologie et un débat qui crachera sur le Rust peut être.
7  1 
Avatar de SofEvans
Membre émérite https://www.developpez.com
Le 17/12/2019 à 9:57
Citation Envoyé par Astraya Voir le message

Code : Sélectionner tout
1
2
3
4
5
int* p = 0;
int i = *p; // .... et la?
//ou bien
int& ref = *p; // .... et la? Une référence est censé être toujours valide non?
Régle n°1 quand on déréference un pointeur pouvant être NULL : on test si le pointeur est NULL avant de déréférencer.

Navré d'être brutale, mais là ton exemple c'est juste absolument n'importe quoi.
C'est comme si j'ecrivais

char *chaine = "quarante-deux";
int nombre = *chaine;
et que je disais que l'erreur de design du langage fait que nombre ne vaille pas 42.

Ensuite ton deuxième exemple n'est même pas du C.

Pour finir, depuis quand une référence, en langage C, est-elle toujours censé être valide ? Et c'est quoi cette référence d'ailleurs ? Un autre nom des pointeurs ?
6  1 
Avatar de Pyramidev
Expert éminent https://www.developpez.com
Le 16/12/2019 à 22:58
Bonjour.

Citation Envoyé par SofEvans  Voir le message
Quel est le problème avec le fait que NULL "vaille" 0 (même si ce n'est, à priori, pas tout le temps vrai) en langage C ?

Le problème du langage C avec les pointeurs nuls est que, au niveau du typage, le langage C ne distingue pas les adresses obligatoires des adresses optionnelles : dans les deux cas, on prend un type de pointeur, qui est une adresse optionnelle. Cette faiblesse dans le typage favorise les bogues. Par exemple, admettons qu'une certaine fonction Config const* User_get_config(const User* user) retourne une configuration optionnelle, car certains utilisateurs n'ont pas encore de config. Un jour, un utilisateur distrait, habitué à ce que la plupart des fonctions qui retournent des pointeurs ne retournent pas de pointeurs non nuls, va oublier de se demander si c'est bien le cas de cette fonction. Il va alors faire comme si le pointeur retourné n'était jamais nul. Dans le meilleur des cas, l'erreur sera interceptée par un test unitaire. Dans le pire des cas, le bogue se retrouvera en production.

Dans un langage statiquement typé qui gère correctement les données optionnelles, les données optionnelles ont un type distinct des données non optionnelles et, pour extraire une donnée d'une donnée optionnelle, on peut passer par une opération de pattern matching qui évite les erreurs d'étourderie. Par exemple, en Rust, on peut faire ceci :
Code Rust : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct Config { content: i32 } 
  
struct User<'a> { config: Option<&'a Config> } 
  
impl<'a> User<'a> { 
    fn get_config(&self) -> Option<&'a Config> { 
        self.config 
    } 
} 
  
fn main() { 
    let my_config = Config { content: 42 }; 
    let user_with_config = User { config: Some(&my_config) }; 
    let user_without_config = User { config: None }; 
    for user in [&user_with_config, &user_without_config].iter() { 
        match user.get_config() { 
            Some(conf) => println!("The config content of this user is {}.", conf.content), 
            None => println!("This user does not have any config."), 
        } 
    } 
}
Résultat de l'exécution :
Code : Sélectionner tout
1
2
The config content of this user is 42. 
This user does not have any config.
Dans cet exemple, user.get_config() est une référence optionnelle vers un objet de type Config. On ne peut pas écrire directement user.get_config().content, car la configuration est optionnelle. À la place, on peut utiliser l'opérateur match qui fait ici une sorte de if-else sur l'existence de la config. Si la config existe, alors on va dans la branche Some(conf) => dans laquelle on a accès à la configuration via la variable conf. Si la config n'existe pas, alors on va dans la branche None => dans laquelle la variable conf n'est pas visible.

Citation Envoyé par Metalman  Voir le message
J'ajouterais même : les arguments sur la sécurité sont un peu vides...

Le fait que le langage facilite des failles de sécurité par étourderies est une faiblesse comparé à des langages qui empêchent ces étourderies ou les rendent plus difficiles à faire.

Citation Envoyé par Astraya  Voir le message
- Les chaînes de caractères terminant par '\0' qui est responsable de moulte bugs.
- Les macros sont un problème et non une solution.
- Le système header est un gouffre de temps de compilation et de collision de symbol.

Citation Envoyé par Metalman  Voir le message
...enfin les arguments sur les headers, les chaînes de caractères... pareil : c'est creux. Ca correspond exactement au fait que c'est un langage juste au dessus de l'ASM, et donc il faut gérer soi-même beaucoup de choses. Ca tombe bien : quand on est juste au dessus du processeur, c'est exactement ce qu'il faut pouvoir faire.

Les headers en langage C sont bien inutilement lents à compiler. En effet, si un fichier ".h" est inclus indirectement par n fichiers ".c", alors ce fichier ".h" sera parsé n fois. (Les headers précompilés sont une exception à la règle.)
Les collisions de noms de macros sont aussi un problème. Quand un code inclut un fichier ".h", il n'y a pas de moyen de dire "depuis ce fichier .h, j'importe tels et tels symboles, mais pas les autres", ce qui éviterait, entre autres, d'importer des macros non voulues. Le système modulaire du langage C est foireux.

Les chaînes de caractères terminées par '\0' n'auraient pas dû être le standard des chaînes de caractères. À la place, il aurait fallu avoir des couples (pointeurs vers le premier caractère, nombre d'éléments). C'est bien pratique quand on veut modéliser des sous-chaînes d'une chaîne existante sans réallouer de la mémoire, typiquement quand on fait certaines opérations de parsing. Le cas des chaînes de caractères terminées par '\0' aurait dû être secondaire.

Citation Envoyé par Metalman  Voir le message
Dire que le C n'est pas fait pour le codeur du dimanche, oui, ok, ça se tient.

Le langage C n'est pas fait pour les experts non plus, entre autres car il entrave la mise en place d'abstractions.
6  2 
Avatar de FatAgnus
Membre chevronné https://www.developpez.com
Le 17/12/2019 à 7:30
Citation Envoyé par Bktero Voir le message
Perso, je n'utilise plus de ce langage. Je fais du C++ à la place. Je ne vois que 2 bonnes raisons à continuer à utiliser du C :
- pas de possibilité de faire du C++ (pas de compilateur dispo, interdiction sur le projet)
- l'équipe a la flemme de se former
Et dans quelle catégorie se trouve les développeurs du noyau Linux ou de GIT ? Linus Torvalds trouve le langage C++ est un langage horrible et ne l'a pas choisi pour une des deux raisons précédentes. Le C++ est tellement bien que certains projets adoptent Orthodox C++ (un sous-ensemble minimal de C++ qui améliore le C, mais évite toutes les choses inutiles du C++ moderne).

Le choix qui manque dans le sondage est la simplicité du langage C (à l'opposé de la complexité du C++), mais aussi le fait que le langage C soit très proche du langage machine.
5  1 
Avatar de Bktero
Modérateur https://www.developpez.com
Le 17/12/2019 à 9:05
Citation Envoyé par FatAgnus Voir le message
Et dans quelle catégorie se trouve les développeurs du noyau Linux ou de GIT ? Linus Torvalds trouve le langage C++ est un langage horrible et ne l'a pas choisi pour une des deux raisons précédentes. Le C++ est tellement bien que certains projets adoptent Orthodox C++ (un sous-ensemble minimal de C++ qui améliore le C, mais évite toutes les choses inutiles du C++ moderne).

Le choix qui manque dans le sondage est la simplicité du langage C (à l'opposé de la complexité du C++), mais aussi le fait que le langage C soit très proche du langage machine.
Catégorie 1 bien sûr. Le noyaux Linux est bien antérieur (1991 d'après Wikipédia) à la 1ère normalisation de C++ (1998). On ne peut pas vraiment dire qu'à l'époque, il(s) avai(en)t le choix. Et aujourd'hui c'est interdiction sur le projet et ça se comprend car un seul langage permet de garder une uniformité. Pour Git, vu que Linus n'aime pas C++, il a choisi C (et là on entre dans "interdiction sur le projet".

Après, tu as l'inverse : GCC est depuis un bon moment codé en C++, alors qu'historiquement il était codé en C.
Citation Envoyé par https://gcc.gnu.org/gcc-4.8/changes.html
GCC now uses C++ as its implementation language. This means that to build GCC from sources, you will need a C++ compiler that understands C++ 2003. For more details on the rationale and specific changes, please refer to the C++ conversion page.
J'ai lu la page d'Orthodox C++. Il ya des choses pas débiles. Certaines sont mêmes des règles de codage de Google, comme le fait de ne pas utiliser les exceptions ou le RTTI. Beaucoup tombent finalement dans des règles de bonnes conduites des projets C++ modernes. D'autres me paraissent un brin foireuse ("Don't use stream (<iostream>, <stringstream>, etc.), use printf style functions instead." --> mais c'est génial pour facilement afficher des objets de types perso ça !), ou tellement évidentes ("Don't use anything from STL that allocates memory, unless you don't care about memory management." --> oh ! std::vector fait de l'allocation dynamique et je ne veux pas faire d'allocation dynamique alors je ne dois pas utiliser std::vector? Merci Captain Obvious !). Bon par contre :
Due to lag of adoption of C++ standard by compilers, OS distributions, etc. it's usually not possible to start using new useful language features immediately.
C'est juste faux : il suffit d'aller sur https://en.cppreference.com/w/cpp/compiler_support et de voir le support massif de C++17 par les compilateurs principaux et de voir à quel point C++20 est déjà pas mal suivi.
5  1