Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Comparaîson étrange

8 réponses
Avatar
Thomas Nemeth
Bonjour à tous et excusez-moi de vous sortir de vos
comparaîsons Python/Ruby/Java/whatever ;)

Je viens de lire un article très intéressant à propos de la conception
d'API (http://ozlabs.org/~rusty/index.cgi/tech/2008-03-30.html pour
ceux que ça intéresserait) mettant des mots sur ce qu'on pratique et
voit régulièrement. J'y suis tombé sur un exemple qui me laisse
perplexe :

---8<------------------------------------
The classic of this school is the Linux kernel min() and max() macros,
which use two GCC extensions: a statement expression which allows the
whole statement to be treated by the caller as a single expression, and
typeof which lets us declare a temporary variable of same type as
another:
/*
* min()/max() macros that also do
* strict type-checking.. See the
* "unnecessary" pointer comparison.
*/
#define min(x,y) ({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x < _y ? _x : _y; })

Since a common error in C is to compare signed vs unsigned types and
expect a signed result, this macro insists that both types be identical.
---8<------------------------------------

Cette macro me pose 2 problèmes dont j'ai déduit la réponse à l'un
d'eux.

Tout d'abord, comme pointé dans le commentaire, pourquoi comparer
les _adresses_ des 2 variables temporaires ? Je comprends qu'on puisse
vouloir faire qu'une comparaîson échoue si 2 types sont différents
mais pour les adresses, je ne comprends pas. Un effet de bord du
compilateur ?

Enfin, c'est la forme que prend cette macro : ({...}). C'est une
construction que je n'avais pas vu (ou à laquelle je n'avais pas
prêté attention) avant. Les accolades sont, bien entendu, nécessaire
pour créer un bloc avec des variables dedans et les parenthèses sont
là pour "créer" une expression qui retournera une valeur. J'en
conclu que, la valeur retournée devant être celle de l'expression
c?a:b, le bloc "retournait" cette expression.
Est-ce un effet de bord du compilateur ?
Comment un bloc sans "return" peut-il renvoyer une valeur ?
Est-ce utilisable avec n'importe quelle autre expression ?


Thomas.

8 réponses

Avatar
Jean-Marc Bourguet
Thomas Nemeth writes:
---8<------------------------------------
The classic of this school is the Linux kernel min() and max() macros,
which use two GCC extensions: a statement expression which allows the
whole statement to be treated by the caller as a single expression, and
typeof which lets us declare a temporary variable of same type as
another:
/*
* min()/max() macros that also do
* strict type-checking.. See the
* "unnecessary" pointer comparison.
*/
#define min(x,y) ({
typeof(x) _x = (x);
typeof(y) _y = (y);
(void) (&_x == &_y);
_x < _y ? _x : _y; })

Since a common error in C is to compare signed vs unsigned types and
expect a signed result, this macro insists that both types be identical.
---8<------------------------------------

Cette macro me pose 2 problèmes dont j'ai déduit la réponse à l'un
d'eux.

Tout d'abord, comme pointé dans le commentaire, pourquoi comparer
les _adresses_ des 2 variables temporaires ? Je comprends qu'on puisse
vouloir faire qu'une comparaîson échoue si 2 types sont différents
mais pour les adresses, je ne comprends pas. Un effet de bord du
compilateur ?


Plutot que de comparer les types, la macro compare des variables de type
pointeurs sur les types qu'on veut comparer. Si les types ne sont pas les
memes, la comparaison des pointeurs sera invalides.

typeof est une des extensions de gcc dont il est question.

Enfin, c'est la forme que prend cette macro : ({...}). C'est une
construction que je n'avais pas vu (ou à laquelle je n'avais pas
prêté attention) avant.


C'est l'autre extension de gcc.

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Thomas Nemeth
Jean-Marc Bourguet a tapoté :

Thomas Nemeth writes:
---8<------------------------------------
/*
* min()/max() macros that also do
* strict type-checking.. See the
* "unnecessary" pointer comparison.
*/
#define min(x,y) ({
typeof(x) _x = (x);
typeof(y) _y = (y);
(void) (&_x == &_y);
_x < _y ? _x : _y; })
identical. ---8<------------------------------------

Cette macro me pose 2 problèmes dont j'ai déduit la réponse à l'un
d'eux.

Tout d'abord, comme pointé dans le commentaire, pourquoi comparer
les _adresses_ des 2 variables temporaires ? Je comprends qu'on
puisse vouloir faire qu'une comparaîson échoue si 2 types sont
différents mais pour les adresses, je ne comprends pas. Un effet de
bord du compilateur ?


