OVH Cloud OVH Cloud

Tout est question de... mémoire

18 réponses
Avatar
bruckner.olivier
Bonjour !

je voudrais juste un petit éclaircissement concernant mon code.

#include <iostream>
using namespace std;

#define VALUE 7

int main ()
{
int stack[VALUE] = { 0, 1, 2, 3, 4, 5, 6 };
int *adr = &stack[VALUE];

for (int i=0; i<=6; i++)
{
cout << "stack[" << stack[i] << "] at memory adress: " << adr << endl;
}

return 0;
}

Je de demande si toutes les valeurs de mon tableau ont la meme adresse,
ou bien si c'est une erreur de ma part.

merci de vos réponses.

8 réponses

1 2
Avatar
drkm
Fabien LE LEZ writes:

int main ()
{
int stack[] = { 0, 1, 2, 3, 4, 5, 6 };

unsigned int taille_stack= sizeof stack / sizeof *stack;
/* Ou, si on veut faire dans l'illisible ;-) :
unsigned int const taille_stack= sizeof
stack / (sizeof * stack);
*/

//int *adr = &stack[VALUE];
int *adr= stack + taille_stack; /* Je n'aime pas l'idée d'écrire
le nom d'une variable qui n'existe pas -- ici, stack[taille_stack] */

for (int i=0; i<taille_stack; i++)
{
cout << "stack[" << stack[i] << "] at memory adress: "
<< (void*)(stack + i) << endl;
}

return 0;
}

Du coup, la variable "adr" n'est plus utilisée, mais je ne sais pas à
quoi elle sert de toutes façons, vu que c'est un pointeur qui pointe
sur rien. D'ailleurs je ne sais même pas si l'écriture "stack +
taille_stack" est valide...


Si je ne m'abuse, on peut faire pointer un pointeur sur l'adresse
juste en dehors d'un tableau, celle du premier élément en dehors du
tableau (one past the end). Si je ne m'abuse, toujours, cela est un
comportement indéfini de déréférencer un tel pointer.

