Partage
  • Partager sur Facebook
  • Partager sur Twitter

Discussions sur la FAQ du forum de C

Pas celle sur les bibliothèques tierces.

17 avril 2012 à 22:24:59

Citation : Taurre

C'est peut-être un peu radical de dire que leurs utilisations n'est pas portable (si l'on utilise un champs de bits de type unsigned int, on est certains de pouvoir stocker les valeurs de 0 à 2n-1). Mais, c'est vrai qu'il est sans doute mieux de leur proposer de réaliser eux-mêmes les opérations bits à bits.



Il faut ajouter à ça le fait que l’alignement et l’ordre des champs de bits en mémoire dépend de l’implémentation. Enfin, tout dépend de ce qu’on compte en faire. Si on veut stocker innocemment un entier dans un intervalle précis, on n’aura pas de problème.
(Au fait, j’ai posté cette entrée.)


Citation : Maëlan

Moui, je reste quand même d'avis que cela n'a pas sa place dans la FAQ C, en tous les cas pas en <attention>.



Ah oui, tu as raison sur ce point. Et avec un <information> ?
  • Partager sur Facebook
  • Partager sur Twitter
18 avril 2012 à 11:31:32

Citation : Maëlan


Ah oui, tu as raison sur ce point. Et avec un <information> ?



Cela m'a l'air effectivement mieux avec une balise <information> ;)
  • Partager sur Facebook
  • Partager sur Twitter
18 avril 2012 à 22:51:02

J’ai posté l’entrée sur les opérateurs. :)

L’index de la FAQ n’est toujours pas complété…
  • Partager sur Facebook
  • Partager sur Twitter
20 avril 2012 à 14:46:22

Citation : Maëlan

J’ai posté l’entrée sur les opérateurs. :)




J'ignorais l'existence de ce fil donc je continue ici la discussion entamée par MP avec Taurre et Maëlan concernant les opérateurs (Quels sont les opérateurs du langage C ? Comment se combinent-ils ?)


Ce que je leur disais en substance :

1°) Très bonne initiative que de compléter la FAQ. Noter bien que le placement dans la FAQ n'est pas adéquat puisque de toute évidence, vous ne répondez absolument pas à des questions fréquemment posées.

Il est vrai que c'est l'endroit le moins inadapté pour donner ce genre d'info.

Il existe sur le forum C quelques intervenants à la fois très connaisseurs des arcanes du langage et très rigoureux et sensibles aux difficultés d'apprentissage. Vous devriez vous associez pour essayer non pas de compléter une FAQ (une FAQ est une fausse bonne idée AMHA) mais organiser un document d'apprentissage d'approfondissement du langage C.


2°) Citer les ouvrages de référence et signaler s'ils sont faibles sur certains points. Concernant la notion d'opérateur, K&R est vraiment faible, Harbison & Steele ne dit rien.

3°) A propos de la définition donnée.

Citation : Maëlan


Un opérateur peut être défini comme le symbole d'une opération mathématique ou logique.



Définition :

-- trop imprécise (peut être défini comme, le terme impropre de symbole)
-- trop restrictive (un appel de fonction est une opération qui n'est ni une opération mathématique ni logique).



Je suis quand même gêné par le fait qu'on définisse un opérateur comme un symbole. Un symbole est de nature lexicale. La notion d'opérateur est syntaxique plus que lexicale et ne peut se définir indépendamment de la notion d'opérande. De même, sémantiquement, la notion d'opérateur est indissociable de la notion d'expression, ça c'est un point indiscutable et que l'on retrouve dans toutes les spécifications (Python, Java, C, C++, Ocaml, etc).

Je rappelle la définition que donnait C90 de la notion d'opérateur :

6.1.5 Operators
<...>
An operator specifies an operation to be performed (an evaluation) that yields a value, or yields a designator, or produces a side effect, or a combination thereof.

Maëlan proposerait la définition suivante :

Citation : définition d’« opérateur »

Un opérateur constitue, avec ses opérandes, un composant élémentaire d’une expression. Il spécifie une opération à appliquer à sa ou ses opérandes. Celle-ci peut produire une valeur calculée, avoir un effet de bord (une affectation par exemple) ou les deux.



ce qui me paraît être une amélioration du texte initial.

4°) Bien pour l'arité.

5°) Les notions sur la priorité des opérateurs et leur associativité sont à détailler davantage, il s'agit de notions difficiles pour un zéro.

