Malloc : Process returned -1073741819

Résolu/Fermé
pep - 3 déc. 2009 à 12:02
 pep - 13 déc. 2009 à 22:17
Bonjour,

Problème avec un malloc que je ne parviens pas à interpréter.
Je travaille sous Code::Blocks avec GCC et uniquement les libraires standards d'ANSI C.

Il s'agit d'une fonction C qui construit récursivement un arbre n-aire pour ranger des permutations d'un groupe symétrique. Le facteur de branchement est l'ordre du groupe symétrique moins la distance à la racine.

La fonction se comporte correctement en mode debug, mais provoque l'arrêt du programme en mode release, après avoir été appelée plusieurs fois avec succès.

Un mauvais patch qui force un seuil bas de taille de zone d'allocation permet un fonctionnement normal.

Ci-dessous le morceau de code qui produit un arrêt du programme.
Pré-conditions : t correctement alloué, t->children NULL, dist2end = 6

Si le code d'erreur et le comportement évoque quelque-chose de connu pour l'un(e) d'entre-vous, je serais heureux de profiter de votre éclairage.

Merci

...

if (VERBOSE_DEBUG) printf("t->children = (permut_group_tree *) malloc(%u * %u);\n", dist2end, sizeof(permut_group_tree));

/// EN MODE DEBUG, PAS DE PROBLEME...
/// EN MODE RELEASE, pour size == 24 (6 * 4) arrêt brutal du programme (pas de test de pointeur NULL possible) : Process returned -1073741819 (0xC0000005)

/// CECI EST UNE ENORME VERRUE POUR QUE CA FONCTIONNE :
//unsigned size = dist2end * sizeof(permut_group_tree);
//size = size > 50 ? size : 50;

t->children = (permut_group_tree *) malloc(dist2end * sizeof(permut_group_tree));//size);
if (VERBOSE_DEBUG) printf("---* alloc %u children tab\n", dist2end);

...

5 réponses

mamiemando Messages postés 33446 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 20 décembre 2024 7 812
3 déc. 2009 à 19:44
Si ça marche en debug mais pas en release, c'est probablement parce que tu as un débordement de pointeur. Comme la manière dont est allouée la mémoire diffère entre les deux modes (légèrement suralloué en mode debug), il n'est pas surprenant que le problème ne survienne pas en mode debug. La seule façon de faire si le debugger ne t'aide pas et qui me vient à froid, c'est de mettre des printf pour voir où ça plante.

Bonne chance
0
Merci pour ta réponse.
Je ne savais pas qu'il y avait un débord d'allocation en mode debug, c'est un indice.
L'explosion intervient 'dans' le malloc : celui-ci est directement suivi d'un printf qui ne s'affiche pas.
0
loupius Messages postés 697 Date d'inscription dimanche 1 novembre 2009 Statut Membre Dernière intervention 31 décembre 2017 148 > pep
3 déc. 2009 à 23:23
Attention: bien souvent une erreur d'allocation mémoire ne se révèle pas immédiatement et parfois de manière aléatoire. La seule certitude que l'on puisse avoir c'est que lorsqu'intervient un 'segment fault', l'erreur a été commise avant; On ne peut pas dire mieux que ce qu'aurait dit Mr Lapalisse ;-)
Bonne continuation.
0
mamiemando Messages postés 33446 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 20 décembre 2024 7 812
7 déc. 2009 à 10:52
Elles ne sont malheureusement pas pertinentes : il s'agit d'une structure et d'une fonctionnalité de caching d'instances d'objets mathématiques. J'ai dégrafé les 160 lignes de code du reste, ce morceau apparaît donc hors contexte.

Ok.

Pour la 2), c'est une question de forme et d'habitude : une boucle for est une construction élaborée sur la base d'un while, et j'encourage mes étudiants à se passer du for pour développer leur sens algorithmique.

Ah... Ben chacun son point de vue, mais je trouve que ça complique inutilement le code. En plus un for est potentiellement plus optimisé qu'un while (dépend du compilateur). Je pense que la majorité des personnes en entreprise seraient assez... mmmh... surprise de voir ce genre de boucles :-)

Sur le fond, je cherche toujours où se joue l'effet de bord assassin : je penche pour une bêtise évidente, .. mais seulement une fois le nez dessus^^

Il faudrait me dire ce que ton morceau de code est sensé faire. Chez moi en tout cas après correction des while en for il ne plantait pas. Quant à dire qu'il faisait ce qu'il fallait, difficile à dire.
0
Salut,

Pep : ne t’étonnes pas avec un truc de bac + 10 en algorithmique d’obtenir si peu de feedback sur ton problème.

Effectivement, ton bug est toute bête : tu as écrit :
t_next = t->children[a[d_index]] = …
au lieu de
t_next = t->children[d_index] = …
(105ième ligne).

Mamie : je t’invite à consulter les pages 60 et 61 du Kernighan & Ritchie, avant de dire trop d’âneries. La polémique du « for against while » date des années 60 (ça nous rajeunit pas^^). Il te sera par ailleurs profitable d’approfondir tes connaissances en techniques de compilation et d’optimisation de code.

