Langage C

Introduction au langage C

Le langage C a été inventé au cours de l'année 1972 dans les laboratoires Bell (le Google de l'époque) par Dennis Ritchie et Ken Thompson pour développer le système d'exploitation Unix.

Brian Kernighan contribua avec Dennis Ritchie à populariser le langage C avec le livre "The C programming Langage", encore appelé le "C K&R" pour les initiales des deux auteurs.

Le langage C pour développer des OS (Kernel) / Drivers

C est un langage proche du matériel, adapté pour programmer les sytèmes d'exploitation (OS), les drivers, les micro-contrôleurs, les DSP, les logiciels embarqués, mais pas seulement; l'interpréteur Python, la machine virtuelle Java ou le "moteur" PHP sont également écrits en C/C++.

Linus Torvalds (le créateur de Linux) répondait ainsi à la question de savoir s’il voyait un autre langage que le C pour le développement de systèmes d’exploitation.

La raison pour laquelle je me suis lancé dans Linux et les systèmes d’exploitation en général est que j’aime vraiment le hardware. Vu sous cet angle, je n’ai pas encore vu un langage de programmation qui approche seulement le langage C. Cette affirmation ne tient pas uniquement à ce que le C soit utile pour générer du bon code pour piloter le matériel. Ce qu’il faut dire en plus c’est que l’usage du C fait sens pour des personnes qui pensent comme un ordinateur. Je crois que la raison pour laquelle il en est ainsi est que les personnes qui ont conçu le langage C l’ont fait à un moment où les compilateurs devaient être simples ; à un moment où le langage devait être adapté à la sortie ou au résultat attendu. Donc lorsque je lis du code en langage C, je sais à quoi va ressembler le code assembleur et c’est ce qui m’intéresse. (extraits de 2012)

Le langage C dans l'embarqué

Connaître le langage C reste un pré-requis pour qui souhaite développer des systèmes embarqués en programmation Bare Metal (sans OS) comme sur les Microchip PIC ou STM32. Sur les systèmes avec un OS embarqué comme le Raspberry Pi, on préfère souvent pour des questions de rapidité de développement, programmer dans un langage de plus haut niveau (C++ / Python) mais Les drivers restent développés en C.

Classement TIOBE des langages de programmation

L'index TIOBE mesure la popularité des langages de programmation en se basant sur le nombre de pages web retournées par les principaux moteurs de recherche lorsqu'on leur soumet le nom du langage de programmation.

Le langage C (en noir) continue depuis 50 ans à truster les 2 premières places du classement. En 2022, le langage C se situe derrière Python,

Classement Embedded Markets des langages de programmation

Dans l'enquête de 2019 Embedded Markets Study pour les systèmes embarqués et IoT, le langage C est largement premier avec 56%, suivi de C++ avec 23%. Python arrive loin derrière avec 6%.

Les concurrents du C dans l'embarqué

Chaque décennie apporte son nouveau langage censé remplacer le C, mais force est de constater que le langage C, qui fête ses 50 ans en 2022, n'a pas fini de nous étonner par sa longévité et pertinence dans l'embarqué.

Pertinence du C comme langage de programmation

  1. Est-il pertinent d'apprendre le C en 2022 ?

    • Oui pour de la programmation de micro-contrôleurs et pour l'embarqué
    • Oui, la notion de pointeur se retrouve dans les automates, variateurs de fréquence,
    • Oui pour la programmation de Drivers / modules du Kernel Linux
  2. Est-il pertinent d'apprendre le C en Génie Électrique ?

    • Oui, voir 1.
  3. Quels autres langages apprendre ?

    • pour l'IA, la science des données : Python 3 est très utilisé pour sa simplicité d'utilisation. Dans les faits, on utilise Python pour manipuler des fonctions performantes écrites en C/C++ sans avoir à maîtriser le C++.
    • pour l'embarqué avec FPGA : Verilog ou VHDL

Développer en C, la chaîne de compilation

La Toolchain

  1. L'éditeur de texte : permet de créer et de modifier les fichiers sources
  2. Le compilateur : qui permet de passer d’un fichier source à un fichier objet. Cette transformation se fait en réalité en plusieurs étapes grâce à différents composants (préprocesseur C, compilateur, assembleur).

Remarques :

Le fichier source : est un fichier texte contenant le code C du programme

