OVH Cloud OVH Cloud

array of char

11 réponses
Avatar
3dsman
salut!

j'ai une fonction dans laquelle je definit un tableau de char:

char texttemp[256];

dois je le detruire apres utilisation et si oui comment?
j'ai essayé un

free(textemp);

ca fait crasher a l'execution.

--
Ceci est une signature automatique de MesNews.
Site : http://mesnews.no-ip.com

10 réponses

1 2
Avatar
Loïc Joly
3dsman wrote:

salut!

j'ai une fonction dans laquelle je definit un tableau de char:

char texttemp[256];

dois je le detruire apres utilisation et si oui comment?


Oui, mais tu n'a rien à faire, il est détruit automatiquement dès que la
variable cesse d'être visible.

C'est quand un utilise de l'allocation dynamique que l'on doit gérer la
désallocation manuellement (et encore, souvent, on se débrouille pour
que cette allocation dynamique soit encapsulée dans une allocation
statique, afin de bénéficier de la désallocation automatique, c'est une
partie de ce qu'on appelle RAII en C++).

--
Loïc

Avatar
drkm
Loïc Joly writes:

C'est quand un utilise de l'allocation dynamique que l'on doit gérer la
désallocation manuellement


On peut rappeler le dogme (dont il faut s'écarter lorsque justifié,
mais pas avant) :

pour tout new : un et un seul delete

pour tout new[] : un et un seul delete[]

pour tout delete : un et un seul new ou ( initialisation à 0 et un
ou zéro new )

pour tout delete[] : un et un seul new[] ou ( initialisation à 0
et un ou zéro new[] )

--drkm

Avatar
Pierre Maurette
3dsman wrote:

salut!

j'ai une fonction dans laquelle je definit un tableau de char:

char texttemp[256];

dois je le detruire apres utilisation et si oui comment?



Oui, mais tu n'a rien à faire, il est détruit automatiquement dès que la
variable cesse d'être visible.
Je ne voudrais pas confuser 3dsman, mais il me semble que le terme

"visible" n'est pas idéal. Le tableau est détruit au return de la
fonction, même si entre temps il n'est pas visible..
--
Pierre


Avatar
drkm
Pierre Maurette writes:

Le tableau est détruit au return de la
fonction, même si entre temps il n'est pas visible.


Ben non :

void f() {
{
int i ;
}
}

Mais il est vrai que cela dépend de ce que l'on entend par visible.
Je pense qu'il est de coutume de lui attribuer le sens de « scope ».
Il est vrai que la première variable ci-dessous ne sera détruite qu'à
la fin de son scope, pas lorsqu'elle cesse (à chaque fois qu'elle
cesse) d'être visible :

void g() {
{
int i ;
{
int i ;
}
}
}

--drkm

Avatar
kanze
Pierre Maurette wrote:
[...]
Je pense qu'il est de coutume de lui attribuer le sens de «
scope ». Il est vrai que la première variable ci-dessous ne
sera détruite qu'à la fin de son scope, pas lorsqu'elle
cesse (à chaque fois qu'elle cesse) d'être visible :

void g() {
{
int i ;
{
int i ;
}
}
}


Prenons plutôt un exemple réaliste:

int main(int argc, char * argv[])
{
double f;
char texttemp[256];

/* travailler sur les arguments de la ligne de commande */

MenuPrincipal(f);

return EXIT_SUCCESS;
}


Pourquoi pas un autre exemple réaliste :

void
f( int i )
{
if ( i > 0 ) {
char texttemp[ 256 ] ;
// ...
} else {
// ...
}
// ...
}

Parler de quand la variable sera détruite est un peu difficile
dans le cas de char[], parce que les destructeurs n'ont pas un
comportement visible. Mais si tu remplace char par quelque chose
avec un destructeur réel, c'est clair que les destructeurs
seront appelé à la fin du bloc, et non à la fin de la fonction.

texttemp[] utilsera inutilement de la mémoire pendant presque
toute la durée du programme. Peut-être sans importance,
peut-être à modifier.


Le problème, c'est que tu n'as pas beaucoup d'influence dans
l'histoire. Même dans mon exemple, la plupart des compilateurs
ne libèrera la mémoire qu'à la fin de la fonction. En revanche,
un compilateur est aussi libre à remarquer que la variable ne
sert plus, même en étant visible, et de libérer la mémoire
avant. (C'est même assez fréquent dans le cas des types de base,
comme int.)

--
James Kanze GABI Software http://www.gabi-soft.fr
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
drkm
writes:

un compilateur est aussi libre à remarquer que la variable ne
sert plus, même en étant visible, et de libérer la mémoire
avant. (C'est même assez fréquent dans le cas des types de base,
comme int.)


Tu veux dire que c'est permis aussi pour les types définis par
l'utilisateur ? Avec destructeur trivial, alors, je suppose.

--drkm

Avatar
drkm
Loïc Joly writes:

drkm wrote:

writes:

un compilateur est aussi libre à remarquer que la variable ne
sert plus, même en étant visible, et de libérer la mémoire
avant. (C'est même assez fréquent dans le cas des types de base,
comme int.)


Tu veux dire que c'est permis aussi pour les types définis par
l'utilisateur ? Avec destructeur trivial, alors, je suppose.


Je ne suis pas certain de ce que tu veux dire. N'aurais-tu pas trop
coupé dans le message auquel tu réponds ?


Non. Si tu libères la mémoire d'un objet avant l'appel à son
destructeur non-trivial, boom. Si tu appelles le destructeur d'un
objet avant la fin de son scope (ou après, comme en Java), adieu le
RAII.

J'aurais plus compris ta réponse si elle se rapportait à :


Moi pas. Que la libération de la mémoire soit faite juste après
l'appel au destructeur ou plus tard, je m'en fous un peu, je fais
confiance au compilo.

Le problème, c'est que tu n'as pas beaucoup d'influence dans
l'histoire. Même dans mon exemple, la plupart des compilateurs
ne libèrera la mémoire qu'à la fin de la fonction.


Et dans ce cas, j'ajouterai que James a pris soin de bien distinguer
"appel du destructeur" et "libération mémoire". Même si les deux ont
habituellement lieu en même tepms, il peut être une optimisation de ne
libérer la mémoire que plus tard.


En même temps ? Tu veux dire juste après, non ?

--drkm



Avatar
drkm
James Kanze writes:

drkm wrote:

Non. Si tu libères la mémoire d'un objet avant l'appel à son
destructeur non-trivial, boom. Si tu appelles le destructeur
d'un objet avant la fin de son scope (ou après, comme en
Java), adieu le RAII.


