Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

[debutant] tableaux dynamiques et pointeurs

14 réponses
Avatar
Rincevent06
Bonjour, je ne suis pas sur de bien comprendre le fonctionnement des
tableaux dynamiques...
Quelqu'un peut-il me dire si mes définitions sont exactes ? (pour faciliter
la lecture, mes questions sont précedées du symbole #)

1)
# L'interet d'un tableau dynamique par rapport à un tableau statique est
qu'on peut liberer la mémoire utilisée par le tableau lorsqu'on a finit de
l'utiliser ?
# Ca apporte juste l'avantage de pouvoir "gerer" la mémoire ?
# Y-a-t-il d'autres avantages ?

2) Dans mon cours, nous avons détaillé trois étapes fondamentales : la
déclaration, l'allocation mémoire et la désallocation mémoire. Voici ce que
j'ai compris des 2 premières étapes:

--> La déclaration :
int *tab = NULL;

# Ici on "réserve" un emplacement mémoire pour un tableau contenant des
entiers (int). Ses valeurs sont stockées dans *tab (par exemple (*tab)[0]
renverrais le 1er élement si on l'avait définit). Son adresse est stockée
dans tab.

# Ici ce qu'on affecte à la valeur NULL, c'est bien le tableau en lui même ?
# A cette étape, que vaut tab ?

--> L'allocation mémoire :
tab = new int[taille];

Ici je n'ai pas très bien compris.
# Apparemment, on change la valeur de tab qui est l'adresse du tableau *tab
?
# On lui applique une nouvelle adresse à l'aide de la commande new ?
# Mais alors, où était stocké le tableau avant cette procédure ???

# En clair, les 2 lignes suivantes effectuent la même opération : renvoyer
l'adresse où est stockée la valeur de l'entier 55, c'est bien ça ?

int *n=55;
cout << n;

int n=55;
cout << &n;

si on tape :
int n=55;
# Quel est le type de &n ? Une chaîne de caractère ?
# Si on voulait l'attribuer à une variable x (qui contiendrait donc
l'adresse de n). Une façon possible serait :

int *x = NULL; (1)
x=&n; (2)

(1) : *x est un entier, il a la valeur NULL (cad 0 ?), son adresse est
quelconque...

(2) : on change l'adresse x de *x, sa nouvelle adresse est celle de n.
Auquel cas, la variable x contient l'adresse de n.
*x a également changé, il contient désormais la valeur de n ?
Si on change la valeur de n, celle de *x change aussi non ?
Réciproquement, si on change la valeur de *x (est-ce possible ?),
qu'advient-t-il d ela valeur de n ?


Enfin, ma dernière question :
Dans une procédure qui utilise un tableau T comme argument, une définition
possible est :

void ma_fonction ( int T[ ] )

(dans le cas où la fonction renvoie void et où le tableau contient des
entiers).

Or le prof nous a donné une autre définition qui focntionne aussi et qui
est, selon lui, "plus générale" :

void ma_fonction ( int *T )

# Y-a-t-il un lien entre cette écriture et l'opérateur * (*x = valeur
contenue à l'adresse x) ?

Merci à ceux qui ont eu le courage de lire mon (long) message jusqu'au bout
;-)
Si vous pouviez répondre à mes questions, je vous en suis reconnaissant.
N'ayant que mon cours "papier" sur moi, je ne suis pas en mesure de tester
toutes mes suppositions sur un compilateur :-(

Merci encore

RCV

10 réponses

1 2
Avatar
M. B.
"Rincevent06" a écrit dans le message de news:
bt40kg$dlu$
1)
# L'interet d'un tableau dynamique par rapport à un tableau statique est
qu'on peut liberer la mémoire utilisée par le tableau lorsqu'on a finit de
l'utiliser ?
# Ca apporte juste l'avantage de pouvoir "gerer" la mémoire ?
# Y-a-t-il d'autres avantages ?



C'est surtout utile quand la taille du tableau n'est pas connue au
moment de la compilation, mais au moment de l'execution. On ne peut
donc pas faire autrement que l'allocation dynamique.

2) Dans mon cours, nous avons détaillé trois étapes fondamentales : la
déclaration, l'allocation mémoire et la désallocation mémoire. Voici ce
que

j'ai compris des 2 premières étapes:

--> La déclaration :
int *tab = NULL;