Il faudrait signaler que, dans nos sociétés occidentales, la règle naturelle est de faire de l'associativité vers la droite. D'ailleurs, si on regarde le tableau dans le K&R2 section 2.12, sur une échelle de 15 niveaux de priorité, seuls 3 niveaux sont à priorité vers la gauche (et ce sont donc ces seuls niveaux qu'il suffit d'apprendre).



Il faudrait donner des exemples d'utilisation du tableau. Je proposais d'analyser les expressions suivantes :
*t[i].x++*2

et un autre exemple comme

2*3/4


qui me paraît également pertinent, beaucoup de gens vont faire une analyse sémantique de la syntaxe de l'expression, par exemple 3 baguettes coûtent 4 euros donc 2 baguettes coûteront 2*3/4=2*0,75=1,5 euro (ce que je veux dire : l'associativité vers la droite n'est pas si naturelle).

Je trouve les exemples donnés dans la FAQ à moitié convaincants :

a) il ne faut pas illustrer la notion de priorité en essayant de faire imaginer au lecteur une priorité qui va à l'encontre de ses usages : le lecteur ne va jamais considérer que 5 + 4 * 3 signifierait (5 + 4) * 3. Un exemple utilisant les opérateurs logiques me paraîtrait plus approprié.

b) il ne faut pas illustrer la notion d'associativité avec un exemple improbable tel que (a=b)=c puisqu'en C, l'expression a=b ne peut être une lvalue.


6°) Une légère ambiguïté mais qui reste difficile à dissiper :

Citation : Maëlan


La priorité des opérateurs permet de déterminer l’ordre dans lequel les opérations composant une expression seront effectuées.


Le problème est justement que l'ordre dans lequel les opérations sont effectuées est non spécifié :


The order in which subexpressions are evaluated and the order in which side effects
take place, except as specified for the function-call (), &&, ||, ?:, and comma
operators (6.5).



Je formule des critiques mais globalement j'ai trouvé l'article sur les opérateurs plutôt bien écrit, pertinent et, même en l'état, certainement susceptible d'aider un zéro.
  • Partager sur Facebook
  • Partager sur Twitter
20 avril 2012 à 16:04:14

Merci pour le résumé candide. :)

Je me permet de copier mon dernier MP, vu qu’il répond à certains points que tu soulèves ici.

------------------------

Citation : candide

Citation : Taurre

- comme ceci : A = (B = C);
- ou comme cela : (A = B) = C ?


Sauf que A=B ne peut-être une lvalue et donc l'exemple n'est pas convaincant (tu écriras jamais un truc du genre 42=C)


Je l’avais précisé dans la rédaction originale. Je suis quand même d’accord que ça rend l’exemple peu crédible. Celui que tu donnes, avec multiplication et division, me semble bien (même si je préférais une associativité vers la gauche, vu que M. Lambda est habitué à une associativité vers la droite avec les maths).

---

Comme Taurre, je suis parfaitement conscient que cette entrée n’a normalement pas sa place dans la FAQ. Ça ne répond pas à une question fréquente de débutants. Je voyais plus ça comme une sorte d’aide-mémoire, et je ne vois pas de meilleur endroit où le mettre.

Sinon, je suis assez d’accord avec les propositions qui ont déjà été faites (les opérateurs qui s’écrivent pareil, des exemples sous le tableau, la définition d’« opérateur »…).
Par rapport à la priorité des opérateurs qui « détermine l’ordre dans lequel les opérations sont effectuées », c’est vrai que ça peut porter à confusion mais je ne vois pas comment mieux l’expliquer en restant clair et concis (« détermine l’ordre dans lequel on fait les calculs lorsqu’ils s’emboîtent » ? « détermine la façon dont le compilateur parenthèse/emboîte/combine les sous-expressions d’une expression » ? :euh: ).

Proposition pour la définition d’opérateur :

Citation : définition d’« opérateur »

Un opérateur constitue, avec ses opérandes, un composant élémentaire d’une expression. Il spécifie une opération à appliquer à sa ou ses opérandes. Celle-ci peut produire une valeur calculée, avoir un effet de bord (une affectation par exemple) ou les deux.
[Quelques exemples…]

Une expression peut se définir comme la combinaison d’opérateurs et de leurs opérandes selon un ordre précis : elle peut se modéliser par un arbre dont les nœuds [non terminaux] constituent les opérateurs et les enfants de ses derniers sont leurs opérandes.
[Ce n’est pas tout à fait vrai pour les feuilles terminales, sauf si on joue sur les mots en inventant l’opérateur d’« identité » → récursion infinie…]
[Quelques exemples aussi…]



