2
votes

Défaut de segmentation C malloc utilisant un tableau à 1 dimension

J'utilise malloc pour créer un tableau en C. Mais j'ai eu l'erreur de segmentation lorsque j'ai essayé d'assigner des valeurs aléatoires au tableau en 2 boucles.

Il n'y a pas d'erreur de segmentation lorsque j'attribue des valeurs à ce tableau en 1 boucle. La taille du tableau est grande. Veuillez consulter le code que j'ai joint. N'importe qui peut me donner une idée de ce qui se passe ici. Je suis assez nouveau à C. Merci beaucoup à l'avance.

int n=50000;
float *x = malloc(n*n*sizeof(float));

// there is segmentation fault:
int i, j;
for (i=0; i<n; i++){
   for (j=0; j<n; j++){
       x[i*n+j] = random() / (float)RAND_MAX;
    }
}
// there is no segmentation fault:
int ii;
for (ii=0; ii<n*n; ii++){
        x[ii] = random() / (float)RAND_MAX;
}


0 commentaires

3 Réponses :


5
votes

int débordement.

50000 * 50000 -> 2,500,000,000 -> plus de INT_MAX -> comportement non défini (UB ).

Tout d'abord, assurons-nous qu'un calcul de la taille de cette allocation est possible

// float *x = malloc(n*n*sizeof(float));

// Uses at least `size_t` math by leading the multiplication with that type.
float *x = malloc(sizeof(float) * n*n);
// or better
float *x = malloc(sizeof *x * n*n); 

for (i=0; i<n; i++){
  for (j=0; j<n; j++){
    x[(size_t)n*i + j] = random() / (float)RAND_MAX;
  }
}

Puis avec une largeur suffisamment vérifiée size_t , utilisez size_t math pour faire la multiplication et utilisez size_t math pour le calcul d'index de tableau. Plutôt que int * int * size_t , faites size_t*int*int.

assert(SIZE__MAX/n/n/sizeof(float) >= 1);

2ème boucle pas "échouer" car n * n n'est pas la grande valeur attendue, mais probablement la même valeur UB dans l'allocation.


1 commentaires

Merci! J'ai beaucoup appris. Le problème est résolu lorsque j'utilise size_t pour le calcul d'index x [(size_t) n * i + j]. Je finis par définir n comme size_t au début.



3
votes

Tout d'abord, vous invoquez un comportement indéfini en raison d'un dépassement d'entier signé. En supposant qu'un int est de 32 bits, la valeur de 50000 * 50000 dépasse la plage d'un int , provoquant le débordement.

Vous pouvez résoudre ce problème en mettant sizeof (float) en premier dans l'expression. Le résultat de sizeof est un size_t qui est non signé et au moins aussi grand qu'un int . Ensuite, lorsque chaque n est multiplié, il est d'abord converti en size_t évitant ainsi le débordement.

float *x = malloc(sizeof(float)*n*n);

Cependant, même si vous corrigez cela, vous demandez trop de mémoire.

En supposant que sizeof (float) est de 4 octets, n * n * sizeof (float) représente environ 10 Go de mémoire. Si vous vérifiez la valeur de retour de malloc , vous verrez probablement qu'elle renvoie NULL.

Vous devrez rendre votre tableau beaucoup plus petit. Essayez plutôt n = 1000 , qui n'utilisera qu'environ 4 Mo.


0 commentaires

1
votes

Je pense que le problème est lié au dépassement d'entiers:

50 000 x 50 000 = 2,5 milliards

2 ^ 31 ~ 2,1 milliards

Ainsi, vous invoquez un comportement non défini lors du calcul de l'index du tableau. Quant à savoir pourquoi cela fonctionne pour l'un mais pas pour l'autre, c'est juste comme ça. Un comportement non défini signifie que le compilateur (et l'ordinateur) peut faire tout ce qu'il veut, y compris faire ce que vous attendez et planter le programme.

Pour corriger, changez les types de i, j, n et ii en long long à partir de int. Cela devrait résoudre le problème de débordement et l'erreur de segmentation.

Modifier:

Vous devez également vérifier que malloc renvoie un pointeur valide avant d'effectuer des opérations sur le pointeur. Si malloc échoue, vous recevrez un pointeur nul.


0 commentaires