# Ici on "réserve" un emplacement mémoire pour un tableau contenant des
entiers (int). Ses valeurs sont stockées dans *tab (par exemple (*tab)[0]
renverrais le 1er élement si on l'avait définit). Son adresse est stockée
dans tab.


Non, ici on reserve de la memoire pour stocker une variable 'tab' qui
contiendra l'adresse du tableau, quand il sera alloue. C'est un pointeur
(une adresse) '*' prevue pour pointer vers un entier 'int'. On l'initialise
a zero car pour l'instant on n'a pas alloue le tableau et ca pointe
n'importe ou.

# Ici ce qu'on affecte à la valeur NULL, c'est bien le tableau en lui même
?

# A cette étape, que vaut tab ?

--> L'allocation mémoire :
tab = new int[taille];

Ici je n'ai pas très bien compris.
# Apparemment, on change la valeur de tab qui est l'adresse du tableau
*tab

?
# On lui applique une nouvelle adresse à l'aide de la commande new ?
# Mais alors, où était stocké le tableau avant cette procédure ???



Ici on reserve une zone memoire pour 'taille' entiers, et l'adresse du debut
de cette zone est mise dans 'tab'. Maintenant 'tab' pointe bien vers le
premier
entier du tableau.

# En clair, les 2 lignes suivantes effectuent la même opération : renvoyer
l'adresse où est stockée la valeur de l'entier 55, c'est bien ça ?

int *nU;
cout << n;



Les 2 lignes precedentes n'ont pas de sens. Tu initialises une adresse
pour pointer vers la case memoire numero 55, qui n'est surement pas libre
et qui a toutes les chances d'etre deja utilisee. Plantage assure !!

int nU;
cout << &n;



Dans les 2 lignes precedentes, tu reserves une case memoire dans laquelle
tu stockes une variable 'n' qui vaut 55. Jusque la pas de probleme. Mais
tu affiches ensuite son adresse en memoire, ce qui n'a pas beaucoup
d'interet.

si on tape :
int nU;
# Quel est le type de &n ? Une chaîne de caractère ?


'&n' ca veut dire "adresse de n", c'est donc un pointeur.

# Si on voulait l'attribuer à une variable x (qui contiendrait donc
l'adresse de n). Une façon possible serait :

int *x = NULL; (1)
x=&n; (2)



Oui, tu mets dans 'x' l'adresse de 'n'. C'est coherent.

(1) : *x est un entier, il a la valeur NULL (cad 0 ?), son adresse est
quelconque...

(2) : on change l'adresse x de *x, sa nouvelle adresse est celle de n.
Auquel cas, la variable x contient l'adresse de n.
*x a également changé, il contient désormais la valeur de n ?
Si on change la valeur de n, celle de *x change aussi non ?
Réciproquement, si on change la valeur de *x (est-ce possible ?),
qu'advient-t-il d ela valeur de n ?



Oui.

n = 25;
*x = 25;

Ca fait la meme chose.


Enfin, ma dernière question :
Dans une procédure qui utilise un tableau T comme argument, une définition
possible est :

void ma_fonction ( int T[ ] )

(dans le cas où la fonction renvoie void et où le tableau contient des
entiers).

Or le prof nous a donné une autre définition qui focntionne aussi et qui
est, selon lui, "plus générale" :

void ma_fonction ( int *T )

# Y-a-t-il un lien entre cette écriture et l'opérateur * (*x = valeur
contenue à l'adresse x) ?



C'est une difficulte classique du langage C : le caractere '*' a 2
significations
differentes.

1) Dans une declaration

int *x;

Ici on DECLARE une variable 'x' de type 'pointeur sur un entier'

2) Dans une operation

n = *x;

Ici '*' veut dire 'valeur pointee par'.

Merci à ceux qui ont eu le courage de lire mon (long) message jusqu'au
bout

;-)
Si vous pouviez répondre à mes questions, je vous en suis reconnaissant.
N'ayant que mon cours "papier" sur moi, je ne suis pas en mesure de tester
toutes mes suppositions sur un compilateur :-(



Avec plaisir.

MB

Avatar
Martinez Jerome
Les pros on l'air en congès, allez je me lance a aider :)
:

Rincevent06 wrote:

# L'interet d'un tableau dynamique par rapport à un tableau statique est
qu'on peut liberer la mémoire utilisée par le tableau lorsqu'on a finit de
l'utiliser ?


Oui et Non
Oui dans le sens ou ce que tu dis me parait vrai.
Non dans le sens ou on ne l'utilise pas pour ca, mais pluto parce qu'on
peut definir a l'execution) la taille du tableau, et non seulement a la
compilation. c'est plus pratique :)


# Ca apporte juste l'avantage de pouvoir "gerer" la mémoire ?


Dison que ca sert a rien de mettre un int[1000000] si le gars va juste
utiliser 2 int... mais que tu ne le sais pas a l'avance

# Y-a-t-il d'autres avantages ?

2) Dans mon cours, nous avons détaillé trois étapes fondamentales : la
déclaration, l'allocation mémoire et la désallocation mémoire. Voici ce que
j'ai compris des 2 premières étapes:


Cours de? surement pas de C++


--> La déclaration :
int *tab = NULL;


Non. Tu postes sur un newsgroup de C++, pas de C.
donc utilise vector<int> tab;