Mais là, au niveau du vocabulaire employé, ça commence déjà à devenir technique. Et on parle d’opérande à tout va, terme qui est défini après seulement. :-°
Le deuxième paragraphe n’est pas essentiel et est vraiment pointu, à voir s’il faut le garder.
  • Partager sur Facebook
  • Partager sur Twitter
20 avril 2012 à 17:09:40

Hm, honnêtement, je trouve que c'est finalement pas super important de savoir ce qu'est précisément un opérateur. Je pense que la différence fondamentale est entre opérateur (du langage) et fonction/macro (de la bibliothèque standard).

Ça a une conséquence importante pour le débutant (ou moins débutant) : tout ce qu'il sait sur les fonctions s'applique aussi aux fonctinos standards, tandis que pour les opérateurs, il faut apprendre au cas par cas, avec quelques généralités, mais pas tant que ça.

Moi je dirais juste un truc du genre : « Un opérateur est un élément du langage qui introduit un calcul (une opération), produisant un résultat (une valeur) et éventuellement un effet supplémentaire (p.ex. changer la valeur d'une variable). Les opérateurs font partie du langage C et aucun en-tête n'est nécessaire pour les utiliser. » Après, on peut faire une ptite classification selon comment ils s'écrivent (unaire pré/postfixé, binaire, ternaire, n-aire), en introduisant vite fait le mot « opérande » au passage (« l'opération s'applique à des opérandes dont le nombre dépend de l'opérateur ; p.ex. deux pour l'addition, mais un seul pour la négation »). C'est vague, certes, mais il faut se poser la question : a-t-on besoin d'en savoir plus ? Les détails du modèle syntaxique défini par la norme n'intéressent pas grand monde, à ce niveau, à mon avis.

Pour la priorité, je ne suis pas sûr qu'il soit utile d'insister ; c'est à la fois une notion intuitive et très difficile à décrire précisément sans entrer dans les détails de syntaxe... donc si on laissait ça à l'intuition ? :p Mais s'il faut donner une définition, je dirais : « Le C permet de combiner les opérateurs pour effectuer des calculs complexes. Lorsque plusieurs opérateurs sont utilisés ensemble, le résultat d'une opération sert d'opérande à une autre (p.ex. on peut faire la somme de 2*2 et de 3*4 ; le résultat des deux multiplications est alors réutilisé dans l'addition finale). La priorité spécifie l'ordre dans lequel lire le calcul, c'est-à-dire quelle opération sert d'opérande à quelle autre. En cas de doute, si vous avez l'impression qu'il y a plusieurs manières de lire un bout de code, consultez le tableau suivant : c'est l'opérateur le moins prioritaire qui s'applique au résultat de l'opérateur le plus prioritaire. » Puis des exemples, surtout ça...

C'est ptet pas idéal, mais au final je pense qu'au lieu d'essayer d'expliquer en un paragraphe un bout de théorie des langages formels, on ferait mieux de tenter d'atteindre l'intuition du lecteur avec une formulation pragmatique : en remplaçant la notion de syntaxe par celle de « comment on lit le code » ; c'est la même chose, quand on y pense, mais sans la rigueur, bien évidemment.
  • Partager sur Facebook
  • Partager sur Twitter
20 avril 2012 à 18:00:30

Citation : rz0

« Le C permet de combiner les opérateurs pour effectuer des calculs complexes. Lorsque plusieurs opérateurs sont utilisés ensemble, le résultat d'une opération sert d'opérande à une autre (p.ex. on peut faire la somme de 2*2 et de 3*4 ; le résultat des deux multiplications est alors réutilisé dans l'addition finale). La priorité spécifie l'ordre dans lequel lire le calcul, c'est-à-dire quelle opération sert d'opérande à quelle autre. En cas de doute, si vous avez l'impression qu'il y a plusieurs manières de lire un bout de code, consultez le tableau suivant : c'est l'opérateur le moins prioritaire qui s'applique au résultat de l'opérateur le plus prioritaire. »


Pas mal du tout comme vulgarisation, je trouve. :) C'est nettement plus intuitif que de parler d'arbre... :-°
  • Partager sur Facebook
  • Partager sur Twitter
20 avril 2012 à 18:02:03

Citation : rz0

Hm, honnêtement, je trouve que c'est finalement pas super important de savoir ce qu'est précisément un opérateur.



Disons que ça dépend des objectifs que l'on se donne. Il est certain que beaucoup d'acquisitions se font de façon implicite, sans verbalisation.

Citation : rz0

Je pense que la différence fondamentale est entre opérateur (du langage) et fonction/macro (de la bibliothèque standard).



