C++ boucle for sur intervalle sur un conteneur de type tableau

Bonjour,
Avant d'entrer dans le dur du C++, je viens d'apprendre l'existence de ce type de boucle for qui permet de remplir et de lire un tableau :

double t[30];

void setup() {
  Serial.begin(115200);
  int z = 0;
  // x référence les éléments de t
  for (double& x : t) x = z++; // la vatiable x prend successivement la valeur de tous les éléments du tableau et leur affecte la valeur de z
  for (double& x : t) Serial.println(x); // la vatiable x prend successivement la valeur de tous les éléments du tableau
}

void loop() {}
char t[]= "bonjour C++";
void setup() {
Serial.begin(115200);
for (char& x : t) Serial.println(x); // la vatiable x prend successivement la valeur de tous les éléments du tableau de char
}

void loop() {}

Les boucles classiques offrent bien plus de fonctionnalités. Ici, une variable (sous forme de référence pour modifier les éléments) prend successivement la valeur des éléments d'un tableau.
Je me demande quelle est la plus-value ?

vous n'avez pas besoin d'indice à gérer c'est plus propre comme syntaxe.

moi j'aime mieux ça

const byte pins[] = {2,3,4,5,6};
...
for (const byte &p: pins) pinMode(p, OUTPUT);

que

const byte pins[] = {2,3,4,5,6};
const byte nombreDePins = sizeof pins / sizeof *pins;
...
for (byte i = 0;  i < nombreDePins; i++) pinMode(pins[i], OUTPUT);

mais au final ça fait la même chose

Bonjour @J-M-L ,

Oui effectivement vu comme ça, c'est utile !
Le problème quand on est dans la théorie, c'est qu'on ne vois pas de suite l'utilité dans la pratique, enfin du moins je parle pour moi ...

Bonne journée.

Pour aller plus loin vous pourrez lire aussi des trucs sur la notion d'itérateur pour parcourir les Containers

Merci @J-M-L,

Je vais y regarder dès que j'aurai fini avec les opérateurs new[] et delete[], j'ai commencé, je fini et je regarde tout ça.

Merci beaucoup.
Heureusement que vous êtes là !

Je n'ai pas compris les détails mais je comprends les grandes lignes. De toute façon mon livre explore ces notions en profondeur (pour l'instant il les évoque). Je ne manquerai pas de revenir vers vous en cas d'incompréhensions de ma part si cela ne vous dérange pas bien sûr !

Concernant l'allocation dynamique de mémoire en c++ et les opérateurs new[] et delete[],
Je n'ai pas d'exemples, que des grandes lignes, mais voilà ce que je comprends :

//char  *t  =  new char[20] {'b', 'o', 'n', 'j', 'o', 'u', 'r', ' ', 'C', '+', '+', '\0'}; //11
char  *t  =  new char[40]; // 11

void setup() {
  Serial.begin(9600);
  for (byte i = 0 ; i < 27; i++)  *(t + i) = i + 'a' ;
  *(t + 26) = '\0';
  for (byte n = 0 ; n < 27; n++) Serial.print(*(t + n));
  //Serial.println(t);
  delete[] t;
}

void loop() {}

ça fonctionne donc je pense avoir bien compris.
Maintenant je vais entrer dans le vif du sujet : les classes et objets.

Merci
bonne journée.

en fait on ne recommande pas d'utiliser new et delete :slight_smile: mais c'est bien de savoir ce que ça fait

l'auteur de mon livre prétend le contraire. Bien que malloc, realloc et free soit toujours disponibles en c++, il déconseille fortement leur utilisation :

si on part de cet exemple :
double p* = new double(11);

  • l'opérateur new prend automatiquement en compte la taille de l'entité allouée pour reserver le nombre d'octets nécessaires. par contre, utiliser malloc implique de calculer explicitement ce nombre ;
  • en cas d'échec d'allocation new ne renvoie pas un pointeur nul mais provoque une exception qui permet une gestion efficace des erreurs (je ne connais pas encore, c'est dans un chapitre que je n'ai pas encore lu) ;
  • new permet d'initialiser l'entité allouée, à 11 dans l'exemple ce qui est impossible avec malloc ;
  • l'opérateur new est vaste puisqu'il concerne les objets.

Maintenant il ne distingue pas la programmation des µcontrôleurs et il s'est déjà trompé sur les boucles while ?

ni malloc and realloc non plus :slight_smile:

jetez un oeil à C++ Core Guidelines

Oui effectivement c'est dangereux de programmer soit-même directement la mémoire.
Il faut être attentif à ce que l'on fait surtout dans un programme avec de très nombreuses lignes de code.

L'explication n'est pas très claire, dans quel cas il peut y avoir une fuite mémoire?

Quelque pars le langage est dangereux.

Le gros avantage de new (resp. delete) est d'invoquer implicitement le constructeur (resp. le destructeur) de l'objet.
On ne peut pas créer une instance d'une classe un peu complexe avec malloc, le constructeur (qui ne nous est pas forcément connu) n'étant pas invoqué.
Donc oui à fond à new et delete.
Je pense que l'avis de JML vaut dans le monde de la chiche mémoire des micros-contrôleurs, mais faut voir le risque encouru à les ignorer.

Le problème de new c’est qu’il faut faire delete. Si vous oubliez il y a fuite mémoire.

C’est souvent mieux de laisser le compilateur créer les instances lui même et les détruire quand elles sortent de portée

Et sinon oui, sur les petits micro contrôleurs On évite tout ce qui est allocation dynamique si on peut

Mais là le problème est le même avec malloc/free

Oui, c’est pour cela qu’il faut éviter les deux

En fait je vous ai répondu par réflexe et sans faire preuve de discernement. J'ai opposé la version C à celle de C++ comme l'auteur du livre, alors que vous, vous me mettez en garde contre les dangers de l'allocation dynamique de la mémoire. Et vous avez raison !

Par exemple dans votre code

//char  *t  =  new char[20] {'b', 'o', 'n', 'j', 'o', 'u', 'r', ' ', 'C', '+', '+', '\0'}; //11
char  *t  =  new char[40]; // 11

void setup() {
  Serial.begin(9600);
  for (byte i = 0 ; i < 27; i++)  *(t + i) = i + 'a' ;
  *(t + 26) = '\0';
  for (byte n = 0 ; n < 27; n++) Serial.print(*(t + n));
  //Serial.println(t);
  delete[] t;
}

void loop() {}

Vous avez libéré l’espace mémoire des 40 octets mais le pointeur t existe toujours. Si vous l’utilisez dans la loop c’est la cata :wink:

C'est vrai.
Merci @J-M-L

OK il y a ici 2 discussions en une:

  • éviter l'alloc dynamique sur les micro-controlleurs (OK),
  • préférer new/delete à malloc/free : il n'y a pas photo, new/delete est obligatoire pour toutes classe ayant un ctor et/ou un dtor.

J'ai l'habitude, quand je fait un delete sur un pointeur persistant, d'annuler ce dernier:
delete PointeurObjet; PointeurObjet=NULL;