58 votes

Pourquoi est-a = (a+b) - (b=a) un mauvais choix pour la permutation de deux nombres entiers?

Je suis tombé sur ce code pour la permutation de deux entiers sans utiliser une variable temporaire ou de l'utilisation des opérateurs au niveau du bit.

int main(){

    int a=2,b=3;
    printf("a=%d,b=%d",a,b);
    a=(a+b)-(b=a);
    printf("\na=%d,b=%d",a,b);
    return 0;
}

Mais je pense que ce code a un comportement indéfini dans le swap énoncé a = (a+b) - (b=a); comme il ne contient pas de séquence de points pour déterminer l'ordre d'évaluation.

Ma question est: Est-ce une solution acceptable pour permuter deux nombres entiers?

83voto

haccks Points 33022

Pas de. Ce n'est pas acceptable. Ce code appelle un comportement Indéfini. C'est à cause de l'opération sur l' b n'est pas défini. Dans l'expression

a=(a+b)-(b=a);  

il n'est pas certain qu' b obtenir d'abord modifier ou de ses obtenir une valeur utilisée dans l'expression (a+b) en raison de l'absence de la séquence de point.
Voir ce standard syas:

C11: 6.5 Expressions:

Si un effet indésirable sur un scalaire objet est séquencé par rapport à un autre effet secondaire sur le même scalaire objet ou d' une valeur de calcul à l'aide de la valeur de la même scalaire l'objet, le comportement est indéfini. S'il y a plusieurs admissible de l'ordre des les sous-expressions d'une expression, le comportement est indéfini si une telle séquencé côté l'effet se produit dans les rangements.84)1.

Lire C-faq - 3.8 et cette réponse pour une explication plus détaillée de la séquence de point et un comportement indéfini.


1. C'est moi qui souligne.

48voto

Eric Lippert Points 300275

Ma Question est: Est - ce une solution acceptable pour permuter deux nombres entiers?

Acceptable pour qui? Si vous vous demandez si c'est acceptable pour moi, ce ne serait pas obtenir au-delà de tout examen du code, j'étais dans le coup, croyez-moi.

pourquoi est-a=(a+b)-(b=a) un mauvais choix pour la permutation de deux nombres entiers?

Pour les raisons suivantes:

1) Comme vous le notez, il n'y a pas de garantie en C qu'en fait, il le fait. Il pourrait faire n'importe quoi.

2) Supposons que, pour la clarté de l'exposé qu'il fait vraiment de swap de deux entiers, comme il le fait en C#. (C# garantit que les effets secondaires se passer de gauche à droite.) Le code serait encore inacceptable, car il n'est pas du tout évident que son sens est! Le Code ne devrait pas être un tas de trucs astucieux. Écrire du code pour la personne qui arrive après vous qui est à lire et à comprendre.

3) Encore une fois, supposons que cela fonctionne. Le code est toujours inacceptable parce que c'est tout simplement faux:

Je suis tombé sur ce code pour la permutation de deux entiers sans utiliser une variable Temporaire ou de l'utilisation des opérateurs bit par bit.

C'est tout simplement faux. Cette astuce utilise une variable temporaire pour stocker le calcul de l' a+b. La variable est généré par le compilateur en votre nom et non pas un nom, mais il est là. Si l'objectif est d'éliminer temporaires, ce qui est pire, pas mieux! Et pourquoi voulez-vous éliminer temporaires en premier lieu? Ils sont bon marché!

4) Cela ne fonctionne que pour les entiers. Beaucoup de choses doivent être échangé autres que des nombres entiers.

En bref, passez votre temps à se concentrer sur l'écriture de code est évidemment correct, plutôt que d'essayer de trouver des trucs astucieux qui fait empirer les choses.

32voto

Joni Points 46728

Il y a au moins deux problèmes avec l' a=(a+b)-(b=a).

On vous le mentionnez vous-même: le manque de séquence de points signifie que le comportement est indéfini. En tant que tel, n'importe quoi pouvait arriver. Par exemple, il n'existe aucune garantie de ce qui est évalué en premier: a+b ou b=a. Le compilateur peut choisir de générer du code pour l'attribution de la première, ou de faire quelque chose de complètement différent.

Un autre problème est le fait que le débordement de l'arithmétique signée est un comportement indéfini. Si a+b débordements il n'y a pas de garantie de résultats; même une exception peut être levée.

29voto

jcoder Points 14982

Mis à part les autres réponses, à propos d'un comportement indéfini et le style, si vous écrivez du code simple qui utilise une variable temporaire, le compilateur peut probablement suivre les valeurs et non pas fait de swap dans le code généré, et il suffit d'utiliser le échangé des valeurs plus tard dans certains cas. Il ne peux pas faire cela avec votre code. Le compilateur est généralement mieux que vous à micro-optimisations.

Il est donc probable que votre code est plus lent, plus difficile à comprendre, et probablement peu fiables comportement indéfini.

28voto

Olaf Dietsche Points 35264

Si vous utilisez gcc et -Wall le compilateur déjà vous avertit

un.c:3:26: warning: le fonctionnement sur " b " peut être indéfini [-Wsequence-point]

Si l'utilisation d'une telle construction est discutable à partir d'une performance point ainsi. Quand vous regardez

void swap1(int *a, int *b)
{
    *a = (*a + *b) - (*b = *a);
}

void swap2(int *a, int *b)
{
    int t = *a;
    *a = *b;
    *b = t;
}

et examiner le code assembleur

swap1:
.LFB0:
    .cfi_startproc
    movl    (%rdi), %edx
    movl    (%rsi), %eax
    movl    %edx, (%rsi)
    movl    %eax, (%rdi)
    ret
    .cfi_endproc

swap2:
.LFB1:
    .cfi_startproc
    movl    (%rdi), %eax
    movl    (%rsi), %edx
    movl    %edx, (%rdi)
    movl    %eax, (%rsi)
    ret
    .cfi_endproc

vous pouvez voir aucun avantage pour obfusquer le code.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X