Oui et non : il y a une différence syntaxique mais pas sémantique : un opérateur et une fonction réalisent une action. D'ailleurs, il suffit de voir en C++ il y a new et malloc qui cohabitent et font la même chose. D'ailleurs en Python par exemple, on peut surcharger des opérateurs avec des fonctions spéciales donc pas de différence sémantique.


Citation : rz0

Puis des exemples, surtout ça...




Absolument.


Citation : rz0


C'est ptet pas idéal, mais au final je pense qu'au lieu d'essayer d'expliquer en un paragraphe un bout de théorie des langages formels, on ferait mieux de tenter d'atteindre l'intuition du lecteur avec une formulation pragmatique : en remplaçant la notion de syntaxe par celle de « comment on lit le code » ; c'est la même chose, quand on y pense, mais sans la rigueur, bien évidemment.



En ce qui me concerne, les notions de priorité des opérateurs/associativité me sont restées fondamentalement mystérieuses pendant deux ou trois ans après avoir commencé le C. Disons que j'en avais juste une compréhension superficielle basée sur les banalités habituelles (x est prioritaire sur +) et qui ne prouvent rien.

Encore maintenant, je ne suis pas sûr que tout soit si clair. Par exemple, je ne suis pas sûr que je saurais écrire un algorithme de "prioritarisation" d'une expression à partir du tableau des priorités, leur arité et l'associativité. Ainsi, à partir de l'expression

v = fp[n].need[FP_BASE] >= 0 && v < n


il faudrait renvoyer l'expression équivalente :

v = ((((fp[n]).need)[FP_BASE] >= 0) && (v < n))


[en espérant que je me sois pas gouré ;) ] tiens, il existe pas un utilitaire C qui fait ça, dans le genre cdecl ?



Il est vrai qu'en pratique, ça ne sert à rien car les expression que l'on rencontre sont souvent syntaxiquement déchiffrable à cause de la sémantique (même si ce genre de déchiffrement comporte des risques).
  • Partager sur Facebook
  • Partager sur Twitter
20 avril 2012 à 18:14:29

Petit question,
A quoi sert l’opérateur virgule ?
  • Partager sur Facebook
  • Partager sur Twitter
20 avril 2012 à 18:21:14

Citation : florent m

Petit question,
A quoi sert l’opérateur virgule ?


Réunir un coupe d'expression en une seule. Le retour de la première est ignoré (l'expression est considérée de type void), le retour de la seconde est celui de l'expression totale (type et valeur).

En gros, on s'en sert peut car on peut tjrs faire autrement. Mais dans les for à plusieurs incrémentation ou plusieurs initialisation, c'est pratique.
  • Partager sur Facebook
  • Partager sur Twitter

🍊 - Étudiant - Codeur en C | Zeste de Savoir apprenez avec une communauté | Articles  - ♡ Copying is an act of love.

20 avril 2012 à 18:27:44

Citation : @che


En gros, on s'en sert peut car on peut tjrs faire autrement. Mais dans les for à plusieurs incrémentation ou plusieurs initialisation, c'est pratique.



C'est pratique aussi dans des macros, non ?



Citation : florent m

Petit question,
A quoi sert l’opérateur virgule ?




J'en parlais ICI et et aussi ICI (dans un ternaire) et encore ICI (dans un printf).

Bon attention à la valeur obfuscatoire de l'opérateur virgule ;)
  • Partager sur Facebook
  • Partager sur Twitter
20 avril 2012 à 20:17:45

Citation : candide

Encore maintenant, je ne suis pas sûr que tout soit si clair. Par exemple, je ne suis pas sûr que je saurais écrire un algorithme de "prioritarisation" d'une expression à partir du tableau des priorités, leur arité et l'associativité. Ainsi, à partir de l'expression



Ce n'est effectivement pas une question triviale, principalement à cause de la présence d'opérateurs tels que [] qui ont une syntaxe un peu biscornue, et de la définition euh pas vraiment claire de « priorité ». La notion de « priorité » des opérateurs est utilisée de manière très superficielle dans les grammaires après tout, typiquement pour ordonner des règles de production... D'autant plus que la priorité n'est pas une caractéristique essentielle au niveau syntaxique (sans elle, on accepterait les mêmes programmes syntaxiquement corrects, mais sémantiquement non équivalents). Dans beaucoup d'outils (je pense à yacc et ses successeurs), c'est surtout un hack pour écrire moins de règles tout en obtenant le même résultat vis-à-vis de l'arbre de dérivation produit. Après, je connais pas vraiment (ce n'est pas vraiment un sujet qui me passionne particulièrement :p), mais j'ai déjà entendu dire au détour d'une conversation qu'il y avait de la recherche autour de grammaires se basant sur un concept mieux défini de « priorité ».