Plutot que de comparer les types, la macro compare des variables de type
pointeurs sur les types qu'on veut comparer. Si les types ne sont pas
les memes, la comparaison des pointeurs sera invalides.


Tu veux dire que le fait de _vouloir_ comparer des pointeurs sur
des données de types différents lève une erreur, je suppose ? Car
il me semble que des pointeurs sur des données de même type sont
toujours différents du moment qu'ils ne pointent pas sur les mêmes
données...

Me trompe-je ?

Merci pour tes infos.

Thomas.


Avatar
Alain
On Tue, 01 Apr 2008 12:04:48 +0200
Thomas Nemeth wrote:

[...]
Comment un bloc sans "return" peut-il renvoyer une valeur ?
Est-ce utilisable avec n'importe quelle autre expression ?


il ne renvoie pas, il a une valeur, c'est ce qui fait marcher:

a = (b = c);

et

if (a = b) {
[...]
}

on peut apres imaginer encore tout plein de choses bien gores =)

--
Alain

Avatar
Thomas Nemeth
Alain a tapoté :

On Tue, 01 Apr 2008 12:04:48 +0200
Thomas Nemeth wrote:

[...]
Comment un bloc sans "return" peut-il renvoyer une valeur ?
Est-ce utilisable avec n'importe quelle autre expression ?


il ne renvoie pas, il a une valeur, c'est ce qui fait marcher:
a = (b = c);


Argh. Ok.


et
if (a = b) {


Qui ne devrait jamais être utilisé. C'est une trop grande source
d'erreurs.


on peut apres imaginer encore tout plein de choses bien gores =)


Tu m'étonnes ! Pour moi c'est philosophiquement dérangeant :) Mais
je conçois maintenant comment ça marche.

Merci.


Thomas.


Avatar
Alain
On Wed, 02 Apr 2008 09:47:26 +0200
Thomas Nemeth wrote:

Alain a tapoté :

[...]

et
if (a = b) {


Qui ne devrait jamais être utilisé. C'est une trop grande source
d'erreurs.


en fait on voit tres souvent une variante:

if (a = b()) {
[...]
}

pour assigner et tester en meme temps la valeur de retour, mais bon
c'est pas mieux ;-)

--
Alain


Avatar
Pierre Maurette
On Wed, 02 Apr 2008 09:47:26 +0200
Thomas Nemeth wrote:

Alain a tapoté :

[...]

et
if (a = b) {


Qui ne devrait jamais être utilisé. C'est une trop grande source
d'erreurs.


en fait on voit tres souvent une variante:

if (a = b()) {
[...]
}

pour assigner et tester en meme temps la valeur de retour, mais bon
c'est pas mieux ;-)


Les compilateurs ayant en général la gentillesse d'avertir, il faut lui
expliquer, ainsi qu'au relecteur, son intention. Ça vaut largement un
commentaire /* affectation simultanée */:

if ((x = f()) != 0){
/* ... */
}

if ((x = y) != 0){
/* ... */
}

--
Pierre Maurette



Avatar
Erwan David
Alain écrivait :

en fait on voit tres souvent une variante:

if (a = b()) {
[...]
}

pour assigner et tester en meme temps la valeur de retour, mais bon
c'est pas mieux ;-)


if((a = f()) == val)
{
[....]
}

est assezlisible quand même.

--
Le travail n'est pas une bonne chose. Si ça l'était,
les riches l'auraient accaparé

Avatar
Marc Boyer
On 2008-04-02, Alain wrote:
On Wed, 02 Apr 2008 09:47:26 +0200
Thomas Nemeth wrote:

Alain a tapoté :

[...]

et
if (a = b) {


Qui ne devrait jamais être utilisé. C'est une trop grande source
d'erreurs.


en fait on voit tres souvent une variante:

if (a = b()) {
[...]
}

pour assigner et tester en meme temps la valeur de retour, mais bon
c'est pas mieux ;-)


C'est pas mieux, et surtout, ça n'est pas plus lisible que
a= b();
if (a){...
}

Par contre, c'est très utile pour résoudre le problème de
la boucle et demi.

int c;
while( (c= getchar()) != EOF){
...
}

qui, si on ne s'autorise pas cette syntaxe, devient un rien
plus pénible à faire
do {
c= getchar();
if (c != EOF) {
...
}
while ( c!= EOF );


Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)