Toute la suite est hors de propos en C++ [HS], si ca t'interesse
vraiment de programmer en C il faut aller sur fr.comp.lang.c.
Bon, vais essayer d'y repondre un peu quand meme.
Mais note que tu ne programmes alors absolument pas en C++. en C++, la
gestion de la mémoire, c'est du boulot des classes, alors que la tu
t'embetes avec...


# Ici on "réserve" un emplacement mémoire pour un tableau contenant des
entiers (int). Ses valeurs sont stockées dans *tab (par exemple (*tab)[0]
renverrais le 1er élement si on l'avait définit). Son adresse est stockée
dans tab.


Tu reserves rien du tout poour le moment, tu declares juste une varaible
du type pointeur sur tableau.


# Ici ce qu'on affecte à la valeur NULL, c'est bien le tableau en lui même ?


C'est l'adresse du tableau (qui n'est pas encore créé)

# A cette étape, que vaut tab ?


Tab vaut NULL, puisque tu l'as initialisée a cette valeur.
Relis ton cours de C, et reecris correctement ta ligne de code :
int* tab=NULL;
et non
int *tab=NULL,
tab est de type int*.



--> L'allocation mémoire :
tab = new int[taille];


inutile avec vector<int>, c'est deja fait a la base.


Ici je n'ai pas très bien compris.
# Apparemment, on change la valeur de tab qui est l'adresse du tableau *tab
?


ben oui, tab contient l'adresse du tableau que tu viens de creer.

# On lui applique une nouvelle adresse à l'aide de la commande new ?


On lui applique ce a qui elle est destinée : accueillir l'adresse d'un
tableau de int.

# Mais alors, où était stocké le tableau avant cette procédure ???


Comme il n'est pas créé, je ne vois pas pourquoi tu voudrais stocker
quelque chose d'inexistant.


# En clair, les 2 lignes suivantes effectuent la même opération : renvoyer
l'adresse où est stockée la valeur de l'entier 55, c'est bien ça ?

int *nU;
cout << n;

int nU;
cout << &n;


euh... pas de compilo sous la main, mais ca ne devrait pas faire la meme
chose... le deuxieme devrait bien renvoyer l'adresse de 55. le 1er,
plantage : tu demandes d'afficher le contenu de l'adresse 55, celle-ci
ne doit pas etre a toi. int* nU dit au compilo que *n est situé a
l'adresse 55.


si on tape :
int nU;
# Quel est le type de &n ? Une chaîne de caractère ?


ben non : l'adresse de n, comme tu l'as dit juste avant, et comme tu l'a
ecrit la (& signifie adresse de...)

# Si on voulait l'attribuer à une variable x (qui contiendrait donc
l'adresse de n). Une façon possible serait :

int *x = NULL; (1)
x=&n; (2)


int *x=&n;


(1) : *x est un entier,


non : int*, donc pointeur sur entier.

il a la valeur NULL (cad 0 ?), son adresse est
quelconque...


rien compris



(2) : on change l'adresse x de *x, sa nouvelle adresse est celle de n.


rien compris
x contient l'adresse de n, point.

Auquel cas, la variable x contient l'adresse de n.
*x a également changé, il contient désormais la valeur de n ?


oui

Si on change la valeur de n, celle de *x change aussi non ?


oui

Réciproquement, si on change la valeur de *x (est-ce possible ?),
qu'advient-t-il d ela valeur de n ?


change aussi.

Enfin, ma dernière question :
Dans une procédure qui utilise un tableau T comme argument, une définition
possible est :

void ma_fonction ( int T[ ] )

(dans le cas où la fonction renvoie void et où le tableau contient des
entiers).

Or le prof nous a donné une autre définition qui focntionne aussi et qui
est, selon lui, "plus générale" :

void ma_fonction ( int *T )

# Y-a-t-il un lien entre cette écriture et l'opérateur * (*x = valeur
contenue à l'adresse x) ?


ne pas confondre la declaration et l'utilisation.
int *T signifie creer un pointeur sur int
*T signifie la valeur pointée par T.
Je sais, le C/C++ c'est merdique avec les pointeurs, un meme signe
signifie deux choses differentes suivant l'utilisation, faut faire avec :)


Merci à ceux qui ont eu le courage de lire mon (long) message jusqu'au bout
;-)
Si vous pouviez répondre à mes questions, je vous en suis reconnaissant.


Réponse d'un newbee pas trop fort en C, donc les autres répondront mieux :)
Mais franchement, le mec qui enseigne du C++ comme ca, faut qu'il
renomme son cours en "C" tout court... Ne t'embete pas avec ces choses
la si tu fais du C++.

N'ayant que mon cours "papier" sur moi, je ne suis pas en mesure de tester
toutes mes suppositions sur un compilateur :-(

Merci encore

RCV








Avatar
Pierre Maurette
"Rincevent06" a écrit ...
Bonjour, je ne suis pas sur de bien comprendre le fonctionnement des
tableaux dynamiques...
Quelqu'un peut-il me dire si mes définitions sont exactes ? (pour
faciliter

la lecture, mes questions sont précedées du symbole #)
1)
# L'interet d'un tableau dynamique par rapport à un tableau statique est
qu'on peut liberer la mémoire utilisée par le tableau lorsqu'on a finit de
l'utiliser ?
C'est effectivement nécessaire, mais ce n'est pas nécessairement un intérêt,

plutôt une contrainte. Disons que si ça peut présenter un intérêt, ce serait
dans le cas d'un gros tableau d'usage très fugitif, pour en récupérer la
mémoire avant de sortir de son espace de validité. Encore faut-il que l'OS
vous le rende vraiment ...

# Ca apporte juste l'avantage de pouvoir "gerer" la mémoire ?
# Y-a-t-il d'autres avantages ?
Oui : pouvoir décider de la taille du tableau, et même de son existence en 1

ou n exempleaires, à l'exécution : c'est fondamental, pensez à n'importe
quelle base de données.


2) Dans mon cours, nous avons détaillé trois étapes fondamentales : la
déclaration, l'allocation mémoire et la désallocation mémoire. Voici ce
que