`adr' pourrait servir pour une boucle itérant directement sur un
pointeur plutôt que sur un indice :

int const * end = adr ;
for ( int const * it = & stack[ 0 ] ; it < end ; ++ it ) {
std::cout << * it << " at " << ( void * ) it << std::endl ;
}

Ce qui est très proche du principe d'itérateur de la STL : on
calcule un début, une fin en dehors du conteneur, et on itère depuis
le début (y compris) jusqu'à la fin (non comprise). La fin est alors
inutilisable si ce n'est qu'en tant que marqueur de fin.

--drkm

Avatar
Gabriel Dos Reis
drkm writes:

[...]

| Ce qui est très proche du principe d'itérateur de la STL : on

Ce qui est aussi une manière destructurante. Et une fois qu'on a
réduit tout le monde à des amas linéaire d'atomes et qu'il n'y
a plus d'individualité, on peut faire passer le buldozer, pardon, les
algorithmes de la STL.

| calcule un début, une fin en dehors du conteneur, et on itère depuis
| le début (y compris) jusqu'à la fin (non comprise). La fin est alors
| inutilisable si ce n'est qu'en tant que marqueur de fin.

-- Gaby
Avatar
Pierre Maurette
"" typa:

Bonjour !

je voudrais juste un petit éclaircissement concernant mon code.

#include <iostream>
using namespace std;

#define VALUE 7

int main ()
{
int stack[VALUE] = { 0, 1, 2, 3, 4, 5, 6 };
int *adr = &stack[VALUE];

for (int i=0; i<=6; i++)
{
cout << "stack[" << stack[i] << "] at memory adress: " << adr << endl;
}

return 0;
}

Je de demande si toutes les valeurs de mon tableau ont la meme adresse,
ou bien si c'est une erreur de ma part.
Considérons la question comme portant plus sur C que C++.

Voir autres réponses : il est clair que adr = &stack[7] ne changera
pas pendant la boucle.

Attention: stack[7] n'est pas un élément du tableau. Il me semble que
la norme garanti que l'on peut prendre l'adresse du premier "élément"
qui suit un tableau. En revanche, on ne doit pas le déréférencer.

Je ne vois pas l'intérêt de la macro VALUE si vous codez 6 en dur dans
le for(,,). Si je vois une macro suivie de :
int tab[MACRO];
j'imagine que la définition de la taille du tableau est "centralisée"
dans la macro. Donc, au moins faire :
for(int i = 0; i < VALUE; i++){}

Mais pourquoi ne pas faire tout simplement :

int main()
{
int stack[] = { 0, 1, 2, 3, 4, 5, 6 };
const int VALUE = sizeof(stack) / sizeof(int);
int *adr = NULL;

for(int i = 0; i < VALUE; i++)
{
adr = &stack[i];
cout << "stack[" << stack[i] << "] at memory adress: " << adr <<
endl;
}
return 0;
}


ou

int main()
{
int stack[] = { 0, 1, 2, 3, 4, 5, 6 };
const int VALUE = sizeof(stack) / sizeof(int);

for(int i = 0; i < VALUE; i++)
{
cout << "stack[" << stack[i] << "] at memory adress: " <<
&stack[i] << endl;
}
return 0;
}

--
Pierre

Avatar
kanze
"Alexandre" wrote in message
news:<40aa55c5$0$11233$...
Ben oui, a part cout, le reste c'est des tableaux et des pointeurs.

Si je remplace cout par printf, j'ai un beau programme C...


donc un programme C++, non ?


Même avec le printf, ce serait un programme C++. Ce serait un programme
qui se situe dans l'intersection entre C et C++.

Ce n'est pas un programme idiomatique C++. Mais c'est une autre
question. Je suppose qu'en fait, il a extrait ce qu'il a posté d'un
programme plus grand. Où peut-être il y avait des motivations à ne pas
utiliser les classes de C.

En fait, même dans l'extrait, il y en a une : l'initialisation comme une
aggrégation. Vue qu'il s'agit d'une variable locale, je ne vois pas trop
ce que ça gagne, mais je me sers souvent des tableaux statiques ou
globaux de type C, simplement pour avoir l'initialisation statique, et
être à l'abri des aléas de l'ordre de l'initialisation.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
kanze
Fabien LE LEZ wrote in message
news:...
On Tue, 18 May 2004 18:37:19 +0200, "Alexandre"
wrote:

P.S. c'est du C, pas du C++ ;-)


Ah ?


Plus précisément, un programmeur C++ programmerait ça assez
différemment. Si tu prends du code C, pondu par un programmeur bien
compétent et tout et tout, et que tu remplaces "printf" par "cout" et
"malloc" par "new[]", ça ne donne pas un bon programme C++ -- la
structure du programme reste très typé "C".


Vrai. Mais je doute que ce qu'il a posté est le programme entier. Je
suppose plutôt qu'il en a extrait le morceau qui montrait ce qu'il
n'avait pas compris. Or, sans voir l'ensemble, c'est impossible à dire
si c'est du C tripoté à ressembler du C++, ou si c'est vraiment du C++
idiomatique. (Je te rappelle que même le C++ idiomatique fait
utilisation des tableaux de type C, dans certains cas. Et qu'il y a en
fait divers C++ idiomatiques, selon l'âge des compilateurs auxquels on a
affaire.)

Enfin, de toutes façons, si le "#define" fait franchement tache sur
fclc++, même sur fclc il ne me paraîtrait pas génial :
- d'une part, une constante entière conviendrait (je crois) dans les
deux langages, même si j'ai un doute sur la présence de "const" en C ;


Le #define donne une constante entière:-). L'expression 7 est bien une
constante entière. Mais je crois que tu veux dire une « integral
constant expression », au sens de la norme. Or là, il y a bien une
différence entre C et C++ : en C++, si on a :
static int const C = 7 ;
« C » est une « integral constant expression ». En C, non.

- d'autre part, le nom "VALUE" ne convient vraiment pas ;


Sans savoir le contexte d'où il a extrait ce morceau, comment savoir. Si
on va critiquer à ce niveau, appeler un tableau où on visite tous les
éléments « stack » ne convient pas non plus -- par définition, si c'est
une pile, on n'accède qu'à l'élément en haut.

- enfin, il ne sert pas à grand-chose, surtout qu'il n'est pas
utilisé au seul endroit utile, i.e. dans la boucle for.

Ça pourrait donner un machin du style :

int main ()
{
int stack[] = { 0, 1, 2, 3, 4, 5, 6 };

unsigned int taille_stack= sizeof stack / sizeof *stack;
/* Ou, si on veut faire dans l'illisible ;-) :
unsigned int const taille_stack= sizeof
stack / (sizeof * stack);
*/


C'est effectivement comment je l'écrirais en C. Ou en C++, si je devais
supporter des compilateurs un peu vieillots (ce qui est le cas). En C++
plus moderne, j'écrirais plutôt :

for ( int* p = begin( stack ) ; p != end( stack ) ; ++ p ) {
// quelque chose avec *p...
}

avec les définitions templatées habituelles de begin() et de end().

//int *adr = &stack[VALUE];
int *adr= stack + taille_stack; /* Je n'aime pas l'idée d'écrire
le nom d'une variable qui n'existe pas -- ici, stack[taille_stack] */


D'autant plus qu'en C++, ça a un comportement indéfini, même si la
probabilité que ça fasse un problème est quasiment 0, et qu'on en a fait
un cas spécial en C.

Note bien que si à la place d'un tableau C, tu avais un std::vector,
c'est bien possible que l'écriture &stack[ stack.size() ] pose un
problème, au moins en mode debug.

for (int i=0; i<taille_stack; i++)
{
cout << "stack[" << stack[i] << "] at memory adress: "
<< (void*)(stack + i) << endl;
}

return 0;
}

Du coup, la variable "adr" n'est plus utilisée, mais je ne sais pas à
quoi elle sert de toutes façons, vu que c'est un pointeur qui pointe
sur rien. D'ailleurs je ne sais même pas si l'écriture "stack +
taille_stack" est valide...


C'est l'itérateur de fin:-).

Note : c'est difficile de dire si c'est vraiment du C++ ou juste du C
habillé en C++, car c'set juste un petit bout de code pour essayer de
comprendre les rouages internes, plus que du code qui fait réellement
quelque chose. Note tout de même que parcourir un tableau avec un
for(int indice...), et donc faire afficher "stack[i]" est assez rare
-- d'ailleurs, si le tableau en question est un std::list<>, ce n'est
même pas possible.


Je suppose que le but était plutôt pédagogique qu'autre chose.

En oubliant les indices, et en n'affichant que la valeur, on peut
s'amuser à remplacer la boucle for() par un :

for_each (stack, stack+taille_stack, AfficheValeurEtAdresse);

avec AfficheValeurEtAdresse défini comme suit :

template <class T> void AfficheValeurEtAdresse (T const& t)
{
cout << t << " a l'adresse " << (void*)(&t) << endl;
}


En somme, pourquoi faire simple et lisible, quand on peut faire complex
et obfusqué. :-)

Note : le "template", loin de compliquer les choses, ou même de tendre
vers la généricité, permet juste de ne pas se préoccuper du type passé
-- et c'est souvent bien agréable, surtout quand le type est un
"std::map <MaClasse::Machin, MonAutreClasse::Truc>::const_iterator"
;-)


Ce qu'il prend d'une main, il rédonne de l'autre:-).

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34



Avatar
Fabien LE LEZ
On 19 May 2004 01:32:36 -0700, wrote:

En somme, pourquoi faire simple et lisible, quand on peut faire complex
et obfusqué. :-)


C'est bien pour ça que j'ai parlé de s'"amuser" ;-)
Un peu comme pour le jeu de Mastermind dans "More Exceptional C++".

--
;-)
FLL, Epagneul Breton

Avatar
drkm
Gabriel Dos Reis writes:

drkm writes:

[...]

| Ce qui est très proche du principe d'itérateur de la STL : on

Ce qui est aussi une manière destructurante.


?

Et une fois qu'on a
réduit tout le monde à des amas linéaire d'atomes et qu'il n'y
a plus d'individualité,


?

on peut faire passer le buldozer, pardon, les
algorithmes de la STL.


Je n'ai pas dit que c'est une bonne ou une mauvaise chose. Je
répondais à Fabien qui se demandait si l'écriture

int const * end = & array[ 0 ] + sizeof array ;

était correcte. Cela est en effet le moyen d'utiliser un algorithme
de la STL sur un tableau C. Je ne serais d'ailleurs pas étonné que le
design des itérateurs de la STL soit inspiré de cette manière de
parcourir un tableau avec un pointeur (un itérateur de la STL est
alors une généralisation de cette utilisation des pointeurs).

Personnellement, je n'aime pas trop la conception des itérateurs de
la STL. J'aurais préfèré des itérateurs encapsulant une position *et*
une fin (ou un intervalle de validité), par exemple en gardant une
référence sur le conteneur.

--drkm

Avatar
Gabriel Dos Reis
drkm writes:

| Gabriel Dos Reis writes:
|
| > drkm writes:
|
| > [...]
|
| > | Ce qui est très proche du principe d'itérateur de la STL : on
|
| > Ce qui est aussi une manière destructurante.
|
| ?

Par exemple, voir un red-black tree comme une suite linéaire est
certainement déstructurante, d'autant qu'il n'y a pas qu'une seule
manière et pourtant la STL insiste.

|
| > Et une fois qu'on a
| > réduit tout le monde à des amas linéaire d'atomes et qu'il n'y
| > a plus d'individualité,
|
| ?
|
| > on peut faire passer le buldozer, pardon, les
| > algorithmes de la STL.
|
| Je n'ai pas dit que c'est une bonne ou une mauvaise chose. Je

Je n'étais pas en train de critiquer ta manière d'expliquer la
chose. Je faisais un commentaire d'ordre général sur l'approche
« tout est une suite » de la STL.

[...]

| Personnellement, je n'aime pas trop la conception des itérateurs de
| la STL. J'aurais préfèré des itérateurs encapsulant une position *et*
| une fin (ou un intervalle de validité), par exemple en gardant une
| référence sur le conteneur.

J'aurais aimé une approche complémentaire basée sur les containers où
on peut choisir la vue qu'on veut et non l'insistance à tout voir en
termes d'itérateurs.

-- Gaby
1 2