Comme beaucoup d'autres langages, le C++ a des pointeurs. Tu sais déjà que les références et les pointeurs se ressemblent, mais considère-les comme un niveau plus proche du fonctionnement interne de ton ordinateur. Les pointeurs sont des variables qui contiennent des adresses d'objets. Ils servent à interagir directement avec les objets, ce qui permet l'allocation dynamique de mémoire et une manipulation efficace des données en C++.
Si les pointeurs sont nouveaux pour toi, ils peuvent sembler un peu mystérieux, mais une fois que tu t'y es habitué, ils sont tout à fait simples.
Ils sont une partie cruciale du C++, alors prends le temps de bien les comprendre. La version minimale de ce concept est aussi appelée dumb pointer ou pointeur brut. Avec le C++ moderne, il existe aussi des pointeurs intelligents, mais le type de base n'est pas intelligent du tout et tu dois gérer tout le travail manuellement.
Avant d'entrer dans les détails, il vaut la peine de comprendre à quoi servent les pointeurs. Les pointeurs permettent de partager l'adresse d'un objet avec d'autres parties de notre programme, ce qui est utile pour deux raisons majeures :
En C++, déclarer un pointeur consiste à préciser le type de données vers lequel il pointe, puis un astérisque (*) et le nom du pointeur.
Quand on déclare un pointeur, il n'est pas initialisé automatiquement.
Sans affectation explicite, un pointeur contient généralement une valeur indéterminée, souvent appelée « adresse poubelle ».
Même si certains compilateurs initialisent les pointeurs à nullptr, ce comportement n'est pas garanti pour tous les compilateurs ; il est donc essentiel de ne pas s'y fier.
La bonne pratique est d'initialiser explicitement les pointeurs bruts et de vérifier qu'ils ne sont pas nuls avant de les utiliser, afin d'éviter d'éventuels problèmes.
int* ptr{nullptr}; // Declares a pointer and makes sure it is not invalid
Pour affecter l'adresse d'une variable à un pointeur, on utilise l'opérateur d'adresse (&).
On déréférence un pointeur à l'aide de l'opérateur d'indirection (*).
std::string opponent{"Solomon Lane"};
// 'ethan' points to the address of the string opponent
std::string* ethan{&opponent};
// Instead of ethan's, the opponent's name address is given to the passPort
std::string passportName{*ethan};
Attention : le déréférencement doit se faire explicitement, alors qu'une référence fonctionne simplement comme un alias.
L'arithmétique des pointeurs permet d'effectuer des opérations arithmétiques sur des pointeurs, ce qui est particulièrement utile lorsqu'on travaille avec des tableaux. Ajouter un entier à un pointeur le fait pointer vers un autre élément.
// Stargate Coordinate Code
int gateCode[] = {462, 753, 218, 611, 977};
// 'ptr' points to the first element of 'gateCode'
int* ptr{&gateCode[0]};
// Accesses the third Stargate address through pointer arithmetic
int dialedAddress{*(ptr + 2)};
// Chevron encoded! Dialing Stargate address:
openStarGate(dialedAddress);
L'arithmétique des pointeurs en C++ peut facilement mener à un comportement indéfini si elle n'est pas manipulée avec précaution. Un comportement indéfini peut se manifester par des résultats de programme inattendus, des plantages, voire des failles de sécurité. Un exemple tristement célèbre des conséquences d'un comportement indéfini est la explosion de la fusée Ariane 5 en 1996, où une exception logicielle provoquée par la conversion d'un nombre à virgule flottante de 64 bits en un entier signé de 16 bits a entraîné une défaillance catastrophique.
En C++, l'opérateur -> sert à accéder aux membres d'un objet à travers un pointeur vers cet objet.
C'est une notation raccourcie qui simplifie l'accès aux membres des objets pointés par un pointeur.
Par exemple, si ptr est un pointeur vers un objet qui a une variable membre x, au lieu d'écrire (*ptr).x, tu peux utiliser directement ptr->x.
Cet opérateur améliore la lisibilité du code et réduit la verbosité lorsqu'on travaille avec des pointeurs vers des objets.
Voici un petit exemple, avec une struct Superhero qui possède une variable membre superpower.
La fonction principale crée un pointeur dianaPrince vers un objet Superhero (qui représente Wonder Woman).
L'opérateur -> sert à accéder à la variable membre superpower, ce qui met en valeur le célèbre « Lasso of Truth » de Wonder Woman.
struct Superhero {
std::string superpower;
};
Superhero wonder_woman{};
Superhero* dianaPrince = &wonder_woman;
dianaPrince->superpower = "Lasso of Truth";
// Using the -> operator to access member variable superpower:
std::cout << "Wonder Woman, possesses the mighty " << dianaPrince->superpower;
Les pointeurs et les références permettent tous deux un accès indirect aux objets, mais ils diffèrent par leurs capacités et par les questions de sûreté qu'ils posent. Les pointeurs offrent la souplesse de changer l'objet ciblé et peuvent recevoir la valeur nulle. Cependant, cette souplesse introduit des risques, comme déréférencer un pointeur nul ou créer des pointeurs suspendus. Les références, en revanche, ne peuvent pas être nulles et sont liées à des objets valides dès leur création, ce qui évite ces risques. Comme elles sont plus sûres, il faut préférer les références aux pointeurs, sauf si les fonctionnalités supplémentaires offertes par les pointeurs sont nécessaires.
Bienvenue, ingénieur ! Tu es l'un des derniers vétérans de la Fondation Speedywagon, une organisation secrète qui, depuis des décennies, lutte contre d'anciennes menaces comme les Pillar Men. Dans le cadre de cet effort, tu as passé des années à maintenir les systèmes technologiques de la Fondation, construits à partir d'un mélange de technologies de pointe et de bibliothèques vieillissantes.
Cependant, ces derniers temps, les capteurs qui détectent l'activité des Pillar Men fonctionnent mal. Les systèmes de la Fondation sont anciens, et le code interagit avec une bibliothèque C++ héritée qui ne peut pas être mise à jour. Ta tâche est d'implémenter quatre fonctions essentielles qui surveillent l'activité des capteurs des Pillar Men à l'aide d'une vieille bibliothèque à base de pointeurs.
En tant qu'ingénieur C++ moderne, tu préférerais utiliser des pointeurs intelligents, mais hélas, le code hérité exige le respect des anciennes méthodes. Le sort de l'humanité repose peut-être sur ces pointeurs, alors avance avec prudence, et que l'énergie Hamon te guide.
Comme les relevés des capteurs peuvent être énormes, on fournit une maquette de struct qui est utilisée dans la bibliothèque réelle. Le code a déjà été implémenté dans le fichier d'en-tête pour toi.
struct pillar_men_sensor {
int activity{};
std::string location{};
std::vector<int> data{};
};
connection_check)Ta première tâche consiste à t'assurer que le capteur des Pillar Men est bien connecté.
On ne peut pas se permettre de fausses alertes déclenchées par des capteurs déconnectés.
Tu vas écrire une fonction connection_check, qui vérifie si le pointeur du capteur est valide en testant nullptr.
pillar_men_sensor.true si le pointeur du capteur n'est pas nul, et false sinon.pillar_men_sensor* sensor{nullptr};
bool isConnected = connection_check(sensor);
// isConnected => false
activity_counter)Les Pillar Men rôdent dans l'ombre, et on doit savoir si les capteurs ont détecté la moindre activité.
Tu vas écrire la fonction activity_counter, qui prend un tableau de capteurs et une capacité indiquant le nombre de capteurs dans le tableau.
pillar_men_sensor sensor_array[3] = {{0}, {101}, {22}};
int totalActivity = activity_counter(sensor_array, 3);
// totalActivity => 123
alarm_control)Un capteur ne doit déclencher une alarme que s'il y a un vrai danger.
La fonction alarm_control fait en sorte qu'un capteur ne déclenche une alarme que si son niveau d'activité est supérieur à 0.
Cette fonction doit aussi vérifier que les capteurs ne sont pas nuls pour éviter que le système plante.
pillar_men_sensor.nullptr. Si le capteur est nullptr, renvoie false.true ; sinon, renvoie false.pillar_men_sensor db{9008, "songokunoie", {7, 7, 7}};
bool alarm = alarm_control(&db);
// alarm => true
uv_alarm
Dans cette tâche, tu vas implémenter la fonction uv_alarm pour déterminer si une alarme doit être déclenchée en fonction des niveaux d'exposition aux UV et de l'activité des capteurs.
La fonction uv_alarm doit utiliser la fonction uv_light_heuristic fournie, qui opère sur un vecteur de données et renvoie une valeur en fonction de certains seuils.
Il s'agit d'une version maquette du code complexe qui s'exécutera en production, ne modifie pas l'interface.
Définis la fonction uv_alarm dans l'espace de noms speedywagon. Elle doit :
pillar_men_sensor.false si le pointeur du capteur est nul.uv_light_heuristic en lui passant l'adresse du tableau data du capteur.true si la valeur renvoyée par uv_light_heuristic est supérieure au niveau sensor->activity, sinon renvoyer false.Inscris-toi sur Exercism pour apprendre et maîtriser C++ avec 19 concepts100 exercices, et un vrai mentorat humain, le tout gratuitement.