k.
0
merci k., tu viens de me faire gagner un temps considérable.
0
mamiemando Messages postés 33446 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 20 décembre 2024 7 812
4 déc. 2009 à 01:41
Je suis d'accord avec loupious. A mon avis ce débordement doit se jouer à peu de choses près s'il ne déclenche pas d'erreur en mode debug.

A priori c'est un malloc sous-dimensionné ou une boucle qui parcourt une zone mémoire et qui en sort.

Si le code n'est pas trop gros on peut imaginer que tu nous le mettes à disposition (éventuellement via une archive sur rapidshare) pour essayer de voir.

Bonne chance
-1
Merci pour ta proposition que je saisis au vol.

J'ai isolé en 160 lignes les éléments de structures et de fonctionnalités minimaux qui reproduisent le bug et ajouté des traces explicites.

Un progrès : le malloc retourne à présent un pointeur NULL, alors qu'auparavant il plantait le programme sans davantage de prises.

Le plantage intervient ici au troisième appel de la fonction get_permut dans le main, à mi-chemin de la descente récursive via .

#include <stdio.h>
#include <stdlib.h>

#define DEBUG 1

typedef struct permut
{
unsigned n;
unsigned *a;

} permut;

/// stockage des éléments du groupe symétrique d'ordre n selon l'ordre lexicographique
typedef struct permut_group_tree_node
{
unsigned n; /// ordre du groupe hérité du parent
permut *p; /// non NULL ssi feuille
struct permut_group_tree_node **children; /// la descendance

} permut_group_tree_node;

typedef permut_group_tree_node *permut_group_tree;

/** affiche un vecteur d'entier positifs **/
void print_vector(char *s, unsigned *v, unsigned n)
{
if (n == 0) printf("%s : {}", s);
else
{
unsigned i = 0;
printf("%s : { ", s);
while (i++ < n) printf("%u ", v[i - 1]);
printf("}");
}
}

static permut_group_tree *permut_groups = NULL; /// stockage statique des groupes de permutation
static unsigned max_order = 15;

/** initialise l'espace de stockage statique pour les groupes de permutations **/
void init_permut_groups()
{
if (DEBUG) printf("\n---* init_permut_groups[%u]\n\n", max_order);
permut_groups = (permut_group_tree *) malloc(max_order * sizeof(permut_group_tree));
unsigned n = max_order;
while (n-- > 0) permut_groups[n] = NULL;
}

/** private : recursive method to search/init storage structure **/
permut *find_or_create_permut(unsigned *a, permut_group_tree t, unsigned d)
{
if (DEBUG)
{
printf("\nFIND OR CREATE (current tree node address : %u)\n", t);
printf(" group order: %u", t->n);
printf(" depth: %u", d);
printf(" distance to end: %u\n", t->n - d);
printf(" local permut address (!= NULL iff leaf node) : %u\n", t->p);
printf(" local children array address : %u\n", t->children);
}

unsigned n = t->n;
unsigned dist2end = t->n - d;

if (dist2end > 0 && t->children == NULL)
{
if (DEBUG) printf("---* t->children NULL => malloc(%u * %u);\n", dist2end, sizeof(permut_group_tree));

t->children = (permut_group_tree *) malloc(dist2end * sizeof(permut_group_tree));
if (DEBUG)
{
if (t->children != NULL) printf("---* %u children tab allocated\n", dist2end);
else
{
printf("---* %u children tab NOT allocated !!!!!!!!!!!!!!!!!!\n", dist2end);
printf(">>>> ON PROGRESSE : CATCH DE l'ERR !!!!!!!!!!!!!!!!!\n");
exit(-1);
}
}

unsigned i = dist2end;
while (i-- > 0) {
if (DEBUG) printf("t->children[%u] = NULL;", i);
t->children[i] = NULL;
if (DEBUG) printf(" done\n");
}
}

/// condition de terminaison
if (dist2end == 0) return t->p;

/// vue naïve : permut_group_tree t_next = t->children[a[d]]; -> plus compliqué :
/// l'index relatif (d_index) de l'élément a[d] dans le tableau de successeurs (children)
/// correspond à la position relative de a[d] par rapport à a[d], a[d + 1] ... a[n - 1]
/// si a[d] est le minorant de cet ensemble, d_index vaut zéro
/// en pratique (pas optimisé cf. récursion), on initialise d_index à 0 et on l'augmente de l'unité pour chaque a[j > d] tq a[j] < a[d]

unsigned d_index = 0;
unsigned i = n;
unsigned a_d = a[d];
while (--i > d) if (a[i] < a_d) d_index++;

if (DEBUG) printf("---* d = %u, a[d] = %u, d_index = %u => t_next = t->children[%u]\n", d, a[d], d_index, d_index);
permut_group_tree t_next = t->children[d_index];

if (t_next == NULL)
{
if (DEBUG) printf("---* t_next is NULL : init it \n");
t_next = t->children[a[d_index]] = (permut_group_tree) malloc(sizeof(permut_group_tree_node));
t_next->n = t->n;
t_next->children = NULL;
t_next->p = NULL;
}

return find_or_create_permut(a, t_next, d + 1);
}

