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

Variable locale détruite lors de l'appel de bsearch ?

7 réponses
Avatar
Michel
Bonjour,

J'ai enfin trouvé comment résoudre mon problème d'exception lors de
l'appel à bsearch.
Il fallait remplacer la variable locale char coupleLettres[3];
par une variable/zone mémoire allouée dynamiquement.
Mais je cherche à comprendre ce qui se passait avec le tableau de char
comme variable locale.
Est-ce que cette variable était détruite lors de l'appel à bsearch ou
lors de l'appel à la fonction de comparaison ou c'est une autre raison ?
Ci dessous le code qui fonctionne bien et celui qui génèrait l'exception.
Michel

Le code qui fonctionne bien :

#include <stdio.h>
#include <stdlib.h>
#include <search.h>
#include <string.h>
#include <ctype.h>
#include "entete.h"
#include "definition.h"

#define NOM_MODULE "VerifierChampsPub.c"

Booleen VerifierChampsPub(char *pCaractereTrouve,
InfosFichier *pInfosFichierIni_t) {

char *pCoupleLettres = NULL;
Booleen idPub = FAUX;

/* allocation mémoire pour stocker le couple de lettre à comparer */
if((pCoupleLettres = malloc(sizeof(char) * 3))
== NULL) {
TraiterErreur(NOM_MODULE, __LINE__ - 1, "\202chec d'allocation
m\202moire par \"malloc\".");
}

if(isdigit(*(pCaractereTrouve + 1)) &&
isdigit(*(pCaractereTrouve + 2)) &&
isalpha(*(pCaractereTrouve + 3)) &&
isalpha(*(pCaractereTrouve + 4)) &&
isdigit(*(pCaractereTrouve + 5)) &&
isdigit(*(pCaractereTrouve + 6))) {

/* copie du couple de lettre dans la zone mémoire allouée
dynamiquement*/
strncpy(pCoupleLettres, pCaractereTrouve + 3,
LONGUEUR_COUPLE_LETTRES);
pCoupleLettres[2] = '\0';

/* recherche du couple de lettres dans le tableau */
if(bsearch(&pCoupleLettres,
pInfosFichierIni_t->pTabIdPub,
pInfosFichierIni_t->nbElement,
sizeof(char*), ComparerChaine) != NULL)
idPub = VRAI;
}

return idPub;
}


Le code qui génèrait l'exception :

#include <stdio.h>
#include <stdlib.h>
#include <search.h>
#include <string.h>
#include <ctype.h>
#include "entete.h"
#include "definition.h"

Booleen VerifierChampsPub(char *pCaractereTrouve,
InfosFichier *pInfosFichierIni_t) {

char coupleLettres[3];
Booleen idPub = FAUX;

if(isdigit(*(pCaractereTrouve + 1)) &&
isdigit(*(pCaractereTrouve + 2)) &&
isalpha(*(pCaractereTrouve + 3)) &&
isalpha(*(pCaractereTrouve + 4)) &&
isdigit(*(pCaractereTrouve + 5)) &&
isdigit(*(pCaractereTrouve + 6))) {

/* copie du couple de lettre dans lle tableau variable locale*/
strncpy(coupleLettres, pCaractereTrouve + 3, 3);
coupleLettres[2] = '\0';

/* recherche du couple de lettres dans le tableau */
if(bsearch(&coupleLettres,
pInfosFichierIni_t->pTabIdPub,
pInfosFichierIni_t->nbElement,
sizeof(char*), ComparerChaine) != NULL)
idPub = VRAI;
}

return idPub;
}

7 réponses

Avatar
Randolf Carter
Salut,

Le plantage dans le cas du tableau déclaré sur la stack provient d'une
erreur d'adressage. Le tableau local n'est absolument pas détruit, il est
bel et bien alloué sur la pile et reste présent tant que l'on reste dans des
fonctions appelées par ta fonction VerifierChampsPub().

L'origine du souci, c'est ta fonction de comparaison...
int ComparerChaine(const void *chaine1, const void *chaine2)
{
return strcmp(*(char**) chaine1, *(char**) chaine2);
}
...qui demande des pointeurs sur des pointeurs de caractères -> char **.

Dans le cas de l'allocation dynamique, tu passes bien un char ** :
char *pCoupleLettres;
bsearch(&pCoupleLettres, [...]);
En d'autres termes, le type de &pCoupleLettres est bien char **.

