Si je vous demande d'échanger le contenu de deux entiers a et b, vous allez certainement écrire un code similaire à celui-ci :
int t = a ;
a = b ;
b = t ;
Mais il est possible d'échanger les valeurs de deux registres/variables, sans utiliser de registre/variable temporaire ! Pour cela, il existe différentes méthodes assez simples.
L'échange de deux variables par addition et soustraction.
La première méthode alternative qui utilise des additions et soustractions. Il faut effectuer ces opérations dans l'ordre suivant :
Pour comprendre plus profondément pourquoi cette méthode marche, il suffit de regarder à chaque étape ce que l'on trouve dans A et dans B.
Cependant, il y a un risque de débordement au niveau de l'addition, qui fait que cette technique fonctionne mal avec certaines opérandes. Cette technique utilise de plus des opérations arithmétiques, qui sont plus lentes que les opérations logiques sur de nombreux processeurs. Bref : il n'y a pas vraiment de raisons d'utiliser cette méthode. Son seul intérêt est d'économiser un registre, mais cette économie ne compense pas le fait que le code avec addition/soustraction est plus lent.
Le "Stupid XOR trick".
Une autre méthode se base sur les particularités de l'instruction XOR et est connue sous le nom de stupid XOR trick chez nos amis anglo-saxons. Il est en effet possible d'échanger le contenu de deux registres/variables A et B en effectuant les opérations suivante, dans l'ordre :
Le code source correspondant, en C/C++, est un joli oneliner :
x ^= y ^= x ^= y ;
Cette technique était utilisée comme optimisation sur les anciens processeurs, sur lesquels les opérations bit à bit étaient plus rapides que les opérations arithmétiques. Mais les processeurs modernes peuvent échanger facilement deux registres très rapidement par simple renommage, en une seule instruction pouvant prendre un seul cycle d'horloge. En conséquence, cette méthode n'est utile que sur les anciens processeurs et les petits microcontrôleurs qui ne possèdent que très peu de registres, dans un objectif d'optimisation certain.
Fonctionnement du "trick".
Le fonctionnement de cette méthode se base sur le fait que formula_7. Faites les calculs vous-même : formula_8. Pour comprendre plus profondément pourquoi cette méthode marche, il suffit de regarder à chaque étape ce que l'on trouve dans A et dans B.
Vu que XOR est associatif, commutatif, distributif, on peut alors supprimer les parenthèses. Les identités formula_9 et formula_10 nous permettent alors de simplifier fortement les expressions. Les simplifications précédentes donnent :
Ce qui se simplifie en :
Comme on le voit, nos données ont bien étés inversées.
Piège avec les pointeurs.
Il faut faire attention au cas où des pointeurs sont utilisés pour adresser indirectement les variables à échanger, par exemple si une fonction est créée pour procéder à l'échange, comme celle-ci :
void exchangeIntegers(int* px, int* py)
 *px ^= *py ^= *px ^= *py ;
Dans le cas où l'on passe deux adresses différentes, aucun problème ne se produit :
int a = 5;
int b = 10;
exchangeIntegers(&a, &b);
Dans le cas où l'on passe deux adresses identiques, la variable pointée est remise à zéro (a XOR a) :
int a = 5;
exchangeIntegers(&a, &a);
Ce n'est pas l'effet voulu par la fonction. Il faut donc soit utiliser la technique de la variable intermédiaire qui n'a pas ce problème, soit vérifier les adresses passées en paramètres.
void exchangeIntegers(int* px, int* py)
 if (px != py) *px ^= *py ^= *px ^= *py ;

Auto-éditer un wikilivre/Annexer (Version internationalisée)/src/cpcleaned2bin.sh