Ceci dit, dans le cas du C, la grammaire officielle élimine ces confusions en spécifiant la priorité différemment, via des règles de production, directement, et sans avoir recours à une comparaison de la priorité des opérateurs ; mais je suppose que tu sais déjà ça. C'est pour ça qu'à mon avis, le tableau n'est jamais qu'une approximation et un aide-mémoire utile pour le programmeur, mais faut pas essayer de trop formaliser autour, parce que ce n'est pas le formalisme de la norme, dans tous les cas, donc on aura du mal à faire coller ça avec le texte officiel...

Citation : candide


v = fp[n].need[FP_BASE] >= 0 && v < n



il faudrait renvoyer l'expression équivalente :

v = ((((fp[n]).need)[FP_BASE] >= 0) && (v < n))



[en espérant que je me sois pas gouré ;) ] tiens, il existe pas un utilitaire C qui fait ça, dans le genre cdecl ?



Pas à ma connaissance. Il faudrait écrire un parser avec le bout de grammaire pertinent.


Citation : candide

Citation : @che


En gros, on s'en sert peut car on peut tjrs faire autrement. Mais dans les for à plusieurs incrémentation ou plusieurs initialisation, c'est pratique.



C'est pratique aussi dans des macros, non ?



Oui, je pense que ce sont là les deux cas d'usage principaux. Étant donné que le C fait la différence entre expression et instruction, c'est important d'avoir un moyen de produire des expressions plutôt que des instructions, quand on veut.
  • Partager sur Facebook
  • Partager sur Twitter
20 avril 2012 à 20:30:57

@ rz0 : De la rigueur ou de la vulgarisation, telle est la question. On avait choisi la vulgarisation, candide nous a repris sur les points qui manquaient de rigueur, et voilà. Tout dépend du public visé : si comme son placement dans la FAQ le suggère, ça s’adresse à des débutants, je priviligierai l’intuition. Sinon, on peut y mettre un peu plus de rigueur.
On n’est pas non plus en train de rédiger une norme, et les notions se comprennent très bien même si elles sont peut-être difficiles à formuler rigoureusement, c’est pourquoi je reviendrai à la première option, finalement (comment ça, versatile ?). M’enfin, ça n’empêche pas d’y mettre un peu de bonne parole à un moment si ça ne nuit pas à la clarté.

Citation : yoch

[…]
Pas mal du tout comme vulgarisation, je trouve. :) C'est nettement plus intuitif que de parler d'arbre... :-°



En effet. L’analogie avec l’arbre, c’est vraiment pas digeste (voir incompréhensible) pour un débutant. Là, c’est bien expliqué.


Je vais déjà éditer l’entrée (pas immédiatement, chuis occupé :-° ) pour y intégrer certaines propositions (notamment ce qui avait été dit par MP). On verra ce que ça donne déjà.

Édit : Voilà, c’est fait. J’ai intégré un peu tout ce qui a déjà été dit.


Citation : candide

J'en parlais ICI et et aussi ICI (dans un ternaire) et encore ICI (dans un printf).


Hé ben, tu vas les chercher loin. >_<
  • Partager sur Facebook
  • Partager sur Twitter
21 avril 2012 à 18:17:08

Une question comme sa pourquoi la FAQ C n'est-elle pas épingler ?
  • Partager sur Facebook
  • Partager sur Twitter
21 avril 2012 à 18:29:20

Ben… Elle l’est.

Ah non, plus maintenant (milekscuz). En effet, c’est curieux…

Au fait, en parlent de ça, il y a une faute dans le sous-titre de la FAQ C : il manque un accent circonflexe (si un modo passe par ici…).

Citation

Foire Aux Questions - la vôtre y est peut-être.

  • Partager sur Facebook
  • Partager sur Twitter
21 avril 2012 à 18:31:21

On doit pas avoir la même forum C, en épingler j'ai la "FAQ bibliothèque tierce" et "Tu viens d'arriver..."

J'avais raison non de diou
  • Partager sur Facebook
  • Partager sur Twitter
21 avril 2012 à 18:56:07

Ah une époque, les topic épinglés glissaient tout seul ...
Ils y en a même qui s'épinglaient tout seul ...
  • Partager sur Facebook
  • Partager sur Twitter

🍊 - Étudiant - Codeur en C | Zeste de Savoir apprenez avec une communauté | Articles  - ♡ Copying is an act of love.

22 avril 2012 à 3:13:53

Il est épinglé.

