Rappel
Hexadécimal
- L’hexadécimal est un système de comptage qui comporte 16 valeurs. Elles sont les suivantes :
0 1 2 3 4 5 6 7 8 9 a b c d e f
Remarquez que représente
L'hexadécimal est également connu sous le nom de base-16.
Lorsque l'on compte en hexadécimal, chaque chiffre est une puissance de 16.
Le chiffre est représenté par .
Le chiffre est représenté par .
Le chiffre est représenté par .
Le chiffre est représenté par .
Le chiffre est représenté par .
Le chiffre est représenté par .
Le chiffre est représenté par .
Le nombre est représenté par , car 16 x 15 (ou ) est égal à
240. Ajoutez 15 de plus pour
obtenir 255. C'est le nombre le plus élevé que vous puissiez compter en utilisant un système
hexadécimal à deux chiffres.
L'hexadécimal est utile parce qu'il peut être représenté avec moins de chiffres. L'hexadécimal
nous permet de représenter les informations de manière plus brève
Mémoire
- On peut imaginer que la mémoire est constituée de blocs de mémoire simultanés. En appliquant
une numérotation hexadécimale à chacun de ces blocs de mémoire.
- Tous les nombres hexadécimaux
sont souvent représentés par le préfixe comme suit :
- Dans votre editeur de texte, créez une fichier et
écrivez votre code comme suit :
#include <stdio.h>
int main(void)
{
int n = 50;
printf("%i\n", n);
}
Remarquez que n est stocké en mémoire avec la valeur .
Vous pouvez visualiser la façon dont ce programme stocke cette valeur comme suit :
Le langage C dispose de deux opérateurs puissants liés à la mémoire :
& Fournit l'adresse d'un élément stocké en mémoire.
* Indique au compilateur d'aller à un endroit de la mémoire.
Nous pouvons tirer parti de ces connaissances en modifiant notre code comme suit :
#include <stdio.h>
int main(void)
{
int n = 50;
printf("%p\n", &n);
}
Remarquez le %p, qui nous permet de visualiser l'adresse d'un emplacement en
mémoire. &n peut être traduit littéralement par « l'adresse de n. ».
L'exécution de ce code renverra une adresse de mémoire commençant par 0x.
Pointeurs
- Un pointeur est une variable qui contient l'adresse d'une certaine valeur. Plus succinctement,
un pointeur est une adresse dans la mémoire de votre ordinateur.
- Considérons le code suivant :
int n = 50;
int *p = &n;
Notez que est un pointeur qui contient l'adresse
d'un entier .
Modifiez votre code comme suit :
#include <stdio.h>
int main(void)
{
int n = 50;
int *p = &n;
printf("%p\n", p);
}
Remarquez que ce code a le même effet que notre code précédent. Nous avons simplement mis à
profit notre nouvelle connaissance des opérateurs et .
Pour illustrer l'utilisation de l'opérateur , considérons ce qui
suit :
#include <stdio.h>
int main(void)
{
int n = 50;
int *p = &n;
printf("%i\n", *p);
}
Remarquez que la ligne printf imprime l'entier à l'emplacement de . crée un
pointeur dont la fonction est de stocker l'adresse mémoire d'un entier.
Vous pouvez visualiser notre code comme suit :
Remarquez que le pointeur semble plutôt grand. En effet, un pointeur est
généralement stocké sous la forme d'une valeur de . stocke l'adresse du .
Il est plus facile de se représenter un pointeur comme une adresse qui pointe vers une autre :
Chaînes de caractères
- Rappelons qu'une chaîne de caractères est simplement un tableau de caractères. Par exemple,
peut être représentée comme suit :
- Mais qu'est-ce que le s en réalité ? Où est stocké le dans la
mémoire ? Comme vous pouvez
l'imaginer, doit être stocké quelque part. Vous pouvez
visualiser la relation entre et la
chaîne de caractères de la manière suivante :
- Remarquez qu'un pointeur appelé s indique au compilateur où se trouve le premier
octet de la chaîne de caractères dans la mémoire.
- Modifiez votre code comme suit
#include <stdio.h>
int main(void)
{
char s[] = "HI!";
printf("%p\n", s);
printf("%p\n", &s[0]);
printf("%p\n", &s[1]);
printf("%p\n", &s[2]);
printf("%p\n", &s[3]);
}
Remarquez que le code ci-dessus affiche les emplacements en mémoire de chaque
caractère de la chaîne . Le symbole est utilisé pour afficher l'adresse de chaque élément de
la chaîne. Lors de l'exécution de ce code, vous remarquerez que les éléments se
trouvent les uns à côté des autres dans la mémoire.
De même, vous pouvez modifier votre code comme suit :
#include <stdio.h>
int main(void)
{
char *s = "HI!";
printf("%s\n", s);
}
Notez que ce code présentera la chaîne de caractères qui commence à l'emplacement de
.
Malloc
- En programmation, il est souvent nécessaire de copier une chaîne de caractères dans une autre.
- Dans votre editeur de texte, créez une fichier et
écrivez votre code comme suit :
#include <ctype.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
char s[] = "HI!";
string t = s;
t[0] = toupper(t[0]);
printf("s: %s\n", s);
printf("t: %s\n", t);
}
Remarquez que la chaîne copie l'adresse de
dans . Cela n'accomplit pas ce
que nous souhaitons. La chaîne n'est pas copiée, seule l'adresse l'est.
Vous pouvez visualiser le code ci-dessus comme suit :
Remarquez que et
pointent toujours vers les mêmes blocs de mémoire. Il ne s'agit pas d'une
copie authentique d'une chaîne de caractères. Il s'agit plutôt de deux pointeurs pointant sur la
même chaîne.
Avant de relever ce challenge, il est important de s'assurer que notre code ne présente pas
d'erreur de segmentation, en tentant de copier la chaîne dans
la chaîne , alors que la chaîne
n'existe pas. Pour cela, nous pouvons utiliser la fonction
comme suit :
#include <ctype.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
string s = get_string("s: ");
string t = s;
if (strlen(t) > 0)
{
t[0] = toupper(t[0]);
}
printf("s: %s\n", s);
printf("t: %s\n", t);
}
Remarquez que est utilisé pour s'assurer que
la chaîne existe. Si ce n'est pas le cas, rien ne sera copié.
Pour pouvoir faire une copie authentique de la chaîne de caractères, nous devrons introduire
deux nouveaux blocs de construction. Premièrement, vous
permet, en tant que programmeur,
d'allouer un bloc de mémoire d'une taille spécifique. Deuxièmement, vous permet de dire au
compilateur de libérer le bloc de mémoire que vous avez précédemment alloué.
Nous pouvons modifier notre code pour créer une copie authentique de notre chaîne comme suit :
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char s[] = "HI!";
char *t = malloc(strlen(s) + 1);
for (int i = 0; i <= strlen(s); i++)
{
t[i] = s[i];
}
t[0] = toupper(t[0]);
printf("s: %s\n", s);
printf("t: %s\n", t);
}
Remarquez que crée un bloc de
mémoire de la longueur de la
chaîne plus un. Cela permet d'inclure le caractère null dans notre chaîne finale copiée.
Ensuite, la boucle for parcourt la chaîne et assigne chaque
valeur au même endroit de la
chaîne .
Il apparaît qu'il y a une inefficacité dans notre code. Modifiez votre code comme suit :
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char s[] = "HI!";
char *t = malloc(strlen(s) + 1);
for (int i = 0, n = strlen(s); i <= n; i++)
{
t[i] = s[i];
}
t[0] = toupper(t[0]);
printf("s: %s\n", s);
printf("t: %s\n", t);
}
Remarquez que est maintenant défini
dans la partie gauche de la boucle
for. Il est préférable de ne pas appeler de fonctions inutiles dans la condition centrale de la
boucle for, car elle s'exécutera encore et encore. En déplaçant vers le côté
gauche, la fonction strlen ne s'exécute qu'une seule fois.
Le langage C dispose d'une fonction intégrée pour copier des chaînes de caractères appelée
strcpy. Elle peut être mise en œuvre comme suit :
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char s[] = "HI!";
char *t = malloc(strlen(s) + 1);
strcpy(t, s);
t[0] = toupper(t[0]);
printf("s: %s\n", s);
printf("t: %s\n", t);
}
Remarquez que effectue le même travail que
notre boucle for précédemment.
renvoie , une valeur
spéciale dans la mémoire, en cas de problème. Vous pouvez écrire
un code qui vérifie cette condition de la manière suivante :
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char s[] = "HI!";
char *t = malloc(strlen(s) + 1);
if (t == NULL)
{
return 1;
}
strcpy(t, s);
if (strlen(t) > 0)
{
t[0] = toupper(t[0]);
}
printf("s: %s\n", s);
printf("t: %s\n", t);
free(t);
return 0;
}
Notez que si la chaîne obtenue est de longueur ou
si échoue, est
renvoyé. De plus, notez que indique à l'ordinateur que vous
en avez fini avec ce bloc de
mémoire que vous avez créé via .
Nous pouvons également créer un tableau de 3 entiers en utilisant malloc :
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *x = malloc(3 * sizeof(int));
x[0] = 72;
x[1] = 73;
x[2] = 33;
free(x);
}
Remarquez que nous avons utilisé pour un
tableau d'entiers et non
, c'est parce que les caractères prennent et que les entiers peuvent prendre
en fonction de l'ordinateur. Il est
donc préférable d'utiliser
pour éviter ce problème.
Valeurs poubelles
- Lorsque vous demandez au compilateur un bloc de mémoire, rien ne garantit que cette mémoire sera
vide.
- Il est très possible que la mémoire que vous avez allouée ait déjà été utilisée par
l'ordinateur. Par conséquent, il se peut que vous voyiez des valeurs inutiles ou des valeurs
poubelles. Cela résulte du fait que vous avez obtenu un bloc de mémoire mais que vous ne l'avez
pas initialisé. Par exemple, considérez le code suivant pour :
#include <stdio.h>
int main(void)
{
int entiers[1024];
for (int i = 0; i < 1024; i++)
{
printf("%i\n", entiers[i]);
}
}
Notez que l'exécution de ce code allouera emplacements dans la mémoire pour votre tableau,
mais la boucle for montrera probablement que toutes les valeurs qui s'y trouvent ne sont pas .
Il est toujours préférable d'être conscient du potentiel de valeurs poubelles lorsque vous
n'initialisez pas les blocs de mémoire à une autre valeur, comme zéro ou autre.