cpcleaned2bin.sh (cpcleaned2bin function).
VERSION=211020
function cpcleaned2bin ()
 if [ "$1" = "--v" ]; then echo "cpcleaned2bin version : $VERSION";return 0;fi 
 mkdir -p ./tmp 
 if test -e $Srcdir/$1; then echo; else echo "$Srcdir/$1 not found by function cpcleaned2bin."; return -1;fi
 awk '{
 if ($1=="#D" || $1=="#M" || $1=="#O" || $1=="#P" || $1=="#T") {;} # do not replace with an empty line
 else {print $0} 
 }' $Srcdir/$1 > ./tmp/$1.cleaned-1
 #T cat $Srcdir/$1 | grep -v "#O\|#M\|#P\|#T" > ./tmp/$1.cleaned-1
 cat ./tmp/$1.cleaned-1 | sed "s/eval_gettext /echo \$/g" | sed "s/; echo//g" > ./tmp/$1.cleaned-2
 cat ./tmp/$1.cleaned-2 | sed "s/\"\$(gettext '/\$\"/g" | sed "s/' )//g" | sed "s/')//g" > ./tmp/$1.cleaned-3
 echo $1 | sed "s/.sh//g" | sed "s/.bash//g" > ./tmp/first.txt
 read First < ./tmp/first.txt
 #T echo "First = $First" 
 cat ./tmp/$1.cleaned-3 > $Bindir/$First
 if echo $Bindir/$First | grep ".inc"; then chmod 644 $Bindir/$First; else chmod 755 $Bindir/$First; fi
 if cat $Bindir/$First | grep -e '$\"'
 then
 xgettext -L shell -o $Resourcesdir/lang/po/$First.pot --from-code=UTF-8 $Bindir/$First 
 if test -z $Resourcesdir/lang/po/fr/$First.po
 then 
 cp $Resourcesdir/lang/po/$First.pot $Resourcesdir/lang/po/fr/$First.po
 else 
 msgmerge --update $Resourcesdir/lang/po/fr/$First.po $Resourcesdir/lang/po/$First.pot
 fi 
 msgfmt $Resourcesdir/lang/po/fr/$First.po -o $Resourcesdir/lang/po/fr/$First.mo
 echo -e "\033[1;33m### Only if you are edit and update $First.po with Poedit: ###\033[0m"
 echo "copy $Resourcesdir/lang/po/fr/$First.mo to /usr/share/locale/fr/LC_MESSAGES/$First.mo" 
 if test -z $Resourcesdir/lang/po/es/$First.po
 then 
 cp $Resourcesdir/lang/po/$First.pot $Resourcesdir/lang/po/es/$First.po
 else 
 msgmerge --update $Resourcesdir/lang/po/es/$First.po $Resourcesdir/lang/po/$First.pot
 fi
 msgfmt $Resourcesdir/lang/po/es/$First.po -o $Resourcesdir/lang/po/es/$First.mo
 echo -e "\033[1;33m### Only if you are edit and update $First.po with Poedit: ###\033[0m"
 echo "copy $Resourcesdir/lang/po/es/$First.mo to /usr/share/locale/es/LC_MESSAGES/$First.mo"
 fi
 rm -r ./tmp
 if [ "$Verbose" = "true" ];then echo "$Bindir/$First is updated:"; ls -l $Bindir/$First; fi
test_cpcleadned2bin.bash.
VERSION=211014
clear
echo "syntax of $0 [ --docu ]"
source cpcleaned2bin.sh
Srcdir='../../01-sbsrc'
mkdir -p ./bin
Bindir=./bin
Verbose="true"
cpcleaned2bin --v; echo "----"
sleep 5
cpcleaned2bin cp-src2bin.sh
if [ "$1" = "--docu" ] && test -d ../../../sbin/doc; then
clear; echo "$0 version $VERSION"; echo "----"
cat cpcleaned2bin.sh | grep "#D" | tee ../../../sbin/doc/cpcleaned2bin.docu
fi

Auto-éditer un wikilivre/Annexer (Version internationalisée)/src/killalldoc.sh

killalldoc.sh (killalldoc functions).
VERSION=211019
if [ "$1" = "--v" ];then echo "killalldoc.sh version: $VERSION"; exit 0;fi
function killOPT ()
 if test -z $1; then return -1; fi
 awk '{
 if ($1=="#O" || $1=="#P" || $1=="#T") {print " "} # replace with blank line
 }' $1
function killDMOPT ()
 if test -z $1; then return -1; fi
 awk '{
 if ($1=="#D" || $1=="#M" || $1=="#O" || $1=="#P" || $1=="#T") {print " "} # replace with blank line
 }' $1
function killALLDOC ()
 if test -z $1; then return -1; fi
 awk '{
 if ($1=="#D" || $1=="#M" || $1=="#O" || $1=="#P" || $1=="#T") {;} # do not replace with an empty line
 }' $1
test_killalldoc.bash.
VERSION=211019
clear
echo "syntax of $0 [ --docu ]"
echo "test_killalldoc.sh version: $VERSION"
source killalldoc.sh
Srcdir='../../01-sbsrc'
mkdir -p ./bin
Bindir=./bin
Libdir=../../../lib/annex.lib
Verbose="true"
./killalldoc.sh --v; echo "----"
sleep 5
killALLDOC $Srcdir/compil2bin.sh
read -s -e -n 1 -t 60 -p "Continue ?: y/n" Inkey; echo
if [ "$Inkey" != 'y' ]; then echo "exit"; exit 0; fi
killDMOPT $Srcdir/compil2bin.sh
read -s -e -n 1 -t 60 -p "Continue ?: y/n" Inkey; echo
if [ "$Inkey" != 'y' ]; then echo "exit"; exit 0; fi
killOPT $Srcdir/cp-src2bin.sh
read -s -e -n 1 -t 60 -p "Continue ?: y/n" Inkey; echo
if [ "$Inkey" != 'y' ]; then echo "exit"; exit 0; fi
if [ "$1" = "--docu" ] && test -d ../../../sbin/doc; then
clear; echo "$0 version $VERSION"; echo "----"
cat killalldoc.sh | grep "#D" | tee ../../../sbin/doc/cpcleaned2bin.docu
fi