Le fichier objet : est un fichier binaire contenant les instructions (code machine) et données provenant du processus de traduction du langage

Pour obtenir un fichier exécutable, il manque : l’édition des liens et la localisation

  1. L’éditeur de liens (Linker), qui assure le regroupement des fichiers objet provenant des différents modules et les associe avec les bibliothèques utilisées pour l’application. Nous obtenons ici un fichier exécutable.

  1. Locator: localisation des fonctions et variables

  2. Libraries : bibliothèques

  1. Le débogueur, qui peut alors permettre l’exécution pas à pas du code, l’examen des variables internes, etc. Pour cela, il a besoin du fichier exécutable et du code source.

Notons également l’emploi éventuel d’utilitaires annexes travaillant à partir du code source, le vérificateur de code, les enjoliveurs (beautifier), les outils de documentation automatique, etc.

ToolChain manuelle vs IDE

On remarque que la chaîne de compilation fait intervenir de nombreux outils et peut rapidement devenir complexe si le programme est constitué de nombreux fichiers source et de bibliothèques :

Deux écoles de programmeurs coexistent :

Les IDE permettent de configurer le compilateur de générer automatiquement un Makefile, de réaliser la mise en format du code et générer documentation, etc. Ils sont très efficace, mais pour le débutant, on occulte les différentes étapes qui permettent de générer le programme. Comprendre à minima ces étapes vont nous aider pour la cross-compilation ou la résolution de bugs générés par les automatismes de l'IDE.

Les élements de la chaîne de compilation

L'éditeur de texte :

Quelques éditeurs :

Le compilateur :

Les autres outils :

Les IDE

Les IDE possèdent l'avantage d'intégrer l'ensembles de outils nécessaires au développement (éditeur, compilateur, debugger, ...) souvent optimisé pour un langage donné. Le graphique ci-dessous représente les parts d'utilisation des différents IDE et éditeurs de textes configurables selon un sondage de 2022 du site StackOverflow.

On remarque que VSCode est en tête, les plugins disponibles permettent de transformer l'éditeur de texte en IDE et de l'adapter à de nombreux langages de programmation (pareil pour Notepad++, Vim, Sublim Text).

Ce sondage est cependant à prendre avec beaucoup de hauteur car si l'on regarde en fonction des langages de programmation:

Les fabriquants de micro-contrôleurs se basent sur ces IDE pour développer leurs outils de programmation, par exemple:

IDE pour le langage C

A l'IUT, le choix s'est porté sur l'IDE Code::Blocks avec le compilateur MinGW pour Windows :

Pour la programmation en C sur Linux, vous pouvez utiliser VirtualBox sur Windows et y installer Ubuntu. Dans Ubuntu, il restera à installer le paquet build-essential pour avoir accès aux compilateurs et VSCode si vous souhaitez travailler avec cet éditeur.

Premier programme en C et Compilation sous Linux

En hommage au Livre "The C programming Langage de K&R", il est de tradition de commencer l'apprentissage de la programmation en affichant Hello world! sur l'écran.

/* 
Date    : 2022 08 09
Auteur  : Philippe Celka
Nom     : main.c
Résumé  : Affiche Hello world! à l'écran
*/ 

#include <stdio.h> //Bibliothèque pour la fonction printf

int main(void)
{
  printf("Hello world! \n");
  return 0;
}

Compilation et Édition de liens

Dans cet exemple, le code source hello.c est enregistré dans le répertoire /home/philippe/Documents/Test_c/

Nous pouvons nous placer dans ce répertoire et ouvrir un terminal Linux. Nous utiliserons gcc pour compiler notre programme et générer le fichier exécutable.

gcc s'utilisera de la manière suivante :

gcc [fichier c à compiler] -o [exécutable]

-o correspond à l'option outfile et permet de spécifier le nom pour le fichier de sortie.

gcc main.c -o hello

On remarque que dans le répertoire, un nouveau fichier apparaît, un fichier exécutable.

Pour exécuter ce fichier, depuis le terminal, nous utiliserons la commande

./hello

Hello world! s'affiche dans la console, vous avez réalisé votre premier programme.

Remarque : gcc dans ce cas de figure, n'effectue pas seulement la compilation mais également l'édition des liens pour générer l'exécutable.

Compilation seule et fichier objet

Pour utiliser uniquement le compilateur de gcc et générer le fichier objet, on utilise l'option -c

