OVH Cloud OVH Cloud

Priorité et associativité des opérateurs

125 réponses
Avatar
Greydavayar
Bonjour a tous,

je lis partout le terme "d'associativit=E9" des op=E9rateurs ainsi que le
teme de "priorit=E9" des op=E9rateurs je pense avoir compris celui de la
priorit=E9 mais cette notion d'associativit=E9 reste tr=E8s (trop) floue
pour moi et je ne trouve rien qui l'explique clairement sur le net.

Bonne soir=E9e

10 réponses

Avatar
Antoine Leca
Pierre Maurette écrivit :
Enfin, j'ai remarqué que:
a | b & c | d
a & b | c & d
a || b && c || d
a && b || c && d
génèrent un warning, alors que:
a + b * c + d
a * b + c * d
laissent gcc muet. Est-ce logique ?



Pas vraiment, mais on peut raisonnablement arguer que les deux dernières
formulations ne sont équivoques que pour 0,01% des programmeurs C (qui
d'ailleurs contribuent en grande part à ce
fil ;-) ), tandis que les premières formulations, quoique non
ambiguës, ont tendance à hérisser le poil de nombre de programmeurs
(enfin, c'est surtout vrai des deux premières : les 3 et 4 me paraissent
complètement naturelles, et l'avertissement un tant
soit peu abusif ; en fait, j'ai même cherché un peu un langage sans
priorité relative entre && et ||, et je suis bredouille : même VB
fait la différence).


Antoine
Avatar
Antoine Leca
JKB écrivit :
Le Mon, 06 Sep 2010 17:12:45 +0200,
Antoine Leca écrivait :
JKB écrivit :
Le coup du pointeur NULL qui vaut 0x0, c'est exactement pour moi du
même tonneau que getc() qui renvoie un int,





C'est très simple : à force d'écrire des trucs comme while(*ptr++),
on ne se pose plus la question. Implicitement, on considère que le
tableau termine par un NULL. Si ton NULL vaut 0x1, ton code part
dans le mur. C'est du même tonneau que les gens qui considèrent que
char est un type pour retenir un caractère, ce qui est faut parce
que le caractère EOF est un INT et ne tient que rarement dans un
char.



Mmmm, non.

Le premier est la conséquence du refus de considérer comme légale la
convention C (et de nombreux langages dérivés) qui dit qu'un pointeur
nul (NULL) a une valeur nulle (fausse), et par conséquent invalide les
(nombreux) raccourcis qui exploitent cette propriété.

Le second est la conséquence du système de types de C qui considère que
char est un sous-type de int, utile pour économiser la place mais pas
vraiment pour faire des opérations ; déclarer une variable simple de
type char (qui est le réel problème, getchar() est juste un symptôme)
est de ce fait une faute au sens du système de types de C.


On est bien d'accord si tu veux dire que le système de types de C n'est
pas celui du Pascal (et tes deux problèmes sont dans le même tonneau en
Pascal, je ne pense que ce soit dû au hasard) ;
mais je ne suis pas d'accord avec le style caricaturé par
#define BEGIN {
#define END }
parce que cela mène immanquablement à des gros problèmes avec feof()...


Antoine
Avatar
espie
In article , JKB <invalid> wrote:
Le Mon, 06 Sep 2010 17:12:45 +0200,
Antoine Leca écrivait :
JKB écrivit :
Le coup du pointeur NULL qui vaut 0x0, c'est exactement pour moi du
même tonneau que getc() qui renvoie un int,