j'ai compris des 2 premières étapes:

--> La déclaration :
int *tab = NULL;

# Ici on "réserve" un emplacement mémoire pour un tableau contenant des
entiers (int). Ses valeurs sont stockées dans *tab (par exemple (*tab)[0]
renverrais le 1er élement si on l'avait définit). Son adresse est stockée
dans tab.

# Ici ce qu'on affecte à la valeur NULL, c'est bien le tableau en lui même
?

# A cette étape, que vaut tab ?
Et non ;-)

[tab est un pointeur. Un pointeur est une variable. Jusqu'ici, pas de
tableau à l'horizon]
Ici, vous déclarez une variable tab de type "pointeur sur int", vous dites
au compilateur que tab est un int*. La seule mémoire que vous réservez, et
que vous n'aurez pas à désallouer, c'est la variable tab, une variable dont
la taille dépend de votre implémentation. Les deux lignes suivantes sont
tout à fait comparables :
int* tab = NULL;
int Ent = 0;

Les seules choses que l'on peut faire avec une variable pointeur, c'est :
- L'initialiser par une rvalue du même type, c'est à dire en français un
pointeur vers le même type. new, malloc() en fournisssent. Mais également
tout simplement l'opérateur adresse &. Vous pouvez donc écrire :
tab = &Ent;
- Le déréférencer : obtenir la variable de type adéquat (ici int) pointée :
*tab = 12;
Ent = 12;
tab[0] = 12;
*(tab + 0) = 12;
C'est tout pareil, tant que la valeur de tab n'a pas changé. Pour les deux
derniers, lire plus bas.
- Le passer comme argument de fonction.
- Faire un peu d'arithmétique des pointeurs, par exemple tab[4] ou *(tab +
4) désignent la même chose, le quatrième entier après Ent (intuitivement).
Ce qui est important, c'est que le calcul du compilateur dépend du type
pointé par tab. L'idée de tableau pointe son nez. Si vous voulez absolument
raisonner en octets et adresses physiques (pas bon, mais des fois ça aide) :
char* tab = &caract;
long* tub = &Ent;
(long sur 32 bits, char sur 8 bits pour l'exemple)
Alors (tab + 2) vaut NUMERIQUEMENT tab plus 2
et (tub + 2) vaut NUMERIQUEMENT tub plus 8.
Vous avez pris la sage précaution d'initialiser ce pointeur à NULL. En
effet, à la déclaration, sa valeur est n'importe quoi. NULL est une valeur
clairement invalide. Il est souvent utile de tester un pointeur par rapport
à NULL avant de l'utiliser.

/*Lecture facultative*/
Si vous aviez tapé :
int tab[12];
vous auriez réellement déclaré un tableau de 12 int, de tab[0] à tab[11].
Là, la mémoire est allouée, elle sera libérée automatiquement. A la place de
12 doit être une rvalue entière déterminable à la compilation, en bref une
constante entière.
tab tout seul est un int*. Rien ne vous empêche de faire *(tab + 400), ou
tab[400]. Simplement, ça va certainement planter. C'est le C/C++. Que du
bonheur.


--> L'allocation mémoire :
tab = new int[taille];

Ici je n'ai pas très bien compris.
# Apparemment, on change la valeur de tab qui est l'adresse du tableau
*tab

?
# On lui applique une nouvelle adresse à l'aide de la commande new ?
Oui, c'est ça. reformulons toutefois, aucun tableau n'existe en tant que tel

:
La variable tab, de type int*, est (ré)initialisée par l'opérateur new[],
qui renvoie la valeur d'un pointeur de type int* vers le premier élément
d'un tableau de taille int qu'il vient de se faire allouer (par l'OS, par
exemple). Cette valeur est précieuse, c'est la seule clé qui nous permettra
de désallouer.

# Mais alors, où était stocké le tableau avant cette procédure ???
Y'en avait pas, voir plus haut ...


# En clair, les 2 lignes suivantes effectuent la même opération : renvoyer
l'adresse où est stockée la valeur de l'entier 55, c'est bien ça ?

int *nU;
cout << n;
Eh non ...

Au choix (et j'en oublie, du coté du C par exemple) :
int* pEnt = ;
int* pEnt = new int;
int* pEnt = (int*)UnPointeurQuelconque; // à vos risques et périls
int* pEnt = (int*)&UneVariableQuelconque; // à vos risques et périls
etc..Ensuite :
*pEnt = 55;
cout << *pEnt;

/* ne pas lire ;-) */
Enfin, si, ça pourra afficher 55, mais ce n'est pas ce que vous souhaitiez.
Pour compiler, il faudrait faire :
int *n = (int*)55;
cout << n;
Mais il ne faut JAMAIS initialiser en dur un pointeur, sauf NULL et si par
exemple vous programmez des BIOS et autres friandises.

int nU;
cout << &n;
Affiche l'adresse de n.

int n = 55;
int* pn = &n;
std::cout << &n << 't' << pn << std::endl;
Affiche deux fois l'adresse de n, qui est la valeur de la variable pointeur
pn.

si on tape :
int nU;
# Quel est le type de &n ? Une chaîne de caractère ?
int*, pointeur sur int


# Si on voulait l'attribuer à une variable x (qui contiendrait donc
l'adresse de n). Une façon possible serait :

int *x = NULL; (1)
x=&n; (2)
[Ici, le passage par NULL n'est pas une obligation, même de style :

int* x = &n; // est correct]

(1) : *x est un entier, il a la valeur NULL (cad 0 ?), son adresse est
quelconque...
oui, NULL vaut 0. Mais c'est un peu ambiguë. Laissez tomber pour l'instant.

En général, *x est une entier. L'adresse de *x est x. Ici, c'est x qui vaut
NULL, donc une adresse non valide. La valeur de *x n'a pas de sens, puisque
:
int Ent = *x;
conduit à l'arrêt du programme (déréférencement de pointeur NULL).
Si vous faites :
int* x;
Alors, x a une valeur quelconque, aléatoire. Donc, *x lit "n'importe où".

x=&n; (2)
(2) : on change l'adresse x de *x, sa nouvelle adresse est celle de n.
Perdu ! Bon, je pense que maintenant c'est plus clair : on change la valeur

de x en "l'adresse de n".
L'adresse d'un pointeur existe, elle est du type pointeur sur pointeur,
int** ici, mais nous ne nous en occupons pas.
Ici, nous avons :
x, valeur du pointeur, donc int*, disons la valeur de l'adresse d'un int.
*x : désigne l'entier pointé par la valeur actuelle de x (déréférencement du
pointeur x).

Auquel cas, la variable x contient l'adresse de n.
Oui !!!! C'est à dire la même valeur que &n.


*x a également changé, il contient désormais la valeur de n ?
*x n'est qu'un symbole, celui du déréférencement de x.


Si on change la valeur de n, celle de *x change aussi non ?
On s'accroche :

Si on change la valeur de n, *x, s'il est utilisé, renverra alors la
nouvelle valeur de n, pour peu que la valeur de x n'ait pas été changée.

Réciproquement, si on change la valeur de *x (est-ce possible ?),
qu'advient-t-il d ela valeur de n ?
Si vous faites :

*x = 110;
vous écrivez 110 dans la case mémoire correspondant à n (tant que x vaut
&n). Donc, n vaut maintenant 110.

Enfin, ma dernière question :
J'y crois pas ...


Dans une procédure qui utilise un tableau T comme argument, une définition
possible est :
Sincèrement, votre démarche est bonne, mais il vous faut absolument être

clair avec les pointeurs avant d'attaquer les fonctions.
Je dis que votre démarche est bonne, parce que de toute évidence vous
souhaitez clarifier cette question des pointeurs. C'est fondamental.


void ma_fonction ( int T[ ] )

(dans le cas où la fonction renvoie void et où le tableau contient des
entiers).

Or le prof nous a donné une autre définition qui focntionne aussi et qui
est, selon lui, "plus générale" :

void ma_fonction ( int *T )

# Y-a-t-il un lien entre cette écriture et l'opérateur * (*x = valeur
contenue à l'adresse x) ?
Je pense que tout ce qui précède devrait vous aider ...


Merci à ceux qui ont eu le courage de lire mon (long) message jusqu'au
bout

;-)
Si vous pouviez répondre à mes questions, je vous en suis reconnaissant.
C'est un plaisir. Ça ne peut pas nuire de refaire le points sur les

fondamentaux.

N'ayant que mon cours "papier" sur moi, je ne suis pas en mesure de tester
toutes mes suppositions sur un compilateur :-(
C'est pourtant utile. Trop de clavier nuit, mais pas du tout ......


Merci encore
Cordialement,

Pierre

Avatar
Alexandre

Cours de? surement pas de C++



Pourquoi pas ? Ce n'est pas parce qu'il existe des classes qu'on ne doit pas
apprendre les allocations dynamiques de mémoire ! Certes aucun programmeur
C++ n'utilisera un tableau dynamique fait "à la main" mais aura parfois
besoin de new...

Non. Tu postes sur un newsgroup de C++, pas de C.
donc utilise vector<int> tab;


C'est du C++ aussi.


Toute la suite est hors de propos en C++ [HS],


Pas vraiment. La preuve, il y a "new" dans son post ;-)

Mais note que tu ne programmes alors absolument pas en C++. en C++, la
gestion de la mémoire, c'est du boulot des classes, alors que la tu
t'embetes avec...


Ben si tu dois faire la classe qui gère la mémoire, faudra bien t'emmerder
avec la gestion mémoire ;-)