gcc main.c -c -o main.o

Pour afficher le contenu du fichier objet, nous utiliserons objdump

objdump -S main.o

Nous obtenons le code machine et le code assembleur suivant :

main.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
   0:	f3 0f 1e fa          	endbr64 
   4:	55                   	push   %rbp
   5:	48 89 e5             	mov    %rsp,%rbp
   8:	48 8d 05 00 00 00 00 	lea    0x0(%rip),%rax        # f <main+0xf>
   f:	48 89 c7             	mov    %rax,%rdi
  12:	e8 00 00 00 00       	call   17 <main+0x17>
  17:	b8 00 00 00 00       	mov    $0x0,%eax
  1c:	5d                   	pop    %rbp
  1d:	c3                   	ret    

Il s'agit d'une section du code. Le linker réalisera ensuite le lien entre les différents fichiers objet et bibliothèques pour générer l'exécutable. (Je ne développerai pas ces opérations dans cet article)

Compilation et édition de liens avec plusieurs fichiers

Nous souhaitons maintenant compiler deux fichiers c pour obtenir un exécutable :

/* 
Nom     : main.c
*/ 

#include <stdio.h> 

int affichage1(void); //  prototype de la fonction affichage1

int main(void)
{
  printf("Hello world! \n");
  affichage1();  // appel de la fonction affichage1
  return 0;
}
/* 
Nom     : affichage.c
*/ 

#include <stdio.h> //Bibliothèque pour la fonction printf

int affichage1(void)
{
  printf("Ceci est un message affichage1  \n");
  return 0;
}

Pour générer l'exécutable, gcc va compiler main.c pour en faire un main.o (objet), affichage.c devient un affichage.o et le linker va lier les deux fichiers pour générer l'exécutable test.

gcc main.c affichage.c -o test

Fichier .h

//Nom     : main.c
#include <stdio.h> 
#include "affichage.h" //fichier header

int main(void)
{
  printf("Hello world! \n");
  affichage1();  
  affichage2();  
  affichage3();  
  return 0;
}
//Nom     : affichage.c

#include <stdio.h> //Bibliothèque pour la fonction printf

int affichage1(void)
{
  printf("ALEA  \n");
  return 0;
}

int affichage2(void)
{
  printf("JACTA \n");
  return 0;
}

int affichage3(void)
{
  printf("EST  \n");
  return 0;
}
//Nom     : affichage.h

#ifndef   _AFFICHAGE_H_
#define   _AFFICHAGE_H_

int affichage1(void);
int affichage2(void);
int affichage3(void);

#endif
gcc main.c affichage.c -o test

ALEA, JACTA, EST s'affiche correctement, notre chaîne de compilation fonctionne avec plusieurs fichiers.

Pour le moment, nous avons deux fichiers c à compiler. Un programme complet va en contenir plusieurs dizaines ou beaucoup plus. Le moteur de jeu comme Unity dépasse le Million de lignes de code, des centaines de fichiers, il est alors nécessaire de structurer le projet et d'automatiser le processus de compilation.

Présentation de Makefile

Présentation

Un Makefile est un fichier constitué de plusieurs règles de la forme :

Sélectionnez

cible: dependance
    commandes

Chaque commande est précédée d'une tabulation. Lors de l'utilisation d'un tel fichier via la commande make la première règle rencontrée, ou la règle dont le nom est spécifié, est évaluée.

L'évaluation d'une règle se fait en plusieurs étapes :

###Makefile minimal

Pour notre exemple, le makefile minimal est :


test: affichage.o main.o
  gcc -o test affichage.o main.o

affichage.o: affichage.c
  gcc -o affichage.o -c affichage.c 

main.o: main.c affichage.h
  gcc -o main.o -c main.c -W 

Regardons comment fonctionne ce Makefile :

Nous cherchons à créer le fichier exécutable test, la première dépendance est la cible d'une des règles de notre Makefile, nous évaluons donc cette règle. Comme aucune dépendance de affichage.o n'est une règle, aucune autre règle n'est à évaluer pour compléter celle-ci.

Deux cas se présentent ici : soit le fichier affichage.c est plus récent que le fichier affichage.o, la commande est alors exécutée et affichage.o est construit, soit affichage.o est plus récent que affichage.c est la commande n'est pas exécutée. L'évaluation de la règle affichage.o est terminée.

