Apprendre Cocoa : Revoir le Rôle des Variables
Joey dans Friends est le plus lent à la comprendre.
Tim Curry semble être le méchant dans tous ses nouveaux films.
Et Macaulay Culkin sera pour toujours le petit garçon de Home Alone.
Nous, les spectateurs, nous connaissons ces personnes dans leurs rôles d’acteur, et elles ne peuvent pas faire grand chose à leur destin : pour nous elles auront toujours cette représentation.
Pour les variables dans votre programme Cocoa, c’est un peu plus facile.
Aujourd’hui nous verrons quelques nouvelles intéressantes façon de faire des choses que nous ne pouvions pas auparavant, en nous appuyant sur ce que nous avons appris dans la dernière leçon.
Quelques Principes
Maintenant vous devriez savoir que toute variable est un ensemble de bits, que nous pouvons lire d’une certaine façon comme un entier, un pointeur, ou tout ce dont nous avons besoin de nous rappeler.
Bien, de quoi d’autre pouvons nous avoir besoin de nous rappeler ?
Nous avons remarqué lorsque nous avons étudié l’int qu’un entier ne pouvait pas contenir de décimal, et donc nous ne pouvons pas stocker PI dans un int.
Il existe un autre type de donnée pour stocker ces nombres qui est appelé float, parce qu’il stocke une nombre à virgule flottante, appelé ainsi parce que la virgule peut se trouver n’importe où à l’intérieur du nombre.
Regardons un code simple utilisant des floats ; j’ai modifié la méthode main précédente pour que notre nombre préféré contienne une valeur décimale, pour tous les matheux du monde.
int main (int argc, const char * argv[])
{
//Calcul de notre nombre favori
float divideBy = 3.0;
float favoriteNumber = (3.14159265 * integerForSeedValue(8)) / divideBy;
favoriteNumber--;
/* maintenant, affichons au monde entier notre nombre */
printf("My favorite number is %f", favoriteNumber);
return 0;
}
Rien de tout cela ne devait être une surprise pour nous.
Nous pouvons faire des calculs, transférer les nombres à gauche à droite, utiliser l’opérateur de décrémentation pour compter à rebours et plein d’autres opérations tout à fait normales.
Mais il y a quelques petites choses qui peuvent ne pas paraitre clair au premier coup d’oeil.
La première est que nous pouvons mélanger des entiers et des flottants dans notre code.
Nous voyons cela dans la ligne où favoriteNumber est déclaré ; nous multiplions un flottant par un entier, qui est retourné par notre fonction.
Deuxièmement, notez que nous pouvons utiliser un float pour stocker un entier, quand nous stockons 3.0 dans la variable divideBy.
Ceci est parfaitement clair ; un int est juste un flottant dont la virgule est sur le sur le côté, sans rien sur sa droite.
Enfin, remarquez que nous utilisons une nouvelle spécification de conversion pour imprimer notre float.
Naturellement, les flottants sont représentés par une caractère “f”.
Ce code devrait compiler et s’exécuter correctement.
Si vous êtes à la pointe de la technologie et faites tourner Panther et Xcode, vous devriez voir apparaitre une belle fenêtre pour afficher le résultat.
Si vous utilisez Project Builder sur Jaguar, le résultat devrait apparaitre dans l’onglet Run.
Maintenant effectuons quelques étapes en arrière et explorons de nouveaux territoires :
int main (int argc, const char * argv[])
{
//Calcul notre nombre favori
int favoriteNumber = (3.14159265 * 4.321) / 2.92;
/* maintenant, affichons au monde entier notre nombre */
countTo(favoriteNumber);
return 0;
}
Remarquez que favoriteNumber stocke de nouveau un int dans le tas.
Quand nous assignons une valeur à favoriteNumber, nous utilisons uniquement des floats.
Alors que contient en fait favoriteNumber ?
Vous devrier pouvoir deviner qu’il contient 4,64891159, ce que la calculatrice de Google nous donne.
Mais nous savons qu’un int ne peut pas stocker ce genre de détails ; il est limité au nombres entiers uniquement.
En effet, favoriteNumber contient simplement la valeur 4.
Il a pris le float que nous lui avons donné et a fait une conversion automatique en ce qu’il comprend, en tronquant le nombre en un int, supprimant les décimales.
Remarquez qu’il n’a pas fait d’arrondi ; 4,99999 sera quand même tronqué en 4.
Vous vous demandez peut-être pourquoi nous avons utilisé un int en premier lieu.
Après tout, il efface toute cette information que nous lui avons fournie et qui pourrait être utile.
Ces nombres après la décimale pourrait faire la différence.
La raison ici est que nous utilisons probablement le mauvais type ; si nous voulions faire attention, nous utiliserions un float comme nous le faisons dans le premier code de cette leçon.
Cependant, il existe de nombreux cas, où la partie décimale ne vous intéresse pas, et ceci en est un ; notre fonction countTo() prend un entier, pas un flottant ; elle ne comptera pas jusqu’à 4,6 ; elle comptera seulement jusqu’à 4.
Comme nous n’avons pas besoin de cette donnée supplémentaire, nous allons donc l’ignorer.
Un dernier mot à propos de float et nous continuerons.
Les ordinateurs caculent facilement avec les nombres entiers, comme nous.
Ils sont simples.
Les décimaux sont plus compliqués, parce que vous ne savez jamais comment faire avec la virgule.
Il existe des règles complexes pour le faire, mais même avec cela, les floats vous joueront parfois des tours, il vous arrivera de voir que la variable que vous initialisez à 4,0 est maintenant 3,9999998.
Ceci parce que float ne stocke pas la donnée que vous lui donnez, mais une approximation proche.
C’est suffisamment proche pour la plupart des utilisations, et si vous avez besoin de plus de précision, il existe d’autres types de données qui peuvent vous l’apporter.
Cela signifie que vous ne pouvez pas vérifier si des flottants sont égaux entre eux, parce que parfois, ils ne le seront pas alors qu’ils devraient.
Au lieu de cela, vous devrez utiliser un code comme le suivant :
//suffisant pour un travail sans précision ! if (fabs(varISetToFourPointOh - 4.0) > .01)
Nous utilisons la fonction fabs, qui calcule la valeur absolue d’un float.
Les flottants ne sont plus compliqués une fois que vous avez compris ces limitations, mais il existe un autre type de données que nous utilisons beaucoup sans savoir comment il est stocké.
Les Chaînes de Caractères
La chose la plus évidente que nous ne savons pas faire pour l’instant est stocker du texte.
Nous pouvons coder en dur du texte dans nos programmes, mais nous ne pouvons pas le stocker et encore moins le manipuler.
Pour cela, nous avons besoin de quelques nouveaux types.
Le premier que nous allons voir est char, et il stocke un seul caractère de texte.
Il fait cela par quelques manipulations appelées encodage.
En fait, l’idée est la même que les vieux décodeurs à carte de certains jeux : chaque lettre est représentée par un nombre, et vous convertissez les nombres pour déterminer les caractères au fur et à mesure.
La chaine ‘COCOA’ devient la séquence de nombres 67, 79, 67, 79, 65 (oui, la lettre ‘A’ est le nombre 65. Pourquoi ? Parce que c’est comme cela).
Vous pouvez trouver une liste complète des conversions ici.
Il est facile de comprendre comment tout cela va de pair avec ce que nous connaissons du C ; les variables stockent ce nouveau type toujours sur 32 bits, et maintenant, nous le lisons comme une lettre plutôt que comme un nombre.
C’est tout simple.
La difficulté vient quand nous devons représenter une séquence de nombres.
Comment pouvons nous faire ?
Et bien il existe une manière compliquée (notez que le %c est la spécification de conversion de _c_aractère).
int main (int argc, const char * argv[])
{
//affiche des lettres par la maniere compliquee
char letterC = 'c';
char letterO = 'o';
char letterA = 'a';
printf("%c%c%c%c%c", letterC, letterO, letterC, letterO, letterA);
return 0;
}
Mais c’est évidemment ridicule.
Il existe une façon plus évidente, et elle nécessite un peu de la magie que nous avons vue la semaine dernière, avec le nouveau concept de cette semaine :
int main (int argc, const char * argv[])
{
//affiche des lettres de la maniere simple.
char* string = "cocoa";
printf("%s", string);
return 0;
}
Un peu mieux.
Mais que faisons nous ici ?
Nous avons simplement créé une chaine de caractères et l’avons stockée dans une variable, appelée évidemment string.
Cette variable, comme vous pouvez le voir, est référencée par un pointeur.
Mais c’est un pointeur sur char.
Comment cela marche-t-il ?
Un Vaste Champ de Possibilités
Cela marche parce que les programmes manipulent les chaines de caractères en permanence, et C nous donne quelques raccourcis pour travailler avec elles.
Au lieu de définir chaque caractère que nous imprimons, nous pouvons définir un tableau de caractères, et les utiliser ainsi comme un groupe.
Mais qu’est-ce qu’un tableau ?
Un tableau n’est pas un nouveau type de donnée ; vous pouvez l’utiliser avec n’importe quel type de donnée.
Avec les chaines de caractères, nous construisons un tableau de caractères.
Mais vous pouvez facilement créer un tableau d’int ou float ou encore de pointeur et n’importe quoi d’autre.
Mais en gros, un tableau est une liste de variables, toutes du même type, avec lesquelles vous voulez travailler en tant que groupe.
Regardons ce bout de code pour en voir un exemple :
int main (int argc, const char * argv[])
{
int numbers[5]; //cree un tableau
numbers[0] = 67; //y ajoute des valeurs
numbers[1] = 79;
numbers[2] = 67;
numbers[3] = 79;
numbers[4] = 65;
//imprime les donnees
printf("My favorite numbers are %d, %d, %d, %d, %d",
numbers[0], //extrait des valeurs
numbers[1],
numbers[2],
numbers[3],
numbers[4]);
return 0;
}
Ici, nous voyons à peu près tout ce dont nous avons besoin de savoir pour utiliser les tableaux.
Nous pouvons en définir un en ajoutant un nombre entre crochets après le nom de la variable.
Ce nombre est la capacité du tableau ; il définit combien d’éléments il peut contenir.
En faisant cela, nous avons créé un tableau avec ce nom, et suffisamment de place pour le nombre d’élément spécifié, et chaque élément dans le tableau est de ce type.
Notez que la capacité du tableau doit être une constante ; il ne peut pas être une variable.
Ceci parce que c’est le compilateur qui alloue de la mémoire pour les tableaux ; ce sont des variables normales à l’exécution.
A partir de là, nous pouvons voir le tableau dans sa globalité en faisant référence à son nom, dans ce cas, numbers.
Mais si nous voulons utiliser un des éléments du tableau, nous devons y référer par son index.
Les index spécifient une position par leur distance après le début, alors le début est toujours 0 (étant situé à 0 places après lui-même) et le dernier index est la capacité que nous avons spécifiée lors de la création du tableau oté de un.
Notez que quand nous spécifions des index, nous sommes libres d’utiliser une variable si nous le voulons.
L’index est placé entre des crochets et placé après le nom du tableau ; donc numbers[2] fait référence à l’élément situé à l’index 2 (le troisième élément) dans le tableau numbers.
Si nous voulons assigner une valeur à un élément, nous pouvons le faire comme pour une variable normale, avec l’opérateur d’assignement, mais nous devons utiliser un index pour nous assurer que nous assignons la valeur à un élément du tableau et pas au tableau lui-même.
De même, nous pouvons extraire des données du tableau en ajoutant simplement des crochets et un index approprié.
Il est important de remarquer que en C, contrairement à de nombreux langages plus modernes comme Java et Python, les tableaux n’ont aucune idée du nombre d’éléments qu’ils contiennent ; vous conservez cette information et essayez de ne pas de mettre de données dans des éléments qui n’existent pas (c’est comme cela qu’on peut arriver à un dépassement de buffer, une faille de sécurité très répandue).
Tout Mettre Ensemble
Maintenant nous en savons plus à propos des caractères et des tableaux, qui ensemble forment les chaines de caractères.
Mais comment ?
Voyons quelques façons de faire nos tableaux de façon simple.
Le code vu précédemment est compacté en ce qui suit :
int main (int argc, const char * argv[])
{
int numbers[5] = {67, 79, 67, 79, 65};
//afficher les donnees
printf("My favorite numbers are %d, %d, %d, %d, %d",
numbers[0],
numbers[1],
numbers[2],
numbers[3],
numbers[4]);
return 0;
}
Nous pouvons utiliser les crochets pour rendre les choses un peu plus concises.
Vous pouvez compiler et exécuter ce bout de code et voir qu’il fait exactement la même chose que le code précédent.
Maintenant nous allons ruser un peu.
Modifiez la chaine de formatage de la première ligne pour que printf ressemble à ceci :
printf("My favorite programming environment is %c%c%c%c%c!",
Compilez et exécutez de nouveau et voyez les modifications agir.
L’ordinateur a pris les entiers et les affiche comme des caractères ; nous avons fait une chaine de caractère à partir d’un tableau d’entiers.
Pour rendre nos vies encore plus simple, C nous donne le format du double quote que nous avons vu, et que nous voyons encore ici :
int main (int argc, const char * argv[])
{
char numbers[] = "COCOA";
//afficher les donnees
printf("My favorite programming environment is %c%c%c%c%c!",
numbers[0],
numbers[1],
numbers[2],
numbers[3],
numbers[4]);
return 0;
}
Le format double quote est une manière simple de créer des tableaux de caractères que nous pouvons utiliser comme tout autre tableau.
Remarquez qu’il est également possible d’éditer la chaine de caractère dans le code :
int main (int argc, const char * argv[])
{
char numbers[] = "COCOA";
numbers[0] = 'S';
numbers[2] = numbers[4];
numbers[2] = 'M';
//afficher les donnees
printf("My favorite island is %c%c%c%c%c!",
numbers[0],
numbers[1],
numbers[2],
numbers[3],
numbers[4]);
return 0;
}
Le Typage (Casting)
Auparavant, quand nous avons utilisé printf pour convertir des entiers en caractères, nous avons utilisé ce que l’on appelle le typage ou casting.
Maintenant, utilisons le casting pour faire quelque chose d’intéressant dans notre code.
Réécrivons le main comme il était auparavant avant cet article, et modifions countTo() :
int main (int argc, const char * argv[])
{
//Calcul de notre nombre favori
int* favoriteNumber = malloc(sizeof(int));
*favoriteNumber = (3 * 4) / 2;
*favoriteNumber = integerForSeedValue(*favoriteNumber + 2);;
/*Affichons notre nombre favori*/
countTo(*favoriteNumber);
free(favoriteNumber);
return 0;
}
void countTo(int loopsToPerform)
{
char* msg;
int loopsToGo = loopsToPerform;
if (loopsToGo > 26)
{
msg = "All the letters of the alphabet!n";
loopsToGo = 26;
}
else if (loopsToGo < 0)
{
msg = "None of the letters of the alphabet!n";
loopsToGo = 0;
}
else
{
msg = "The first %d letters of the alphabet!n";
}
printf(msg, loopsToPerform);
while (loopsToGo) {
char thisChar = (char) (65 + (loopsToPerform-loopsToGo));
printf("%c!n", thisChar);
--loopsToGo;
}
printf("Next time won't you cast with me?n");
}
main est ici pour rappel ; ce qui est nouveau se trouve dans countTo.
Il y a trois nouvelles choses dans ce bout de code ; nous parlerons des moins intéressantes en premier.
Notez que nous avons fait quelque chose d’un peu différent dans notre premier appel à printf en lui passant une variable au lieu d’une constante.
C’est tout à fait possible ; le programme utilisera la chaine de caractères passée sans aucune difficulté.
Vous utiliserez cette méthode assez souvent dans la programmation en Cocoa, parce que passer des constantes rend votre programme plus difficile à traduire (localiser), et il est donc préférable de passer des variables.
Deuxièmement remarquez que nous utilisons de nouveau l’opérateur –, mais cette fois, avant la variable (en préfixe) au lieu d’après (en suffixe).
Les deux marchent très bien ici, où la modification de la variable est tout ce que nous faisons.
Mais vous pouvez en fait utiliser ces opérateurs n’importe où vous avez une variable, et leur comportement est alors un peu différent.
Quand l’opérateur est en préfixe, la variable est modifiée avant d’être utilisée.
Quand il est utilisé en suffixe, la variable est modifiée après avoir été utilisée.
Si vous trouvez plus d’un de ces opérateurs sur une même ligne, il est plus sage de les mettre sur deux lignes.
La dernière chose à remarquer est tout l’intérêt de ce bout de code : le casting.
La première ligne de notre boucle while contient le cast.
La ligne déclare un char appelé thisChar, mais l’équation qu’elle utilise retourne un int.
Pour convertir en un char, nous plaçons le type que nous souhaitons entre parenthèse, et nous plaçons l’ensemble devant le code que nous évaluerions normalement comme un int.
Mais comment sait-il comment traduire un nombre en lettres ?
En fait, il ne le sait pas vraiment, et c’est la toute la beauté du système : ce que nous faisons ici n’est pas de la traduction, nous lisons juste la valeur que nous avons d’une manière un peu différente.
Les uns et zéros restent les même, mais nous pouvons maintenant les utiliser comme char.
Si la valeur peut réellement être lue en tant que telle, tant mieux ! Sinon, vous voyez les problèmes : nous pourrions aussi bien caster en un char*, et l’ordinateur penserait qu’il s’agit d’un pointeur.
Mais quand nous utilisons ce pointeur, l’ordinateur chercher un char en mémoire à l’adresse 65, et se rend compte qu’il ne peut pas lire dans cet espace qui est réservé au noyau du système.
Le programme plante.
Le casting est une méthode très puissante, et peut être utilisé pour accomplir des choses intéressantes comme celle décrites ci-dessus (que vous pouvez maintenant compiler et exécuter), et d’autre encore plus complexes qui vous épargneront des efforts et vous feront gagner du temps quand vous programmez.
Cependant, c’est un outil qu’il est facile de mal employer et qui doit être utiliser avec soin.
Conclusion
Nous avons vu que nous pouvons manipuler une chaine de caractères de nombreuses différentes façons — nous pouvons l’afficher, modifier son contenu, la reconstruire et bien d’autres choses pour la transformer d’un état à un autre.
Ce que nous ne pouvons pas faire (pour l’instant) est d’augmenter sa taille.
Cette opération demande un peu plus de connaissances que nous en avons pour le moment, mais nous y remédierons dans le prochain article, ou nous parlerons de NULL et ses cousins, et où nous aborderons les Objets, les briques des programmes modernes.

Textes originaux en anglais sur O’Reilly : Learning Cocoa: Repurposing Variables par Seth Roby
Chargement
Commentaires récents