47 votes

Est-il bien défini d'utiliser un pointeur pointant vers un-past-malloc?

En C, il est parfaitement bien de faire un pointeur qui pointe vers un au-delà du dernier élément d'un tableau et de l'utiliser dans l'arithmétique de pointeur, aussi longtemps que vous n'avez pas déréférencement d'elle:

int a[5], *p = a+5, diff = p-a; // Well-defined

Cependant, ces UBs:

p = a+6;
int b = *(a+5), diff = p-a; // Dereferencing and pointer arithmetic

Maintenant, j'ai une question: est-ce que la mémoire allouée dynamiquement? Supposons que je suis le seul à l'aide d'un pointeur pointant vers un passé-le-dernier dans l'arithmétique de pointeur, sans déférence, et malloc() réussit.

int *a = malloc(5 * sizeof(*a));
assert(a != NULL, "Memory allocation failed");
// Question:
int *p = a+5;
int diff = p-a; // Use in pointer arithmetic?

26voto

Serge Ballesta Points 12850

Le projet de n4296 pour le C11 est explicite que pointer un passé, un tableau est perfecly définis: 6.5.6 Langue / Expressions / Additif pour les opérateurs:

§ 8 Quand une expression de type entier est ajoutée ou déduite à partir d'un pointeur, l' résultat est du type du pointeur de l'opérande. ... D'ailleurs, si l'expression de P points à la dernière élément d'un tableau d'objet, l'expression (P)+1 points dans un passé le dernier élément de la objet array, et si l'expression de Q points l'un après le dernier élément d'un tableau d'objet, l'expression (Q)-1 points pour le dernier élément de l'objet de tableau... Si le résultat des points l'un après le dernier élément de l'objet tableau, il ne doit pas être utilisé comme opérande de unaire * l'opérateur qui est évaluée.

Comme le type de la mémoire n'est jamais précisé dans l'article, il s'applique à tout type de mémoire, y compris alloué à un.

Qui signifie clairement qu'après:

int *a = malloc(5 * sizeof(*a));
assert(a != NULL, "Memory allocation failed");

les deux

int *p = a+5;
int diff = p-a;

sont parfaitement définies et que l'habitude de l'arithmétique des pointeurs règles s'appliquent, diff doit recevoir la valeur 5.

23voto

haccks Points 33022

Est-il bien défini pour utiliser un pointeur pointant vers un passé-malloc?

Il est bien défini si p vers un passé, la mémoire allouée et il n'est pas déréférencé.

n1570 - §6.5.6 (p8):

[...] Si le résultat des points l'un après le dernier élément de l'objet tableau, il ne doit pas être utilisé comme opérande de unaire * de l'opérateur qui est évaluée.

Soustraction de deux pointeurs ne sont valables que lorsqu'ils désignent des éléments d'un même objet array ou un au-delà du dernier élément du tableau d'objet, sinon il en résulte un comportement indéfini.

(p9):

Lorsque les deux pointeurs sont soustraits, à la fois doit pointer sur le même objet de tableau, ou un passé le dernier élément de l'objet array [...]

Les citations ci-dessus sont également applicables pour les deux de façon dynamique et statique de la mémoire allouée.

int a[5];
ptrdiff_t diff = &a[5] - &a[0]; // Well-defined

int *d = malloc(5 * sizeof(*d));
assert(d != NULL, "Memory allocation failed");
diff = &d[5] - &d[0];        // Well-defined

Une autre raison pour que cela soit valable pour la mémoire allouée dynamiquement, comme indiqué par Jonathan Leffler dans un commentaire est:

§7.22.3 (p1):

L'ordre et la contiguïté de stockage alloué par les appels successifs à l' aligned_alloc, calloc, malloc, et realloc fonctions n'est pas spécifié. Le pointeur retourné si la répartition réussit est correctement alignés de sorte qu'il peut être attribué à un pointeur vers n'importe quel type d'objet avec un fondamental de l'alignement de l'exigence et ensuite utilisé pour accéder à un tel objet ou un tableau de ces objets dans l'espace alloué (jusqu'à ce que l'espace est explicitement libéré).

Le pointeur retourné par malloc dans l'extrait ci-dessus est attribué d et la mémoire allouée est un tableau de 5 int objets.

7voto

Bathsheba Points 23209

Oui, les mêmes règles s'appliquent aux variables dynamique et automatique de la durée de stockage. Il s'applique même à un malloc demande d'un seul élément (un scalaire est équivalent à un élément de tableau à cet égard).

L'arithmétique des pointeurs n'est valable que dans des tableaux, dont un passé la fin d'un tableau.

Sur un déréférencement, il est important de noter une contrepartie: en ce qui concerne l'initialisation int a[5] = {0};, le compilateur ne doit pas tenter de déréférencer a[5] dans l'expression int* p = &a[5]; il doit compiler ce qu' int* p = a + 5; Nouveau, la même chose s'applique à la dynamique de stockage.

7voto

chux Points 13185

Est-il bien défini pour utiliser un pointeur pointant vers un passé-malloc?

Oui, encore un coin à l'existence où ce n'est pas bien définie:

void foo(size_t n) {
  int *a = malloc(n * sizeof *a);
  assert(a != NULL || n == 0, "Memory allocation failed");
  int *p = a+n;
  intptr_t diff = p-a;
  ...
}

Fonctions de gestion de mémoire ... Si la taille de l'espace requis est égal à zéro, le comportement de la mise en œuvre est définie par: soit un pointeur null est retourné, ou le comportement est comme si la taille avait quelques valeur différente de zéro, sauf que le pointeur retourné ne doit pas être utilisé pour accéder à un objet. C11dr §7.22.3 1

foo(0) --> malloc(0) peut renvoyer à un NULL ou non-NULL. Dans la première mise en œuvre d'un retour d' NULL n'est pas un "échec d'allocation de Mémoire". Cela signifie que le code est tentant int *p = NULL + 0; avec int *p = a+n; qui ne satisfait pas les garanties sur pointeur de mathématiques, ou, au moins, apporte un tel code en question.

Portable code avantages en évitant 0 taille allocations.

void bar(size_t n) {
  intptr_t diff;
  int *a;
  int *p;
  if (n > 0) {
    a = malloc(n * sizeof *a);
    assert(a != NULL, "Memory allocation failed");
    p = a+n;
    diff = p-a;
  } else {
    a = p = NULL;
    diff = 0;
  }
  ...
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X