Vous ne le voyez pas en haut de la liste ? C'est un bug fort regrettable.

Citation : GuilOooo

Salut,

C'est toujours un post-it (j'ai bien le lien « desepingler ce sujet » et non « épingler ce sujet »), il n'apparaît simplement plus en haut des forums. C'est un bug connu, et je ne peux rien faire car dé-postiter/re-postiter n'aide pas. Il faudra probablement attendre la v4 pour avoir des posts-its qui marchent bien.

  • Partager sur Facebook
  • Partager sur Twitter
64kB de mémoire, c'est tout ce dont j'ai besoin
22 avril 2012 à 11:06:45

Ah ben ça alors…

Il n’apparait même pas dans la liste des sujets normaux, si on regarde par date de dernier post.


Au fait, j’ai édité l’entrée sur les opérateurs. J’ai essayé d’y intégrer à peu près toutes vos remarques. Qu’en pensez-vous ? Vous voyez encore des choses à reprendre ?
  • Partager sur Facebook
  • Partager sur Twitter
22 avril 2012 à 11:37:58

Citation : Maëlan

J’ai essayé d’y intégrer à peu près toutes vos remarques. Qu’en pensez-vous ?




Tu as fait de l'excellent travail, le texte est très clair avec le bon degré de complexité.
  • Partager sur Facebook
  • Partager sur Twitter
10 mai 2012 à 21:50:15

Yop,

Une petite question qui revient assez fréquemment:

[1][8] Comment peut-on lire un caractère sans appuyez sur Enter ?

La solution dépend de votre système d'exploitation:

- sous Windows, il est possible d'utiliser la fonction getch (déclarée dans l'en-tête conio.h);

- sous Unixoïde (GNU/Linux, BSD, Mac OS X, Solaris, ...), il est nécessaire de modifier les attributs du terminal (à l'aide des fonctions de l'en-tête termios.h), ce qui est assez compliqué... Heureusement, cela peut se faire facilement à l'aide de la fonction system (déclarée dans l'en-tête stdlib.h) et de la commande stty.

Voici un petit code d'exemple s'exécutant jusqu'à ce que vous appuyez sur 'q':


#ifdef __unix
#	include <stdio.h>
#	include <stdlib.h>
#else
#	include <conio.h>
#endif 


int
lire_caractere(void)
{
	int c;

#ifdef __unix
	system("stty -icanon min 1 time 0");
	c = getchar();
	system("stty icanon");
#else
	c = getch();
#endif
	return c;
}	


int
main(void)
{
	while (lire_caractere() != 'q')
		;

	return 0;
}
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
10 mai 2012 à 22:11:04

Il n'y a pas de bibliothèque qui fait ça facilement sous Unix ?
  • Partager sur Facebook
  • Partager sur Twitter
11 mai 2012 à 17:06:58

Citation : snlsdflkkl


Il n'y a pas de bibliothèque qui fait ça facilement sous Unix ?



Il y a les fonctions de l'en-tête termios.h, mais ce n'est pas spécialement simple. Un exemple:


#define _XOPEN_SOURCE 600
#include <stdio.h>
#include <termios.h>
#include <unistd.h>


int
lire_caractere(void)
{
	int c;
	struct termios term;

	if (tcgetattr(STDIN_FILENO, &term) < 0)
		return EOF;

	term.c_lflag &= ~ICANON;
	term.c_cc[VMIN] = 1;	
	term.c_cc[VTIME] = 0;

	if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &term) < 0)
		return EOF;

	c = getchar();
	term.c_lflag |= ICANON;
	tcsetattr(STDIN_FILENO, TCSAFLUSH, &term);
	return c;
}


int
main(void)
{
	lire_caractere();
	return 0;
}

  • Partager sur Facebook
  • Partager sur Twitter
11 mai 2012 à 18:35:47

Citation : Taurre

Il y a les fonctions de l'en-tête termios.h, mais ce n'est pas spécialement simple. Un exemple:

[…]



Bah, une fois que c’est enveloppé dans une fonction (ou deux), plus besoin de l’écrire. Chépa, j’trouve ça plus « propre » que d’utiliser system

Au passage, si on appelle la fonction mais que le terminal est déjà dans ce « mode », on le perdra… Pour faire complet, il faudrait sauvegarder les flags initiaux puis les restaurer à la fin.
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
11 mai 2012 à 19:18:21

merci Taurre pour cette info je ne savais pas :)
  • Partager sur Facebook
  • Partager sur Twitter
11 mai 2012 à 21:58:32

Citation : Maëlan