Réponse d'un newbee pas trop fort en C, donc les autres répondront mieux
:)

Mais franchement, le mec qui enseigne du C++ comme ca, faut qu'il
renomme son cours en "C" tout court... Ne t'embete pas avec ces choses
la si tu fais du C++.


Pas vraiment, c'est peut-etre un exercice "de début"...
On ne va pas commencer un cours de POO en utilisant std::vector, et puis
pour apprendre l'allocation dynamique, l'exemple du tableau n'est pas mal.

Avatar
Rincevent06
OK,
merci à tous pour vos réponses et surtout pour votre rapidité !
RCV, qui a un peu mieux compris les pointeurs à present ;-)

"Rincevent06" a écrit dans le message de news:
bt40kg$dlu$
Bonjour, je ne suis pas sur de bien comprendre le fonctionnement des
tableaux dynamiques...


snip le blabla

Merci à ceux qui ont eu le courage de lire mon (long) message jusqu'au
bout

;-)
Si vous pouviez répondre à mes questions, je vous en suis reconnaissant.
N'ayant que mon cours "papier" sur moi, je ne suis pas en mesure de tester
toutes mes suppositions sur un compilateur :-(

Merci encore

RCV



Avatar
Pierre Maurette
"Martinez Jerome" a écrit ...

2) Dans mon cours, nous avons détaillé trois étapes fondamentales : la
déclaration, l'allocation mémoire et la désallocation mémoire. Voici ce
que