Dans le cas du tableau local ce n'est pas le cas :
char coupleLettres[3];
bsearch(&coupleLettres, [...]);
Il est impossible d'obtenir l'adresse de l'adresse de coupleLettres.
coupleLettres est *déjà* l'adresse de tes caractères sur la pile, et
demander l'adresse de son adresse n'a pas de sens. C'est un peu subtile à
comprendre, mais avec un peu de réflexion cela vient tout seul ;-)
Tu peux vérifier ce que je raconte en comparant le résultat des expressions
coupleLettres et &coupleLettres : tu verras que tu obtiendras le même
résultat.
Exemple :
char *p = coupleLettres; /* Récupère l'adresse du premier caractère */
char *q = &coupleLettres; /* Récupère l'adresse de coupleLettres */
Et à coup sûr p == q. Car l'adresse de coupleLettres sur la pile *est*
l'adresse de son premier caractère. C'est pour cela que demander l'adresse
de son adresse est impossible.

Pour résoudre ton problème sans passer par une allocation dynamique
injustifiée (c'est lourd pour ce que tu en fais), il suffit de passer par
une variable locale intermédiaire dont tu pourras prendre l'adresse. Exemple
:
char coupleLettres[3];
[...]
char *p = coupleLettres;
bsearch(&p, [...]);
Là tu as bien un char ** (un pointeur p, pointant sur l'adresse d'un tableau
de char).

--
À plus,
- Randolf
Enlever les X de mon adresse pour me répondre directement.
Avatar
Michel

Dans le cas du tableau local ce n'est pas le cas :
char coupleLettres[3];
bsearch(&coupleLettres, [...]);
Il est impossible d'obtenir l'adresse de l'adresse de coupleLettres.
coupleLettres est *déjà* l'adresse de tes caractères sur la pile, et
demander l'adresse de son adresse n'a pas de sens. C'est un peu subtile à
comprendre, mais avec un peu de réflexion cela vient tout seul ;-)
Tu peux vérifier ce que je raconte en comparant le résultat des expressions
coupleLettres et &coupleLettres : tu verras que tu obtiendras le même
résultat.
Exemple :
char *p = coupleLettres; /* Récupère l'adresse du premier caractère */
char *q = &coupleLettres; /* Récupère l'adresse de coupleLettres */
Et à coup sûr p == q. Car l'adresse de coupleLettres sur la pile *est*
l'adresse de son premier caractère. C'est pour cela que demander l'adresse
de son adresse est impossible.


Si j'ai bien compris :
coupleLettres étant le nom du tableau, c'est donc une constante et une
constante n'a pas d'adresse.
C'est pour cela qu'il est impossible d'obtenir l'adresse de l'adresse de
coupleLettres?


Pour résoudre ton problème sans passer par une allocation dynamique
injustifiée (c'est lourd pour ce que tu en fais),


Oui, ça je l'avais remarqué, mais ça permettait au moins de résoudre mon
problème d'exception.

il suffit de passer par
une variable locale intermédiaire dont tu pourras prendre l'adresse. Exemple
:
char coupleLettres[3];
[...]
char *p = coupleLettres;
bsearch(&p, [...]);
Là tu as bien un char ** (un pointeur p, pointant sur l'adresse d'un tableau
de char).


Effectivement, ça fonctionne bien.
Merci bcp.
Michel

Avatar
Randolf Carter
Si j'ai bien compris :
coupleLettres étant le nom du tableau, c'est donc une constante et une
constante n'a pas d'adresse.
C'est pour cela qu'il est impossible d'obtenir l'adresse de l'adresse de
coupleLettres?


C'est un peu ça, mais pas vraiment.
coupleLettres désigne une zone mémoire de 3 octets sur la pile (un tableau
de 3 chars), et tu accèdes à tes caractères grace aux expressions
coupleLettres[0], coupleLettres[1] et coupleLettres[2]. Parfait.
Lorsque tu déclares n'importe quelle autre variable locale, c'est à peu près
le même principe. Par exemple si j'écris
long i;
le symbole i me sert à accéder au 4 octets (sur un système 32bits) réservés
sur la pile pour mon entier. Pour obtenir l'adresse de i sur la pile,
j'utilise l'expression &i.
De même, si je veux l'adresse de mon tableau sur la pile, j'utilise
l'expression &coupleLettres.

Maintenant comment ferais-tu pour obtenir un long ** pointant sur un
pointeur sur i ?
Impossible. Tu n'as nul part une zone mémoire (variable ou autre) stockant
l'adresse de i dont tu pourrais prendre l'adresse.
Pour le tableau coupleLettres c'est la même chose. Il n'y a nul part une
zone mémoire contenant sont adresse. C'est pour cela qu'il faut passer par
une variable intermédiaire, dans laquelle on place l'adresse du tableau,
afin de prendre l'adresse de cette variable...

J'espère que c'est plus clair maintenant ;-)