/** renvoie la permutation du groupe symétrique d'ordre n donnée par sa clé a **/
permut *get_permut(unsigned *a, unsigned n)
{
if (DEBUG)
{
print_vector("\n\n >>>> get_permut <<<<", a, n);
printf("\n");
}
if (a == NULL || n > max_order) return NULL;

if (permut_groups == NULL) init_permut_groups();

permut_group_tree group_n_tree = permut_groups[n - 1];
if (group_n_tree == NULL)
{
group_n_tree = permut_groups[n - 1] = (permut_group_tree) malloc(sizeof(permut_group_tree_node));
group_n_tree->n = n;
group_n_tree->children = NULL;
group_n_tree->p = NULL;
}

return find_or_create_permut(a, group_n_tree, 0);
}

int main()
{
printf("\n> GROUPE SYMETRIQUE ET PERMUTATIONS\n\n");

printf("\n > Reconnaitre une permutation puis l'instancier (et la stocker)\n\n");

unsigned id[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
get_permut(id, 10);

unsigned a[10] = {0, 2, 1, 5, 3, 4, 9, 8, 7, 6};
get_permut(a, 10);

unsigned b[10] = {0, 6, 4, 7, 9, 8, 2, 1, 5, 3};
get_permut(b, 10);

system("pause");

return 0;
}
0
mamiemando Messages postés 33446 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 20 décembre 2024 7 812
6 déc. 2009 à 20:00
Bon il y plusieurs choses qui ne vont pas :

1) les variables statiques injustifiées (comme tu fais) c'est mal
2) les boucles avec des while décroissant quand un simple for fait l'affaire c'est mal.
3) les mallocs sans faire le free qui va avec c'est très très mal.

Comment je ferais :

1) Les variables statiques que tu utilises devraient être passées en paramètre des différentes fonctions que tu appelles. Elle devraient être déclarées et initialisées dans le main. De manière générale les variables globales et statiques sont à utiliser avec parcimonie (idéalement le moins possible) car elles nuisent fortement à la lisibilité du code.

2) Exemple de flagrant délit : print_vector. Voici une manière bien plus lisible de l'écrire.

/** affiche un vecteur d'entiers positifs **/
void print_vector(char *s, unsigned *v, unsigned n){
    unsigned i;
    printf("%s : { ",s);
    for(i=0;i<n;++i) printf("%u ",v[i]);
    printf("}");
}


3) Pour libérer un arbre tu dois faire un appel récursif et libérer en partant des feuilles. Exemple avec un arbre binaire, sachant que la démarche est quasiment la même avec un arbre n-aire.

typedef struct _node_t{
  struct _node_t *left;  /**< Le fils gauche */
  struct _node_t *right; /**< Le fils droit */
} node_t;

typedef struct _tree_t{
  node_t *root; /**< Le noeud racine */
} tree_t;

void free_subtree(node_t *node){
  free_subtree(node->left);
  free_subtree(node->right);
  free(node);
}

void free_tree(tree_t *tree){
  free_subtree(tree->root);
  free(tree);
}


En arrangeant ces deux trois choses, le programme n'a pas planté chez moi (mais en fait je n'ai pas très bien saisi ce qu'il était sensé faire...).

Bonne chance
-1
Bien lu ta proposition..

Je comprends la motivation de tes remarques 1) et 3) en regard de ta dernière phrase parenthésée.
Elles ne sont malheureusement pas pertinentes : il s'agit d'une structure et d'une fonctionnalité de caching d'instances d'objets mathématiques. J'ai dégrafé les 160 lignes de code du reste, ce morceau apparaît donc hors contexte.

Pour la 2), c'est une question de forme et d'habitude : une boucle for est une construction élaborée sur la base d'un while, et j'encourage mes étudiants à se passer du for pour développer leur sens algorithmique.

Sur le fond, je cherche toujours où se joue l'effet de bord assassin : je penche pour une bêtise évidente, .. mais seulement une fois le nez dessus^^

Merci pour ton temps.
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
mamiemando Messages postés 33446 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 20 décembre 2024 7 812
13 déc. 2009 à 14:31
Hello k, merci pour tes remarques et tes suggestions de lecture. Malheureusement je n'ai pas le K&R dans bibliothèque, il ne semble pas accessible en ligne, et après avoir cherché un peu sur google je n'ai pas trouvé de page qui faisait référence au passage auquel tu fais allusion.

Par ailleurs, je n'ai pas dit que la manière dont il écrit un while était fausse mais juste qu'elle compliquait inutilement le code. Je ne pense pas avoir dit une ânerie à ce niveau là. Après chacun sa manière de coder.

Pour les histoires de boucles while et for en terme de code, effectivement j'ose espérer que de nos jours les compilateurs C n'ont plus ce genre de défaut et même s'ils l'ont, ce sera sans doute imperceptible avec les machines actuelles. Par rapport à la problématique while against for, c'était juste pour dire que non seulement la version while est moins lisible, et qu'en plus autrefois en C elle était moins performante. Après je reconnais que ce que j'ai écrit pouvait porter à confusion.

Merci pour ta contribution, et bonne continuation.
-1