OVH Cloud OVH Cloud

Problème avec bsearch()

1 réponse
Avatar
JKB
Bonjour à tous,

J'ai un petit problème avec bsearch(), la fonction de comparaison
me renvoyant un segfault. Avant de poster ici, j'ai naturellement
vérifié avec valgrind qu'il n'y avait pas de corruption mémoire,
je n'ai rien trouvé. La même fonction de comparaison fonctionne
parfaitement (sur le même tableau) avec qsort().

J'ai écrit ceci :

static int
fonction_comparaison(const void *argument_1, const void *argument_2)
{
return(strcmp((unsigned char *) (**((struct_objet **) argument_1)).objet,
(unsigned char *) (**((struct_objet **) argument_2)).objet));
}

Le prototype de struct_objet est le suivant :
typedef struct objet
{
enum t_type type;
integer8 extension_type;
void *descripteur_bibliotheque;
volatile long nombre_occurrences;
pthread_mutex_t mutex;
void *objet;
} struct_objet;

Seul nous intéresse ici le pointeur sur le void. En l'occurence,
lorsque l'objet contient une chaîne de caractère, ce pointeur est un
pointeur sur un unsigned char *.

Lorsque cet objet est un tableau, le pointeur tape sur une structure
struct_tableau définie comme :

typedef struct tableau
{
integer8 nombre_elements;
struct_objet **elements;
} struct_tableau;

Le type integer8 est un entier 64 bits.

Le bout de code en question crée un objet tableau contenant
quatre chaînes de caractères.

On a donc une structure S :

struct_objet -> struct_tableau -> elements(4)

Je réécris les fonctions qui posent problème. Il peut s'être
glissées des coquilles car j'ai simplifié les expressions (les
structures en mémoire sont beaucoup plus complexes que cela).

qsort(((struct_tableau *) S->objet)->elements,
(size_t) ((struct_tableau *) S->objet)->nombre_elements,
sizeof(struct_objet *),
fonction_comparaison);

Ce tri fonctionne parfaitement et mon tableau est trié. Je
l'initialise pour mon test avec "key" "value" "i" "j", qsort()
retourne "i" "j" "key" "value".

J'essaie d'accéder à un élément particulier en utilisant bsearch() à
partir d'une autre fonction. J'utilise donc la même fonction
de comparaison. Avant d'appeler bsearch(), je vérifie que le contenu
de mon tableau est bon :

(indice->adresse (chaîne))

0->0x559c5070b3c8 (i)
1->0x559c50712958 (j)
2->0x559c506f0a38 (key)
3->0x559c506f09f8 (value)
0x559c506f0a38->key

Cette sortie est faite par :

for(i = 0; i < ((struct_tableau *) S->objet).nombre_elements;
printf("%d->%p (%s)\n", i,
(unsigned char *) ((struct_tableau*) S->objet)->elements[i])->objet,
(unsigned char *) ((struct_tableau*) S->objet)->elements[i])->objet,
i++);

Le nombre d'éléments est correct, le contenu des chaînes est bon.
Valgrind ne rale pas.

J'appelle donc bsearch() de la manière suivante (clef est
initialisée un peu plus haut) :

s_objet_element = bsearch((unsigned char *) clef,
((struct_tableau *) S->objet)->elements,
(size_t) ((struct_tableau *) S->objet)->nombre_elements,
sizeof(struct_objet *), fonction_comparaison);

Et je me tape un segfault dans la fonction de comparaison. J'ai donc
investigué un peu plus loin en regardant les adresses passées à
cette fonction.