--
À plus,
- Randolf
Enlever les X de mon adresse pour me répondre directement.

Avatar
Michel

Par exemple si j'écris
long i;
le symbole i me sert à accéder au 4 octets (sur un système 32bits) réservés
sur la pile pour mon entier. Pour obtenir l'adresse de i sur la pile,
j'utilise l'expression &i.
De même, si je veux l'adresse de mon tableau sur la pile, j'utilise
l'expression &coupleLettres.

Maintenant comment ferais-tu pour obtenir un long ** pointant sur un
pointeur sur i ?


Ok, je pense avoir compris :
Si je déclare UNIQUEMENT :
int i = 10;
Je peux bien sûr afficher l'adresse de i avec l'opérateur &.
i étant une case mémoire d'adresse &i, cette adresse N'A PAS D'ADRESSE.

Pour obtenir l'adresse de l'adresse de i (c'est à dire l'adresse
de '&i'), je n'ai pas d'autre choix que DE CREER une variable de type
pointeur qui contiendra l'adresse de l'adresse de &i; ce qui donne :

int i = 10;
int *pAdresseDeI = &i;
int **AdresseDeAdresseDeI = &pAdresseDeI;

J'ai bon là?

Impossible. Tu n'as nul part une zone mémoire (variable ou autre) stockant
l'adresse de i dont tu pourrais prendre l'adresse.
Pour le tableau coupleLettres c'est la même chose. Il n'y a nul part une
zone mémoire contenant sont adresse. C'est pour cela qu'il faut passer par
une variable intermédiaire, dans laquelle on place l'adresse du tableau,
afin de prendre l'adresse de cette variable...

J'espère que c'est plus clair maintenant ;-)


Ah oui, c'est bien expliqué.
Merci bcp.
Michel

Avatar
Yves ROMAN


Par exemple si j'écris
long i;
le symbole i me sert à accéder au 4 octets (sur un système 32bits) réservés
sur la pile pour mon entier. Pour obtenir l'adresse de i sur la pile,
j'utilise l'expression &i.
De même, si je veux l'adresse de mon tableau sur la pile, j'utilise
l'expression &coupleLettres.

Maintenant comment ferais-tu pour obtenir un long ** pointant sur un
pointeur sur i ?


Ok, je pense avoir compris :
Si je déclare UNIQUEMENT :
int i = 10;
Je peux bien sûr afficher l'adresse de i avec l'opérateur &.
i étant une case mémoire d'adresse &i, cette adresse N'A PAS D'ADRESSE.

Pour obtenir l'adresse de l'adresse de i (c'est à dire l'adresse
de '&i'), je n'ai pas d'autre choix que DE CREER une variable de type
pointeur qui contiendra l'adresse de l'adresse de &i; ce qui donne :

int i = 10;
int *pAdresseDeI = &i;
int **AdresseDeAdresseDeI = &pAdresseDeI;

J'ai bon là?

Oui.

Une adresse n'existe que pour une variable (au sens 'zone mémoire contenant des
données').
Une adresse est une donnée.
Donc &i existe, est une donnée, et n'a pas d'adresse.
Il faut donc utiliser une autre variable pouvant contenir une adresse ( pointeur) et la remplir avec cette donnée.
L'adresse de cette variable est alors l'adresse d'une variable contenant
l'adresse d'une autre variable contenant un entier.


Avatar
Horst Kraemer
On Fri, 26 Dec 2003 17:20:10 +0100, Michel <noSpam> wrote:

Bonjour,