j'ai compris des 2 premières étapes:


Cours de? surement pas de C++

Non. Tu postes sur un newsgroup de C++, pas de C.
donc utilise vector<int> tab;

Toute la suite est hors de propos en C++ [HS], si ca t'interesse
vraiment de programmer en C il faut aller sur fr.comp.lang.c.
Bon, vais essayer d'y repondre un peu quand meme.
Mais note que tu ne programmes alors absolument pas en C++. en C++, la
gestion de la mémoire, c'est du boulot des classes, alors que la tu
t'embetes avec...
Nous sommes en 2004, il est pertinennt de faire un cours de C++ "from

scratch", sans prérequis. Hors, il est important de bâtir sur des bases
solides, et parmi celles-ci, la notion de pointeur. En fait, si le cycle
déclaration/allocation/désallocation, et la différence entre déclarer un
pointeur et déclarer un tableau de taille fixe, n'est pas claire, il est
inutile de continuer. Et personnellement je n'aurais pas d'état d'âme à
utiliser malloc() et free(), fonctions définies dans la norme C++, à ce
niveau de l'apprentissage.
Il me semble que souvent, les gars sortent encore vaseux des pointeurs, pour
attaquer bille en tête les références, au plus mauvais moment pour aborder
cette notion (je vois un meilleur endroit, avec les fonctions,
personnellement).

Relis ton cours de C, et reecris correctement ta ligne de code :
Pourquoi aurait-il un cours de C ? Le C++ est un langage autonome.


int* tab=NULL;
et non
int *tab=NULL,
tab est de type int*.
C'est mon choix également, comme dirait notre Marianne.

Qui plus est dans une approche pédagogique.
Néanmoins, int * tab = ..., et même int *tab = ... ont des défenseurs,
parfaitement dans leur droit.

# En clair, les 2 lignes suivantes effectuent la même opération :
renvoyer


l'adresse où est stockée la valeur de l'entier 55, c'est bien ça ?

int *nU;
cout << n;

int nU;
cout << &n;


euh... pas de compilo sous la main, mais ca ne devrait pas faire la meme
chose... le deuxieme devrait bien renvoyer l'adresse de 55. le 1er,
plantage : tu demandes d'afficher le contenu de l'adresse 55, celle-ci
ne doit pas etre a toi. int* nU dit au compilo que *n est situé a
l'adresse 55.
Le second renvoie l'adresse de n, &n, c'est trivial.