Les autres dépendances de test sont examinées de la même manière puis, si nécessaire, la commande de la règle test est exécutée et test est construit.

On crée le makefile avec un éditeur de texte, en faisant attention d'utiliser des tabulations et non des espaces pour les règles. On lance la commande make qui va exécuter les règles de compilation issues du makefile.

Makefile "enrichi"

Plusieurs cas ne sont pas gérés dans l'exemple précédent :

Ces différents cas conduisent à l'écriture de règles complémentaires :

En ajoutant ces règles complémentaires, notre Makefile devient donc :

all: test

test: affichage.o main.o
  gcc -o test affichage.o main.o

affichage.o: affichage.c
  gcc -o affichage.o -c affichage.c 

main.o: main.c affichage.h
  gcc -o main.o -c main.c -W 

clean:
    rm -rf *.o

mrproper: clean
    rm -rf test

Après avoir généré l'exécutable, on peut lancer la commande make clean pour supprimer les fichiers objets.

La réalisation d'un makefile mérite un article spécifique. On n'a même pas affleuré les possibilités de cet outils. L'objectif de cette présentation de makefile était de présenter les différentes étapes que gère un IDE quand vous appuyez sur le bouton play.

Conclusion

Compiler un programme avec les outils gcc et make peut rapidement devenir complexe avec l'augmentation du nombre de fichiers.

L'utilisation d'un IDE comme Code::Blocks va générer de manière transparente le bon makefile pour compiler votre projet. Par contre, comme dans tout automastisme, il y a des cas de figures où cela ne fonctionne pas. Maîtriser à minima les différentes étapes de compilation permet de mieux comprendre les erreurs remontées par l'IDE et trouver une solution.

Lorsque l'on appuye sur le bouton play de l'IDE, de nombreuses étapes sont effectuées en toute transparence pour l'utilisateur pour générer l'exécutable. Le programmeur peut se concentrer sur la qualité de son code.

Pour le développement des systèmes embarqués, maîtriser un peu plus en profondeur les étapes de compilation est nécessaire.

Le C pour l'embarqué

Avant-propos

L'objectif de cette série d'articles sur le langage C, n'est pas de faire un cours exhaustif, mais de réaliser un focus sur certains aspects techniques, pas toujours suffisament développés dans les cours, mais nécessaires quand on travaille sur des systèmes embarqués.

Pour traiter les exemples présentés, nous pouvons utiliser un simulateur de code C en ligne https://www.onlinegdb.com/online_c_compiler.

Manipulation sur les Bits

La technique du masquage consiste à modifier individuellement des bits sans affecter les autres bits.

Opérateurs de manipulation de bits

&      // AND bit à bit
|      // OR bit à bit
^      // OU Exclusif bit à bit
~      // NOT bit à bit (complément à 1)
<<     // décalage à gauche
>>     // décalage à droite

Exemple d'utilisation

A partir de ce programme C, essayez dans un premier temps d'anticiper les résultats à obtenir avant d'éxecuter le code.

#include <stdio.h>
#include <stdint.h>

int main()
{
  uint8_t A=0x18,B=0x57, Res;
  Res=A&B;
  printf("A=0x %hhx & B=0x %hhx donne Res=0x %hhx \n",A,B,Res);
  Res=B>>3;
  printf("Un décalage à droite de B>>3 donne Res=0x %hhx \n",Res);
  Res=~A;
  printf("Le complément à 1 de A donne  Res=0x %hhx \n",Res);
  return 0;
}

Solution

Rappel: un décalage de 1 bit vers la gauche correspond à une multiplication par 2, 2 bits à gauche correspond à une multiplication par 4 .... Pour un décalage à droite, le principe est identique mais engendre une division.

Bit SET

Pour faire une mise à 1 de bit (SET), nous utiliserons l'operateur OR qui est représenté par | et qui effectue une opération OR bit à bit.

// Nous voulons faire un SET du Bit#7 d'un registre nommé: REG
REG = REG | 0x80;

// Pour fair un SET du bit#31:
REG = REG | 0x80000000;

// Simplifions :
// (1 << 31) signifie que 1 sera décalé à gauche 31 fois pour produire 0x80000000
REG = REG | (1 << 31);

// Simplifions et compactons:
REG |= (1 << 31);