Lors du premier appel de la fonction de comparaison, le second
argument semble bon (il pointe sur l'une de mes chaînes) :

0x559c506f0a38->key

En revanche, le premier argument pointe sur n'importe quoi :

0x41048c200e47038d->Trying to catch access violation

Le message "Trying to catch access violation" provient du
gestionnaire de signaux.

Là, je ne comprends pas. J'ai beau reprendre les arguments de
bsearch(), je passe bien dans l'ordre la clef, le pointeur sur la
base du tableau, le nombre d'éléments du tableau, la taille de
chaque élément et un pointeur sur la fonction de comparaison. Et les
types sont les bons.

Je sèche. Toute idée sera la bienvenue.

Bien cordialement,

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
=> http://loubardes.de-charybde-en-scylla.fr

10 réponses

1 2
Avatar
Samuel DEVULDER
Le 22/12/2018 à 15:22, Pascal J. Bourguignon a écrit :
Enfin, pour s'en sortir, surtout avec du code legacy, il faut absolument
introduire des abstraction fonctionnelles, et ne surtout pas utiliser
partout des accès aux structures et des casts!

Méfiance!! Comme je le dis concernant du code légacy: on ne répare pas
un code qui marche. Le mieux est l'ennemi du bien!
Souvent en faisant mieux, on obtient bien pire parce qu'on considère
comme mieux actuellement n'est pas forcément adapté au codes et aux
machines de l'époque. Exemple: un GC c'est bien de nos jours, mais sur
un ordi monocoeur il bloquera l’exécution de façon imprédictible ce qui
posera des soucis avec un traitement "temps réel".
a+
sam.
---
L'absence de virus dans ce courrier électronique a été vérifiée par le logiciel antivirus Avast.
https://www.avast.com/antivirus
Avatar
Pascal J. Bourguignon
Samuel DEVULDER writes:
Le 22/12/2018 à 15:22, Pascal J. Bourguignon a écrit :
Enfin, pour s'en sortir, surtout avec du code legacy, il faut absolument
introduire des abstraction fonctionnelles, et ne surtout pas utiliser
partout des accès aux structures et des casts!

Méfiance!! Comme je le dis concernant du code légacy: on ne r épare pas
un code qui marche. Le mieux est l'ennemi du bien!
Souvent en faisant mieux, on obtient bien pire parce qu'on considère
comme mieux actuellement n'est pas forcément adapté au codes et aux
machines de l'époque. Exemple: un GC c'est bien de nos jours, mais s ur
un ordi monocoeur il bloquera l’exécution de façon impr édictible ce
qui posera des soucis avec un traitement "temps réel".

On parle de code legacy, pas d'ordinateur legacy.
S'il s'agit de faire tourner de vielles machines, c'est sur, on ne va
pas pouvoir moderniser beaucoup le logiciel. Déjà, il n'y aura p as les
outils, ou ça va être une galère pour compiler les outils r écents sur
les vieux systèmes, avec toutes les dépendences, etc.
Mais reprendre du vieux code sur un système moderne, dans la mesure des
besoins, on peu le faire évoluer drastiquement.
Le seul cas où c'est difficile, c'est quand on veut du bogue-pour-bogu e,
car il est en effet facile d'éliminer beaucoup de bogues en le faisant
évoluer proprement…
--
__Pascal J. Bourguignon__
http://www.informatimago.com
Avatar
espie
In article ,
Pascal J. Bourguignon wrote:
Samuel DEVULDER writes:
Le 22/12/2018 à 15:22, Pascal J. Bourguignon a écrit :
Enfin, pour s'en sortir, surtout avec du code legacy, il faut absolument
introduire des abstraction fonctionnelles, et ne surtout pas utiliser
partout des accès aux structures et des casts!

Méfiance!! Comme je le dis concernant du code légacy: on ne répare pas
un code qui marche. Le mieux est l'ennemi du bien!
Souvent en faisant mieux, on obtient bien pire parce qu'on considère
comme mieux actuellement n'est pas forcément adapté au codes et aux
machines de l'époque. Exemple: un GC c'est bien de nos jours, mais sur
un ordi monocoeur il bloquera l’exécution de façon imprédictible ce
qui posera des soucis avec un traitement "temps réel".

On parle de code legacy, pas d'ordinateur legacy.
S'il s'agit de faire tourner de vielles machines, c'est sur, on ne va
pas pouvoir moderniser beaucoup le logiciel. Déjà, il n'y aura pas les
outils, ou ça va être une galère pour compiler les outils récents sur
les vieux systèmes, avec toutes les dépendences, etc.
Mais reprendre du vieux code sur un système moderne, dans la mesure des
besoins, on peu le faire évoluer drastiquement.
Le seul cas où c'est difficile, c'est quand on veut du bogue-pour-bogue,
car il est en effet facile d'éliminer beaucoup de bogues en le faisant
évoluer proprement…

Generalement, si c'est du vrai code, ca va commencer par utiliser des
techniques modernes de developpement, donc passer pas mal de temps a
ecrire des tests unitaires et des tests d'integration ;)
Avatar
Samuel DEVULDER
Le 24/12/2018 à 02:30, Pascal J. Bourguignon a écrit :
On parle de code legacy, pas d'ordinateur legacy.

Le "on ne répare pas un truc qui marche" et "le mieux est l'ennemi du
bien" s'appliquent dans les deux cas. Combien de fois n'ai-je pas vu un
logiciel moins bien marcher suite à une mise à jour... Et tout ca pour
faire "mieux"... Moauis...
sam.
---
L'absence de virus dans ce courrier électronique a été vérifiée par le logiciel antivirus Avast.
https://www.avast.com/antivirus
Avatar
David Larochette
Le 22-12-2018, JKB a écrit :
si j'avais le temps, je
réécrirais cette application dans un langage plus approprié que le C. Au
hasard, le C++
JKB

Dis, ça va ? Tu nous couves pas quelque-chose ?
Avatar
Samuel DEVULDER
Le 22/12/2018 à 23:24, JKB a écrit :
je
réécrirais cette application dans un langage plus approprié que le C. Au
hasard, le C++

$ cat c_vs_cxx.c
#include <stdio.h>
void main(void) {
int c=3;
printf("c=%dn", c);
printf("c++=%dn", c++);
}
$ cc c_vs_cxx.c
$ ./a.out
c=3
c++=3
$ exit
Humm de ce que j'en vois, même si on a l'impression d'avoir un coup
d'avance avec le c++, en pratique c et c++ valent la même chose...
sam :D
---
L'absence de virus dans ce courrier électronique a été vérifiée par le logiciel antivirus Avast.
https://www.avast.com/antivirus
Avatar
Pascal J. Bourguignon
Samuel DEVULDER writes:
Le 22/12/2018 à 23:24, JKB a écrit :
je
réécrirais cette application dans un langage plus approprié que le C. Au
hasard, le C++

$ cat c_vs_cxx.c
#include <stdio.h>
void main(void) {
int c=3;
printf("c=%dn", c);
printf("c++=%dn", c++);
}
$ cc c_vs_cxx.c
$ ./a.out
c=3
c++=3
$ exit
Humm de ce que j'en vois, même si on a l'impression d'avoir un coup
d'avance avec le c++, en pratique c et c++ valent la même chose...
sam :D

Il doit y avoir une bogue. J'obtiens ça comme résultat:
#include <stdio.h>
void main(void) {
int c=3;
int cpp=c++;
printf("c++ = %dn", cpp);
printf("c = %dn", c);
printf("c %s c++n", (c<cpp)?"<":((c>cpp)?">":"=="));
}
-*- mode: compilation; default-directory: "~/src/c/" -*-
Compilation started at Thu Dec 27 00:21:15
SRC="/Users/pjb/src/c/c-vs-c++.c" ; EXE="c-vs-c++" ; gcc -I. -L. -g3 -ggdb3 -o ${EXE} ${SRC} && ./${EXE} && echo status = $?
c++ = 3
c = 4
c > c++
Compilation exited abnormally with code 8 at Thu Dec 27 00:21:16
--
__Pascal J. Bourguignon__
http://www.informatimago.com
Avatar
Samuel DEVULDER
Le 27/12/2018 à 00:22, Pascal J. Bourguignon a écrit :
Il doit y avoir une bogue.

hmm... voyons...
J'obtiens ça comme résultat:
#include <stdio.h>
void main(void) {
int c=3;
int cpp=c++;
printf("c++ = %dn", cpp);
printf("c = %dn", c);
printf("c %s c++n", (c<cpp)?"<":((c>cpp)?">":"=="));
}

Normal, c'est pas le même programme, il ne fait absolument pas la même
chose. Je ne le redirais jamais assez: On ne réparer pas un truc qui
marche, sinon a on toutes les chances d'introduire des bugs, ce qui est
le cas ici.
a+
sam ;)
---
L'absence de virus dans ce courrier électronique a été vérifiée par le logiciel antivirus Avast.
https://www.avast.com/antivirus
Avatar
espie
In article ,
David Larochette wrote:
Le 22-12-2018, JKB a écrit :
si j'avais le temps, je
réécrirais cette application dans un langage plus approprié que le C. Au
hasard, le C++
JKB