L'adresse de 55 ? C'est quoi ?
Le premier ne doit pas compiler (ou il warne, mais il ne devrait pas
compiler).
Si on fait :
int* n = (int*)55;
std::cout << n;
à priori, on affiche 55, et non le contenu de l'adresse 55.
Voir à ce sujet un fil récent "Comparaison de pointeurs" sur fclc. J'y ai
appris que sur certaines machines, le simple fait d'invoquer (comparaison)
un pointeur invalide pouvait provoquer un plantage. En archi Intel, pas de
problème, tant qu'on ne déréférence pas.


si on tape :
int nU;
# Quel est le type de &n ? Une chaîne de caractère ?


ben non : l'adresse de n, comme tu l'as dit juste avant, et comme tu l'a
ecrit la (& signifie adresse de...)
Oui, c'est une valeur affectable à une lvalue de type int*.

Mais il est impossible d'écrire :
&n = ptr;
même si ptr est de type int*.

# Si on voulait l'attribuer à une variable x (qui contiendrait donc
l'adresse de n). Une façon possible serait :

int *x = NULL; (1)
x=&n; (2)


int *x=&n;
int* x = &n;

;-)


(1) : *x est un entier,


non : int*, donc pointeur sur entier.
Ben si, *x désigne un entier, si x est valide.


Je sais, le C/C++ c'est merdique avec les pointeurs, un meme signe
signifie deux choses differentes suivant l'utilisation, faut faire avec :)
Et que dire de & !!!!


Réponse d'un newbee pas trop fort en C, donc les autres répondront mieux
:)

Mais franchement, le mec qui enseigne du C++ comme ca, faut qu'il
renomme son cours en "C" tout court... Ne t'embete pas avec ces choses
la si tu fais du C++.
Déjà commenté l'aspect autonome, à mon sens, du C++.

Un prof qui insiste lourdement sur les pointeurs avant de passer à la suite,
ça ne me paraît pas si mal.

Cordialement,
Pierre


Avatar
Loïc Joly
Pierre Maurette wrote:

Déjà commenté l'aspect autonome, à mon sens, du C++.
Un prof qui insiste lourdement sur les pointeurs avant de passer à la suite,
ça ne me paraît pas si mal.


Je nuancerais en disant que si ce point du cours vien au bon moment
(c'est à dire l'apprentissage des pointeurs et des allocations de
tableau à la C APRES l'apprentissage de l'utilisation de std::vector),
alors ça ne me gêne aucunement et ça fait partie de ce que quelqu'un qui
progremme en C++ DOIT savoir, même s'il ne l'utilisera pas souvent.

--
Loïc

Avatar
James Kanze
"Alexandre" writes:

|> > Cours de? surement pas de C++

|> Pourquoi pas ? Ce n'est pas parce qu'il existe des classes qu'on ne
|> doit pas apprendre les allocations dynamiques de mémoire ! Certes
|> aucun programmeur C++ n'utilisera un tableau dynamique fait "à la
|> main" mais aura parfois besoin de new...

Certes. Mais vue la question, je dirais qu'il s'agit plutôt d'un
cours pour débuttants. Tandis que l'allocation des tableaux
dynamiques en C++, c'est vraiment un truc des spécialistes, qui ne
sert pratiquement pas à la plupart des programmeurs. (Je crois que je
ne me suis jamais servi d'un new [] en C++. Et je ne suis pas exactement
un débuttant dans le langage.)

|> > Non. Tu postes sur un newsgroup de C++, pas de C. donc utilise
|> > vector<int> tab;

|> C'est du C++ aussi.

C'est en fait à peu près la seule façon qui sert en C++
d'aujourd'hui pour faire un tableau dynamique.

Il y a dix ans, évidemment, on n'avait pas std::vector. La réponse
alors était d'utiliser la classe de tableau dynamique qui avait
été adopté par le projet. Même il y a dix ans, on ne se
servait pas de new[].

--
James Kanze mailto:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France +33 1 41 89 80 93
Avatar
James Kanze
"Pierre Maurette" <mmaauurreettttttee.ppiieerrrree@@ffrreeee.ffrr> writes:

|> "Martinez Jerome" a écrit ...

|> > > 2) Dans mon cours, nous avons détaillé trois étapes
|> > > fondamentales : la déclaration, l'allocation mémoire et la
|> > > désallocation mémoire. Voici ce que j'ai compris des 2
|> > > premières étapes:

|> > Cours de? surement pas de C++

|> > Non. Tu postes sur un newsgroup de C++, pas de C. donc utilise
|> > vector<int> tab;