// Un SET du Bit#21 et du Bit#23:
REG |= (1 << 21) | (1 << 23);

Exemple

#include <stdio.h>

int main()
{
  int REG=5;
  printf("Au départ REG = %d\n",REG);
	REG |= (1<<7);
	printf("REG |= (1<<7) = %d",REG);
  return 0;
}

Solution

Bit CLEAR

Pour mettre un bit à 0, on parle de Reset ou de Clear.

L'opérateur tilde (~) peut nous aider pour simplifier cette opération

// Nous voulons faire un reset du Bit#7 d'un registre nommé: REG
REG = REG &   0x7F;    
REG = REG & ~(0x80); // Même chose, mais en utilisant ~ pour simplifier

// Faison un reset du bit#31:
REG = REG & ~(0x80000000);

// Simplifions :
REG = REG & ~(1 << 31);

// Simplify et compactons :
REG &= ~(1 << 31);

// Reset Bit#21 et Bit# 23:
REG &= ~( (1 << 21) | (1 << 23) );

Exemple

#include <stdio.h>

int main()
{
  int REG=133;
  printf("Au départ REG = %d\n",REG);
	REG &= ~(1<<7);
	printf("REG &= ~(1<<7) = %d",REG);
  return 0;
}

Solution

Bit TOGGLE

Réaliser un Toggle d'un bit consiste à inverser l'état actuel du bit. On utiliser la fonction XOR notée ^

// On utilise XOR pour faire un toggle du bit#5
REG ^= (1 << 5);

// Inversion du bit#3 et du bit#5
REG ^= ((1 << 3) | (1 << 5));

Exemple

#include <stdio.h>

int main()
{
  int REG=133;
  printf("Au départ REG = %d\n",REG);
	REG ^= (1 << 7);
	printf("Toggle du bit#7 de REG  = %d\n",REG);
  REG ^= (1 << 7);
	printf("on refait un Toggle du bit#7 de REG  = %d\n",REG);
  return 0;
}

Solution

Bit CHECK

Supposons que nous souhaitons vérifier que le bit 7 d'un registre est à 1 (set) :

// Si bit#7 est à 1, on lance la fonction DoAThing()
if(REG & (1 << 7))
{
 	DoAThing();
}

// On reste bloqué dans la boucle while tant que bit#7 est à 0
while( ! (REG & (1 << 7)) ) {
  ;
}

Voici un autre exemple dans lequel nous souhaitons attendre jusqu'à ce que le bit#9 soit à 0

// Tant que bit#9 n'est pas à zero (tant que bit#9 est set)
while((REG & (1 << 9)) != 0) {
  ;
}

// Tant que bit#9 est set
while(REG & (1 << 9)) {
  ;
}

Brainstorming

Combien de manière pouvons nous trouver pour tester si l'entier value est une puissance de deux en utilisant les manipulations de bits.

(value | (value + 1)) == value 
(value & (value + 1)) == value 
(value & (value - 1)) == 0 
(value | (value + 1)) == 0 
(value >> 1) == (value/2) 
((value >> 1) << 1) == value

Exercice

Que réalise cette fonction ?

boolean foo(int x, int y) {
  return ((x & (1 << y)) != 0);
}

Solution

Cette fonction permet de tester si le y ème bit du nombre x est Set ou Reset.

Opérateurs logiques

&&      // Logical AND
||      // Logical OR
!       // Logical NOT

Attention

Historiquement, C ne possède pas de type Booléan (True or False)

Quand on débute en C, on fait souvent l'erreur d'oublier que -37 est équivalent à True sur une opération logique. Vigilence !

Une possibilité d'introduire une notion de booléen plus lisible est d'inclure #include <stdbool.h> On peut ainsi accéder à des noms symboliques de type true ou false, mais c'est uniquement de l'affichage, dans une opération logique en C, -54 sera toujours vue comme True...

#include <stdio.h>
#include <stdbool.h>
int main(void) {
  bool x = true;
  if (x) {
    printf("x is true!\n");
  }
}

Logical AND

De manière simple && retourne vrai (1) si les deux côtés de l'expression sont différents de 0.

L'expression est évaluée de 'Gauche vers Droite'. Si une des partie de l'expression vaut ZERO - l'évaluation se termine.

Par exemple :

	/* These all return TRUE (1) */

	if (4 && 5) return();

	i=3;
	j=2;
	return( i && j);