Tu peux développer STP ? Je vois bien un piège avec le type (et le
domaine de valeurs) retourné par getc(), et je vois bien un autre piège
avec des échanges indus entre NULL et 0 (du genre que tu décris et
d'autres), mais je n'arrive pas à faire l'association entre les deux pièges.



C'est très simple : à force d'écrire des trucs comme while(*ptr++),
on ne se pose plus la question. Implicitement, on considère que le
tableau termine par un NULL. Si ton NULL vaut 0x1, ton code part
dans le mur. C'est du même tonneau que les gens qui considèrent que
char est un type pour retenir un caractère, ce qui est faut parce
que le caractère EOF est un INT et ne tient que rarement dans un
char.



Bon, deja, faut arreter de melanger les choses. NULL, c'est une macro definie
dans stddef.h. Sa valeur n'a qu'un vague rapport avec le pointeur nul et
sa semantique.

C'est important, parce que tu es en train de nous parler d'un machin qui n'est
pas du C. Ca y ressemble, certes, mais ca n'est pas ANSI C 99, ni ANSI C 89,
ni ISO C 90, ni meme K&R.

Lorsque tu ecris while(*ptr++);
NULL n'a rien a voir dans l'histoire. Ce qui compte, c'est ce que devient
la valeur de *ptr dans un test.

La, tu es en train de nous dire que tu as une machine ou ce truc-la ne
marche pas pour parcourir un tableau de pointeur, parce que ca ne va
pas s'arreter sur le pointeur nul, si j'ai bien suivi ?

C'est bizarre et non-conforme a toutes les normes de C que j'ai jamais
vues... et pourtant, dieu sait qu'il y a des notes un peu partout dans la
norme pour prevoir des cas bizarres !

C'est TOTALEMENT different du comportement de getc et de EOF qui ne rentre
pas dans un char. Dans un cas, c'est une chausse-trappe valable pour toute
personne qui veut faire du C portable.

Dans le 2e cas, c'est une bizarrerie dans un langage qui a un vague rapport
avec le C, dans lequel certaines personnes s'obstinent a programmer.

J'irais meme jusqu'a dire que c'est hors-sujet pour ce newsgroup...
Avatar
Pierre Maurette
Antoine Leca, le 06/09/2010 a écrit :
Pierre Maurette écrivit :
Enfin, j'ai remarqué que:
a | b & c | d
a & b | c & d
a || b && c || d
a && b || c && d
génèrent un warning, alors que:
a + b * c + d
a * b + c * d
laissent gcc muet. Est-ce logique ?



Pas vraiment, mais on peut raisonnablement arguer que les deux dernières
formulations ne sont équivoques que pour 0,01% des programmeurs C (qui
d'ailleurs contribuent en grande part à ce
fil ;-) )



Il faut dire que 99,99% des gens qui /font du C/ ont écrit à l'école
ab+cd et (a+b)(c+d).

, tandis que les premières formulations, quoique non
ambiguës, ont tendance à hérisser le poil de nombre de programmeurs
(enfin, c'est surtout vrai des deux premières : les 3 et 4 me paraissent
complètement naturelles, et l'avertissement un tant
soit peu abusif ; en fait, j'ai même cherché un peu un langage sans
priorité relative entre && et ||, et je suis bredouille : même VB
fait la différence).



La trituration sur les expressions booléennes se faisaient au brouillon
- et sur la copie il me semble bien - en utilisant + pour le OU, la
juxtaposition pour le ET et la barre pour la négation. La factorisation
fonctionne, après il faudrait rentrer dans la théorie des ensembles
pour expliciter les limites de cette assimilation.

--
Pierre Maurette
Avatar
JKB
Le Mon, 6 Sep 2010 16:12:12 +0000 (UTC),
Marc Espie écrivait :
In article , JKB <invalid> wrote:
Le Mon, 06 Sep 2010 17:12:45 +0200,
Antoine Leca écrivait :
JKB écrivit :
Le coup du pointeur NULL qui vaut 0x0, c'est exactement pour moi du
même tonneau que getc() qui renvoie un int,