Bah, une fois que c’est enveloppé dans une fonction (ou deux), plus besoin de l’écrire. Chépa, j’trouve ça plus « propre » que d’utiliser system...



Personnellement, je ne fais pas partie de ceux qui considère que system est "sale", l'essentiel à mon sens est d'assurer la portabilité du programme et d'éviter d'éventuels problèmes de sécurité (ce qui en l'occurrence est normalement le cas).

Citation : Maëlan


Au passage, si on appelle la fonction mais que le terminal est déjà dans ce « mode », on le perdra... Pour faire complet, il faudrait sauvegarder les flags initiaux puis les restaurer à la fin.



Effectivement, tu soulèves un point important que j'avais complètement oublié :-°
Merci de l'avoir relevé ;)

Je corrige donc le code de ma précédente proposition d'entrée (en utilisant pas la fonction system puisque cette solution souffre du même problème):


#include <stdio.h>

#ifdef __unix
#	include <termios.h>
#	include <unistd.h>
#else
#	include <conio.h>
#endif


int
lire_caractere(void)
{
	int c;
#ifdef __unix
	struct termios term, tmp;

	if (tcgetattr(STDIN_FILENO, &term) < 0)
		return EOF;

	tmp = term;
	tmp.c_lflag &= ~ICANON;
	tmp.c_cc[VMIN] = 1;	
	tmp.c_cc[VTIME] = 0;

	if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tmp) < 0)
		return EOF;

	c = getchar();
	tcsetattr(STDIN_FILENO, TCSAFLUSH, &term);
#else
	c = getch();
#endif
	return c;
}


int
main(void)
{
	lire_caractere();
	return 0;
}

  • Partager sur Facebook
  • Partager sur Twitter
18 mai 2012 à 15:29:48

Je propose une entrée sur les tampons, mais c'est écrit un peu à la va-vite (il y a peut-être quelques imprécisions).

Citation

[x][y]Qu'est-ce que les tampons ? Comment les manipuler ?

En informatique, la lecture et l'écriture de caractères sur un support physique externe prend un temps constant important (le débit du périphérique étant souvent moins rapide que la cadence du processeur). Ainsi, le nombre de caractère ainsi manipulés est négligeable par rapport au temps constant nécessaire de la communication avec le périphérique.

En C notamment, on a introduit la notion de flux bufferisé. Son fonctionnement est simple : au lieu d'écrire effectivement chaque caractère à chaque appel de fprintf par exemple, on place le résultat dans une zone tampon temporaire (buffer en anglais), remplie au fur et à mesure, et dont le contenu sera vidé à un moment donné (que nous pourrons spécifier tout à l'heure).

De manière concrète, ce tampon se matérialise souvent au niveau du système d'exploitation par une chaîne de caractère, accompagnée d'un pointeur ou d'un indice. Il existe deux types de tampons :

  • les tampons par blocs : les données sont écrites lorsqu'une certaine taille est atteinte (mode utilisé par défaut pour, en théorie, tous les fichiers).
  • les tampons par lignes : les données sont écrites lorsqu'un caractère de saut de ligne est rencontré (mode utilisé par défaut pour les flux associés à un terminal, comme le flux de sortie standard stdout).



Il est possible de modifier un tampon dans un programme C, à l'aide de la fonction setvbuf (C89), qui doit être appelée avant toute opération sur le flux en question (mis à part son ouverture) :

int setvbuf(FILE *stream, char *buf, int mode, size_t size);



stream est le flux concerné, buf le nouveau tampon, size la taille de celui-ci et mode le type de tampon. mode est spécifié par une des constantes ci-dessous :

  • _IONBF : pas de tampon.
  • _IOLBF : tampon par lignes.
  • _IOFBF : tampon par bloc.



Si buf est nul, un tampon est alloué, dont la taille peut être size. Notez que le contenu du tampon à un moment donné est indéterminé.

Il arrive que certains caractères restent dans le tampon associé à un flux (par exemple, si le tampon est en mode _IOLBF et qu'aucun caractère de fin de ligne n'a été affiché). En temps normal, lors de l'arrêt du programme ou lors de la fermeture d'un fichier, les flux bufferisés sont vidés. Cependant, il arrive (notamment lors d'un appel à _Exit, ou bien lorsque le programme se termine par une boucle infinie) que ce ne soit pas le cas. Dans ces cas, on a souvent recours à la fonction fflush, qui vide le tampon associé à un flux passé en paramètre.

Hélas, vider le tampon associé à un flux d'entrée est un comportement indéterminé (notamment le vidage du flux d'entrée standard). Ainsi, l'exemple le plus courant se produit lorsque l'utilisateur rentre plus de caractères que prévus lors d'une saisie. Les caractères supplémentaires restent dans le tampon du flux d'entrée standard, et sont directement lus à la lecture suivante. Pour remédier à cela, on ne peut pas utiliser fflush, mais il suffit de consommer les caractères restants après une lecture suspecte :