J'ai enfin trouvé comment résoudre mon problème d'exception lors de
l'appel à bsearch.
Il fallait remplacer la variable locale char coupleLettres[3];
par une variable/zone mémoire allouée dynamiquement.
Mais je cherche à comprendre ce qui se passait avec le tableau de char
comme variable locale.
Est-ce que cette variable était détruite lors de l'appel à bsearch ou
lors de l'appel à la fonction de comparaison ou c'est une autre raison ?


Ton problème est le suivant: Tu as un pointeur vers le premier élément
d'un tableau de pointeurs vers char (représenté ici par un tableau de
pointeur vers char) et tu veux compararer les pointeurs en comparant
les chaines vers lesquelles ces pointeurs pointent. Pour les comparer
de facon symmétrique (en utilisant une fonction de comparaison qui
traite le premier pointeur (clé) et le deuxième pointeur (élément) de
la même facon - c.a.d. comme char** - il faut que le pointeur "clé"
fourni comme premier paramètre à bsearch soit aussi un pointeur vers
pointeur vers char.

int compars(void *key, void *e)
/* comparaison symmétrique */
{
return strcmp(*(char**)key,*(char**)e);
}

int compara(void *key, void *e)
/* comparaison asymmétrique */
{
return strcmp(key,*(char**)e);
}


char *a[3] = {"abc","def","ghi"};

char ctab[4] = "def";

char *pc = ctab;

Maintenant

bsearch(&ctab,a,3,sizeof *a,compars);

a le même effet que

bsearch(ctab,a,3,sizeof *a,compars);


et échoue. La raison est que l'expression &ctab ne donne *pas*
l'adresse d'un pointeur vers char qui pointe vers 'd' - parce qu'un
tableau n'est *pas* un pointeur (tu conais le sermon?) mais &ctab est
l'adresse du tableau ctab qui est en même temps l'adresse du premier
caractère de ctab, c.a.d. l'adresse du 'd' dans "def". Il y a toujours
l'identité

(void*)&tab = (void*)tab;

quand 'tab' est un tableau (!!!).

[
Je répète: Le problème est que l'expression 'tab' est en général
convertie en un pointeur constant qui pointe vers le premièr élément
de 'tab' - sauf dans une expression comme &tab our sizeof tab. Quand
on prend l'adresse d'un tableau, le résultat n'est *pas* un pointeur
vers un pointeur constant qui pointe vers le premier élement de 'tab'
mais simplement l'adresse de 'tab' "en personne". La conversion
automatique tableau->pointeur n'a *pas* lieu quand on prend l'adresse
d'un tableau (&tab) ou la taille d'un tableau (sizeof tab).
]

Donc à l'intérieur de 'compars' la transformation

*(char**)(void*)(&k) == *(char**)(void*)k


interprète un pointeur qui pointe vers un char comme un (char**) et
l'expression ci-dessus donne un char et non un pointeur vers char.

Il y deux remèdes sans introduire une allocation dynamique pour la
clè.

1) Utiliser la fonction asymmétrique compara ci-dessus qui traite le
premier paramètre comme char*. C'est possible pour bsearch (et non
pour qsort!) parce que le premier paramètre dans la fonction de
comparaison est toujours identique au premier paramètre de bsearch:

bsearch(ctab,a,3,sizeof *a,compara);

2) Introduire une variable artificielle pc comme ci-dessus et appeler

bsearch(&pc,a,3,sizeof *a,compars);

en utilisant la fonction de comparaison symmétrique compars.
Maintenant &pk est un vrai pointeur vers pointeur vers char parce que
maintenant

*(char**)(void*)&pc

est un pointeur vers le 'd' dans "def".

IMHO l'utilisation d'une allocation dynamique est la solution la mois
élégante.

--
Horst

Avatar
Michel

Je répète: Le problème est que l'expression 'tab' est en général
convertie en un pointeur constant qui pointe vers le premièr élément
de 'tab' - sauf dans une expression comme &tab our sizeof tab. Quand
on prend l'adresse d'un tableau, le résultat n'est *pas* un pointeur
vers un pointeur constant qui pointe vers le premier élement de 'tab'
mais simplement l'adresse de 'tab' "en personne". La conversion
automatique tableau->pointeur n'a *pas* lieu quand on prend l'adresse
d'un tableau (&tab) ou la taille d'un tableau (sizeof tab).


OK, c'est très clair.

Merci bien.
Michel