Dis, ça va ? Tu nous couves pas quelque-chose ?

Mort de rire!
Avatar
ellimann
Le samedi 22 Décembre 2018 à 10:22 par JKB :
Bonjour à tous,
J'ai un petit problème avec bsearch(), la fonction de comparaison
me renvoyant un segfault. Avant de poster ici, j'ai naturellement
vérifié avec valgrind qu'il n'y avait pas de corruption
mémoire,
je n'ai rien trouvé. La même fonction de comparaison fonctionne
parfaitement (sur le même tableau) avec qsort().
J'ai écrit ceci :
static int
fonction_comparaison(const void *argument_1, const void *argument_2)
{
return(strcmp((unsigned char *) (**((struct_objet **) argument_1)).objet,
(unsigned char *) (**((struct_objet **) argument_2)).objet));
}
Le prototype de struct_objet est le suivant :
typedef struct objet
{
enum t_type type;
integer8 extension_type;
void *descripteur_bibliotheque;
volatile long nombre_occurrences;
pthread_mutex_t mutex;
void *objet;
} struct_objet;
Seul nous intéresse ici le pointeur sur le void. En l'occurence,
lorsque l'objet contient une chaîne de caractère, ce pointeur est
un
pointeur sur un unsigned char *.
Lorsque cet objet est un tableau, le pointeur tape sur une structure
struct_tableau définie comme :
typedef struct tableau
{
integer8 nombre_elements;
struct_objet **elements;
} struct_tableau;
Le type integer8 est un entier 64 bits.
Le bout de code en question crée un objet tableau contenant
quatre chaînes de caractères.
On a donc une structure S :
struct_objet -> struct_tableau -> elements(4)
Je réécris les fonctions qui posent problème. Il peut
s'être
glissées des coquilles car j'ai simplifié les expressions (les
structures en mémoire sont beaucoup plus complexes que cela).
qsort(((struct_tableau *) S->objet)->elements,
(size_t) ((struct_tableau *) S->objet)->nombre_elements,
sizeof(struct_objet *),
fonction_comparaison);
Ce tri fonctionne parfaitement et mon tableau est trié. Je
l'initialise pour mon test avec "key" "value"
"i" "j", qsort()
retourne "i" "j" "key" "value".
J'essaie d'accéder à un élément particulier en
utilisant bsearch() à
partir d'une autre fonction. J'utilise donc la même fonction
de comparaison. Avant d'appeler bsearch(), je vérifie que le contenu
de mon tableau est bon :
(indice->adresse (chaîne))
0->0x559c5070b3c8 (i)
1->0x559c50712958 (j)
2->0x559c506f0a38 (key)
3->0x559c506f09f8 (value)
0x559c506f0a38->key
Cette sortie est faite par :
for(i = 0; i < ((struct_tableau *) S->objet).nombre_elements;
printf("%d->%p (%s)n", i,
(unsigned char *) ((struct_tableau*)
S->objet)->elements[i])->objet,
(unsigned char *) ((struct_tableau*)
S->objet)->elements[i])->objet,
i++);
Le nombre d'éléments est correct, le contenu des chaînes
est bon.
Valgrind ne rale pas.
J'appelle donc bsearch() de la manière suivante (clef est
initialisée un peu plus haut) :
s_objet_element = bsearch((unsigned char *) clef,
((struct_tableau *) S->objet)->elements,
(size_t) ((struct_tableau *) S->objet)->nombre_elements,
sizeof(struct_objet *), fonction_comparaison);
Et je me tape un segfault dans la fonction de comparaison. J'ai donc
investigué un peu plus loin en regardant les adresses passées
à
cette fonction.
Lors du premier appel de la fonction de comparaison, le second
argument semble bon (il pointe sur l'une de mes chaînes) :
0x559c506f0a38->key
En revanche, le premier argument pointe sur n'importe quoi :
0x41048c200e47038d->Trying to catch access violation
Le message "Trying to catch access violation" provient du
gestionnaire de signaux.
Là, je ne comprends pas. J'ai beau reprendre les arguments de
bsearch(), je passe bien dans l'ordre la clef, le pointeur sur la
base du tableau, le nombre d'éléments du tableau, la taille de
chaque élément et un pointeur sur la fonction de comparaison. Et
les
types sont les bons.
Je sèche. Toute idée sera la bienvenue.
Bien cordialement,
JKB
--
Si votre demande me parvient sur carte perforée, je titiouaillerai
très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
=> http://loubardes.de-charybde-en-scylla.fr
Je pense qu"il serait sougaitable de voir votre code en entier. Cependant je vius donne les indications suivantes;
La fonction de bibliothèque C void *bsearch(const void *key, const void *base, size_t nitems, size_t size, int (*compar)(const void *, const void *)) function search an array of nitems objects, which the initial member is pointed to by base, pour un membre qui correspond à l’objet pointé, par clé. La taille de chaque membre du tableau est spécifiée par la taille.
Le contenu du tableau doit être en ordre croissant selon la fonction de comparaison référencée par compar.
Déclaration :
Voici la déclaration pour la fonction bsearch().
void *bsearch(const void *key, const void *base, size_t nitems, size_t size, int (*compar)(const void *, const void *)
Cette fonction utilise les paramètres ci-dessois :
Paramètres :
key − Il s’agit du pointeur de l’objet qui sert de clé pour la recherche, caster comme un vide*.
base − Il s’agit du pointeur vers le premier objet du tableau où la recherche est effectuée, lancé en tant que vide*.
nitems − Il s’agit du nombre d’éléments dans le tableau pointé par la base.
size − Il s’agit de la taille en octets de chaque élément du tableau.
compare − C’est la fonction qui compare deux éléments.
Valeur de retour :
Cette fonction renvoie un pointeur à une entrée dans le tableau qui correspond à la clé de recherche. Si la clé n’est pas trouvée, un pointeur NULL est retourné.
1 2