// C99

static inline void
purger(void) {
    int c;
    while ((c = getchar()) != '\n' && c != EOF)
        ;
}

char *
read(char *s, int n) {
    if (fgets(s, n, stdin) != NULL) {
        char *peol;

        peol = strchr(s, '\n');

        if (peol != NULL)
            *peol = '\0';
        else
            purger();
    }

    return s;
}




Il faudrait trouver un titre un peu plus explicite peut-être également. Histoire de rediriger tous les vidages de buffer du forum.
  • Partager sur Facebook
  • Partager sur Twitter
Staff désormais retraité.
18 mai 2012 à 16:31:50

Je ne pense pas qu'une telle entrée sera à la portée de la plupart des zéros, à cause du vocabulaire technique, mais pourquoi pas, ça ne fera qu'enrichir la FAQ.

Quelques remarques pour plus de précisions :

Citation

les tampons par lignes : les données sont écrites lorsqu'un caractère de saut de ligne est rencontré (mode utilisé par défaut pour les flux associés à un terminal, comme le flux de sortie standard stdout).



Pour le flux stdout, ce comportement est surtout celui des unixoïdes me semble-t-il, ce n'est pas le cas sous windows du moins par défaut.

Citation

Il est possible de modifier un tampon dans un programme C, à l'aide de la fonction setvbuf (C89), qui doit être appelée avant toute opération sur le flux en question (mis à part son ouverture) :



On pourrait ajouter que d'après la C99, cette fonction ne peut être appelée qu'après avoir associé le pointeur stream à un flux ouvert et avant toute autre opération sur ce flux.

Citation

la fonction fflush, qui vide le tampon associé à un flux passé en paramètre.



Petite précision à rajouter, fflush inscrit les données restantes du buffer dans le flux ouvert avant de vider ce buffer.

Citation

Hélas, vider le tampon associé à un flux ouvert en lecture est un comportement indéterminé



Plus généralement, si la dernière opération effectuée sur ce buffer est une lecture alors le comportement de fflush n'est pas standard. Je pense aux modes w+, a+, r+.

  • Partager sur Facebook
  • Partager sur Twitter
18 mai 2012 à 16:44:28

Merci pour ton retour.

Citation : uknow

Je ne pense pas qu'une telle entrée sera à la portée de la plupart des zéros, à cause du vocabulaire technique, mais pourquoi pas, ça ne fera qu'enrichir la FAQ.



C'est sûrement ma manière de m'exprimer qui doit paraître pas vraiment claire, puisque le sujet en lui-même n'est pas très compliqué. A la rigueur, je peux essayer de reformuler la première partie de manière plus « simpliste », pour ceux qui ont été redirigés et qui ne connaissent pas du tout les tampons. Dans un deuxième temps, on peut assommer les plus curieux avec des détails un peu plus techniques.

Citation : uknow

Pour le flux stdout, ce comportement est surtout celui des unixoïdes me semble-t-il, ce n'est pas le cas sous windows du moins par défaut.



C'est possible ; j'avais en effet tiré l'information de ma page de documentation GNU. La norme ne me semble pas très claire sur ce point. Elle indique simplement que le flux d'entrée et de sortie standard sont bufferisés en bloc si et seulement si elles ne se réfèrent pas à un « interactive device » (dont la définition exacte et stricte par la norme m'échappe, j'ai donc viré sur un terminal).

Citation : uknow

On pourrait ajouter que d'après la C99, cette fonction ne peut être appelée qu'après avoir associé le pointeur stream à un flux ouvert et avant toute autre opération sur ce flux.



C'était ce que je tentais (vainement ?) d'exprimer avec la phrase. Je vais donc reformuler.

Citation : uknow

Petite précision à rajouter, fflush inscrit les données restantes du buffer dans le flux ouvert avant de vider ce buffer.



Oui, c'est une précision qui a son importance (ça me semblait implicite).

Citation : uknow

Plus généralement, si la dernière opération effectuée sur ce buffer est une lecture alors le comportement de fflush n'est pas standard. Je pense aux modes w+, a+, r+.



J'utilise rarement ces modes, de par les comportements étranges qui y sont associés et nécessaires. Ça permet de préciser un peu mes propos, merci.
  • Partager sur Facebook
  • Partager sur Twitter
Staff désormais retraité.