Ça dépend de ce que fait le destructeur et ce que fait la reste
du programme. Selon la norme, il y a deux cas :

avec destructeur trivial :
avec destructeur non-trivial :


Je pensais qu'il était clair que je parlais uniquement du second
cas. Désolé.

J'aurais plus compris ta réponse si elle se rapportait à :


Moi pas. Que la libération de la mémoire soit faite juste
après l'appel au destructeur ou plus tard, je m'en fous un
peu, je fais confiance au compilo.


C'était quand même un peu ce dont il était question dans le
posting auquel je répondais.


En effet.

Dans la
pratique, je ne connais pas de compilateur qui essaie de
différer l'exécution du destructeur non-trivial.


Ça serait permis ? Si tu penses au « as-if », je ne vois pas
comment un compilo pourrait respecter le « as-if » sans effectivement
appeler un destructeur non-trivial au moment où il devrait l'être.
C'est possible, ça (je veux dire technologiquement) ?

--drkm



Avatar
kanze
drkm wrote:
James Kanze writes:


[...]
Dans la
pratique, je ne connais pas de compilateur qui essaie de
différer l'exécution du destructeur non-trivial.


Ça serait permis ? Si tu penses au « as-if », je ne vois pas
comment un compilo pourrait respecter le « as-if » sans
effectivement appeler un destructeur non-trivial au moment où
il devrait l'être. C'est possible, ça (je veux dire
technologiquement) ?


Ça dépend ce que fait le destructeur, et ce qu'on fait autour de
lui. Considère quelque chose comme le suivant :

void
f( double x, double y )
{
{
ClassWithDtor d ;
// ... (mais sans jamais prendre l'adresse de x
// ni de y.)
}
double z = x * x + 2 * x * y + y * y ;
std::cout << z << 'n' ;
}

Dans ce cas-ci, le compilateur pourrait différer l'appel du
destructeur de d après la ligne qui calcule z, parce que le
destructeur de d ne peu pas modifier x ni y, et le calcul de z
ne dépend que de ces deux valeurs. Le résultat du programme
serait absolument identique.

Si l'implémentation de ClassWithDtor est :

struct ClassWithDtor
{
char* p ;
ClassWithDtor() : p( malloc( 100 ) ) {}
~ClassWithDtor() { free( p ) ; }
} ;

et que le compilateur le voit, il pourrait même différer l'appel
du destructeur après la dernière ligne de la fonction. La même
chose ne vaut pas, en revanche, si ClassWithDtor s'était servi
de new/delete à la place de malloc/free, à moins de pouvoir
determiner que l'utilisateur n'a pas remplacé les fonctions
operator new et operator delete.

Dans la pratique, je vois mal un compilateur qui applique cette
optimisation à ce niveau -- c'est assez complexe à déterminer
quand on en a droit, et ça ne rapporte pas grand chose. En
revanche, après la génération du code, je crois que c'est même
assez courant que le compilateur réordonne les instructions
générées, et que dans un cas comme ceci, l'optimisation de
l'utilisation du pipeline pourrait faire que certaines
instructions de la ligne avec z s'exécute avant l'appel du
destructeur.

--
James Kanze GABI Software http://www.gabi-soft.fr
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
drkm
writes:

[...]

Ok. Donc le point-clef est d'arriver à identifier les relations
entre le code du destructeur et les instructions suivant l'endroit où
il doit être appelé. Pas une mince affaire ! Mais je suis toujours
étonné par l'intelligence des compilos (et de leurs auteurs).

--drkm
1 2