Tu peux développer STP ? Je vois bien un piège avec le type (et le
domaine de valeurs) retourné par getc(), et je vois bien un autre piège
avec des échanges indus entre NULL et 0 (du genre que tu décris et
d'autres), mais je n'arrive pas à faire l'association entre les deux pièges.



C'est très simple : à force d'écrire des trucs comme while(*ptr++),
on ne se pose plus la question. Implicitement, on considère que le
tableau termine par un NULL. Si ton NULL vaut 0x1, ton code part
dans le mur. C'est du même tonneau que les gens qui considèrent que
char est un type pour retenir un caractère, ce qui est faut parce
que le caractère EOF est un INT et ne tient que rarement dans un
char.



Bon, deja, faut arreter de melanger les choses. NULL, c'est une macro definie
dans stddef.h. Sa valeur n'a qu'un vague rapport avec le pointeur nul et
sa semantique.



Permets-moi de ne pas être d'accord.

Lorsque tu écris if (ptr == NULL), tu te contrefiches de la valeur
de ptr et de NULL, tu regarde juste si ces deux valeurs sont égales.
NULL peut être défini par n'importe quoi.

Lorsque tu écris if (ptr), tu testes l'adresse pointée par ptr et tu
regardes si elle est nulle, ce qui est totalement différent. Les
deux tests sont identiques si et seulement si NULL vaut 0x0.
Et je ne vois pas comment le compilo peut s'en sortir de façon
simple.

Je ne me suis jamais posé la question de savoir si NULL doit valoir
0x0, je connais juste quelques contre-exemples.

Maintenant, tu vas me dire que les tableaux de pointeurs doivent se
terminer par 0 et non par un NULL. Peut-être. Le tout est de garder
une cohérence. Où on utilise partout NULL et on teste des égalités à
NULL, où on utilise 0 et on se permet des trucs du style (ptr) en
faisant gaffe aux fonctions qui renvoient des NULL.

Bref, tant que NULL vaut 0, tout se passe pour le mieux. Lorsque ce
n'est plus vrai, on commence à rigoler. Personnellement, ça fait
plus de vingt ans que je me tape du C sur des systèmes bizarres et
je préfère de loin une parenthèse en trop ou un test (ptr == NULL)
que de rechercher des bugs à la noix dans des milliers de lignes de
code.

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
Avatar
espie
In article ,
Pierre Maurette wrote:
La trituration sur les expressions booléennes se faisaient au brouillon
- et sur la copie il me semble bien - en utilisant + pour le OU, la
juxtaposition pour le ET et la barre pour la négation. La factorisation
fonctionne, après il faudrait rentrer dans la théorie des ensembles
pour expliciter les limites de cette assimilation.



Tu confonds theorie des ensembles et algebre, j'ai l'impression.

L'algebre booleenne a des regles relativement simples. En particulier, la
negation est un morphisme de ({faux,vrai}, &, |) -> ({vrai,faux}, |, &),
donc la convention de representer & par * et | par + est relativement
arbitraire. Elle se justifie vaguement si tu represente vrai par 1.
(avec 1 * 0 = 0, 1 * 1 = 1, 1 + 0 = 1. Mais 1+1=1... ca va toujours cahoter
un peu.

Les algebres de Boole, c'est sympa, c'est associatif, commutatif, et
distributif dans tous les sens.

a&b = b&a. a|b = b|a. (a&b)&c=a&(b&c). (a|b)|c = a|(b|c). 1&a=a. 0|a=a.
0&a = 0. 1|a = 1. (a&b)|c = (a|c)&(b|c). (a|b)&c = (a&c)|(b&c).

et j'en passe.
Le plus sympa etant sans doute qu'on peut tout definir a partir du nand...
Avatar
espie
In article , JKB <invalid> wrote:
Bref, tant que NULL vaut 0, tout se passe pour le mieux. Lorsque ce
n'est plus vrai, on commence à rigoler. Personnellement, ça fait
plus de vingt ans que je me tape du C sur des systèmes bizarres et
je préfère de loin une parenthèse en trop ou un test (ptr == NULL)
que de rechercher des bugs à la noix dans des milliers de lignes de
code.



Il y a 20 ans, tes systemes bizarres ne parlaient deja pas C. Ils se sont
mis a le parler "de moins en moins" avec l'arrivee de la norme ANSI, puis
la mise a jour en C99...
Avatar
Pierre Maurette
Marc Espie, le 06/09/2010 a écrit :
In article ,
Pierre Maurette wrote:
La trituration sur les expressions booléennes se faisaient au brouillon
- et sur la copie il me semble bien - en utilisant + pour le OU, la
juxtaposition pour le ET et la barre pour la négation. La factorisation
fonctionne, après il faudrait rentrer dans la théorie des ensembles
pour expliciter les limites de cette assimilation.



Tu confonds theorie des ensembles et algebre, j'ai l'impression.



Non, je ne confonds pas. J'ai simplement oublié presque tout de qui est
théorie, depuis ~ 37 ans. Je n'ai ni étudié ni enseigné depuis, j'ai
juste utilisé ce qui m'a été utile.

L'algebre booleenne a des regles relativement simples. En particulier, la
negation est un morphisme de ({faux,vrai}, &, |) -> ({vrai,faux}, |, &),



De Morgan ?

--
Pierre Maurette
Avatar
Pierre Maurette
Samuel DEVULDER, le 06/09/2010 a écrit :

[...]

Perso j'ai vu que dans des systèmes où "tout est nombre" certains utilisent
le + et * pour faire des "ou" et des "et" logiques. Ce ne sont pas des
charlots pour autant, mais c'est lié à long héritage culturel. Le faux y vaut
0, et le vrai toute valeur non nulle. Si les nombre sont unsigned et les
opérations saturantes.



Ce que vous nommez "opération saturante", est-ce que ça correspond à:
<URL:
http://assembleur.maurette.free.fr/livres/livre_4061/index.php?chapitre=chap09_mmx.htm>
, paragraphe 9.2.3 ?

A ce moment là on a bien identité entre + et ||, et
entre * et &&, sauf qu'il n'y a pas de short-cut dans l'évaluation: les deux
cotés de l'opérateur sont évalués.



Ce que vous appelez le /short-cut/, c'est l'évaluation économique des
expressions booleéennes ?

Pour me faire taper sur la gueule par les universitaires,
<URL:
http://assembleur.maurette.free.fr/livres/livre_4061/index.php?chapitre=chap15_annexes.htm>
; paragraphe 15.2

--
Pierre Maurette
Avatar
Samuel DEVULDER
Le 06/09/2010 21:05, Marc Espie a écrit :

"naturelle", ca suppose par convention que 0úux. Tu aurais tout aussi bien
pu prendre 0=vrai, et ta convention s'echappe...



Pour ce mapping là il faut simplement tout inverser: Prendre la
surjection "est nul" et utiliser + pour le "et" et * pour le "ou"

({T,F}, &, |) --> ({0,1,2}, +, *)

f: T --> 0
F --> 1

g: 0 --> T
1 --> F
2 --> F
(g est le test "== 0").

a | b se décompose en g(f(a) * f(b))
a & b se décompose en g(f(a) + f(b))

T | T -> 0 * 0 = 0 -> T
T | F -> 0 * 1 = 0 -> T
F | T -> 1 * 0 = 0 -> T
F | F -> 1 * 1 = 1 -> F

T & T -> 0 + 0 = 0 -> T
T & F -> 0 + 1 = 1 -> F
F & T -> 1 + 0 = 1 -> F
F & F -> 1 + 1 = 2 -> F

Dans les deux cas les surjections sont naturelles au sens où elles
préservent les propriétés et les structures des deux algèbres.

Note: Si on ne veut pas du 2 pour rester dans {0,1}, alors définir
a & b par: f(a) + f(b) - f(a)*f(b) (c'est l'addition "saturante")

sam.