Exemple

//For example:
#include <stdio.h>

int main()
{
  int k=0;
	int i=3;
	int j=2;
	if ( i-i && j++) k=1;
  printf("i=%d, j=%d, k=%d",i,j,k);
  return 0;
}

Solution

WARNING : ici on sort directement de l'évaluation sans incrémenter j. C'est spécifique à &&

Si à la place de la condition ( i-i && j++), on avait ( i && j++)

Logical OR

De manière simple || retourne vrai (1) quand un des deux côté vaut vrai. OR s'évalue également de 'gauche à droite' et va stopper quand une expression returne true.

Exemple

//For example:
#include <stdio.h>

int main()
{
  int k=0;
	int i=3;
	int j=2;
	if ( i-i || j++) k=1;
  printf("i=%d, j=%d, k=%d",i,j,k);
  return 0;
}

Solution

Exemple avec subtilité

//For example:
#include <stdio.h>

int main()
{
  int k=0;
	int i=3;
	int j=0;
	if ( i-i || j++) k=1;
  printf("i=%d, j=%d, k=%d",i,j,k);
  return 0;
}

Solution

Autre exemple avec subtilité

//For example:
#include <stdio.h>

int main()
{
  int k=0;
	int i=3;
	int j=0;
	if ( i-i || ++j) k=1;
  printf("i=%d, j=%d, k=%d",i,j,k);
  return 0;
}

Solution

Logical NOT

NOT inverse l'état logique de son opérande. Si l'opérande est 0, 1 est retourné, sinon 0 est retouné. Si l'opérande est différent de 0, il est considéré comme True, et 0 est retourné.

	!4	/* Returns 0	*/
	!-4	/* Returns 0	*/
	!1	/* Returns 0	*/
	!0	/* Returns 1	*/

il ne faut pas confondre l'opérateur ~ et l'opérateur !

~ vs !

#include <stdio.h>
#include <stdint.h>

int main()
{
  uint8_t REG=0;
	printf("~REG = %hhu (Not bit à bit)\n",~REG);
	printf("!REG = %hhu (Not logique)\n",!REG);
  return 0;
}

Solution

#include <stdio.h>
#include <stdint.h>

int main()
{
  uint8_t REG=156;
	printf("~REG = %hhu (Not bit à bit)\n",~REG);
	printf("!REG = %hhu (Not logique)\n",!REG);
  return 0;
}

Solution

Opérateurs relationnels (de test)

==      // Egal à
!=      // Différend de 
>       // supérieur
<       // inférieur
>=      // supérieur ou égal
<=      // inférieur ou égal

Une des erreurs classique en C est de confondre = et ==

Post incrémentation et Pré incrémentation

Une des difficulté quand on débute, est de comprendre la différence entre une pré-incrémentation ++i et une post-incrémentation i++

#include <stdio.h>

int main()
{
  int i=41;
  int k=0;
  if(42 == i++) k=1;
  printf("i= %d, k= %d",i,k);
  return 0;
}

Le résultat de ce code est i=42 et k=0, alors que k devrait valoir 1 d'après le code.

Solution

#include <stdio.h>

int main()
{
  int i=41;
  int k=0;
  if(42 == ++i) k=1;
  printf("i= %d, k= %d",i,k);
  return 0;
}

Le résultat de ce code est i=42 et k=1.

Solution

Expressions conditionnelles

On peut compacter les écritures de boucles if-else par des expressions conditionnelles.

// if - else  en C
  if ( x == 1 ) 
    y = 10;
  else
    y = 20;

// l'expression conditionnelle équivalente
  y = (x == 1) ? 10 : 20; 

à droite, on évalue la première expression (x ==1 ) et si elle est vraie, on évalue la seconde 10. Si fausse, la troisième expression est évaluée 20.

// if - else  en C
 if ( x == 1 ) 
   printf("take car"); 
 else
   printf("take bike");

// l'expression conditionnelle équivalente
 (x == 1) ? printf("take car") : printf("take bike");                                          
// ou bien 
 printf( (x == 1) ? "take car" : "take bike");

Il paraîtrait que les structures conditionnelles soient plus efficaces au niveau de la compilation pour les sections de code critique en temps réel. On retrouve les expressions conditionnelles chez de nombreux programmeurs adeptes de la compacité du code au détriment de la lisibilité.