|> > Toute la suite est hors de propos en C++ [HS], si ca t'interesse
|> > vraiment de programmer en C il faut aller sur fr.comp.lang.c. Bon,
|> > vais essayer d'y repondre un peu quand meme. Mais note que tu ne
|> > programmes alors absolument pas en C++. en C++, la gestion de la
|> > mémoire, c'est du boulot des classes, alors que la tu t'embetes
|> > avec...

|> Nous sommes en 2004, il est pertinennt de faire un cours de C++
|> "from scratch", sans prérequis. Hors, il est important de
|> bâtir sur des bases solides, et parmi celles-ci, la notion de
|> pointeur.

Tout à fait d'accord. En C++, on ne peut pas se passer des pointeurs,
et (malheureusement ?), il faut les aborder assez tôt dans
l'enseignement du langage.

|> En fait, si le cycle déclaration/allocation/désallocation, et
|> la différence entre déclarer un pointeur et déclarer un
|> tableau de taille fixe, n'est pas claire, il est inutile de
|> continuer.

Quel rapport entre la taille des tableaux, le cycle
allocation/désallocation (parce qu'il n'y a pas normalement un cylce
déclaration/allocation/désallocation), et les pointeurs ?

La taille d'un tableau, en C++, c'est une attribute de std::vector. Au
moins que des contraints externes nous amener à utilise une autre
classe pour le tableau. On peut, surtout dans le cas des tableaux
statiques, être amené à se servir des T[], mais dans ce
cas-là, la taille est aussi une attribute du tableau, même s'il
faut tricher un peu pour l'obtenir (templates quasi-standard begin, end
et size).

En ce qui concerne la déclaration, évidemment, il n'y a aucun
cycle possible, parce qu'une déclaration ne fais qu'établir un
nom. Mais je suppose en fait que tu voulais parler des définitions.
Dans ce cas-là, la « cycle » concerne
allocation/initialisation/destruction/libération ; c'est le
compilateur qui s'en charge sauf dans les cas les plus particuliers,
mais il faut bien comprendre ce qu'il fait. Mais ça n'a quasiment
rien à faire avec new et delete, au moins pas directement, parce que
ça concerne toutes les objets, qu'ils soient alloués dynamiquement
ou non.

Quant aux pointeurs et new/delete, c'est encore une troisième
thème, qui n'a rien à voir avec les tableaux (vue qu'on n'alloue
jamais un tableau en tant que tel dynamiquement, mais toujours un seul
objet).

|> Et personnellement je n'aurais pas d'état d'âme à utiliser
|> malloc() et free(), fonctions définies dans la norme C++, à ce
|> niveau de l'apprentissage.

Pour donner aux élèves de mauvaises habitudes, et des sources
d'erreurs supplémentaires ?

|> Il me semble que souvent, les gars sortent encore vaseux des
|> pointeurs, pour attaquer bille en tête les références, au
|> plus mauvais moment pour aborder cette notion (je vois un meilleur
|> endroit, avec les fonctions, personnellement).

Je dirais effectivement que les fonctions et les références
doivent être enseignées bien avant les pointeurs. (Je suis
peut-être un peu hérétique, mais je crois que les fonctions
seraient le premier contrôle de flux d'execution que j'enseignerais.
Avant des boucles et des conditionnels, par exemple.)

|> > Relis ton cours de C, et reecris correctement ta ligne de code :

|> Pourquoi aurait-il un cours de C ? Le C++ est un langage autonome.

|> > int* tab=NULL;
|> > et non
|> > int *tab=NULL,
|> > tab est de type int*.

|> C'est mon choix également, comme dirait notre Marianne.
|> Qui plus est dans une approche pédagogique.

Qu'est-ce qu'il y a de pédagogique à montrer quelque chose dont on
ne se sert jamais ?

--
James Kanze mailto:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France +33 1 41 89 80 93
Avatar
James Kanze
Loïc Joly writes:

|> Pierre Maurette wrote:
|> > Déjà commenté l'aspect autonome, à mon sens, du C++.
|> > Un prof qui insiste lourdement sur les pointeurs avant de passer
|> > à la suite, ça ne me paraît pas si mal.

|> Je nuancerais en disant que si ce point du cours vien au bon moment
|> (c'est à dire l'apprentissage des pointeurs et des allocations de
|> tableau à la C APRES l'apprentissage de l'utilisation de
|> std::vector), alors ça ne me gêne aucunement et ça fait
|> partie de ce que quelqu'un qui progremme en C++ DOIT savoir, même
|> s'il ne l'utilisera pas souvent.

Je dirais plutôt que les pointeurs et les tableaux (qui sont
dynamique par défaut en C++) sont deux conceptes bien distinctes, qui
n'ont, ou ne doivent avoir rien à voir l'un avec l'autre. En fait, le
fait qu'on ne peut pas les désassocier en C est un des grands
défauts de C. Défaut qui a été au moins partiellement
corrigé en C++, heureusement.

--
James Kanze mailto:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France +33 1 41 89 80 93
1 2