2015-08-17 1 views
-1

Est-ce que ce qui suit est bien défini, pour des valeurs différentes de REF?Déréférencement d'un pointeur hors limite contenant l'adresse d'un objet (tableau de tableau)

#include <stdio.h> 

#define REF 1 
#define S 1 

int main(void) { 
    int a[2][S] = {{1},{2}}; 
    int *q = REF ? a[1] : 0; 
    int *p = a[0] + S; 
    memcpy (&q, &p, sizeof q); 
    printf ("q[0] = %d\n", q[0]); 
    return 0; 
} 

Notez que p des points à l'après le dernier élément de a[0], et non à un élément du tableau a[0], donc pas dereferenceable. Mais l'adresse stockée dans p est l'adresse de a[1][0]. p points sémantiquement (intentionnellement?) "À" (bien, sur) a[0]mais pointe physiquement dans a[1].

Une copie du modèle binaire d'un pointeur peut-elle être sémantiquement dirigée vers un objet lorsque l'original le fait physiquement?

VOIR AUSSI

J'ai demandé essentiellement la même question C/C++ avec un autre "angle":

+0

a n'est pas vraiment un tableau de tableaux, c'est un tableau qui a un accès bidimensionnel. La mémoire dans ce cas sera allouée comme un bloc et sera traitée en interne comme un bloc unidimensionnel. –

+0

@SamiKuhmonen Donc vous dites qu'il n'y a pas de tableau lié, tant que je reste dans le bloc? Donc 'a [0] [1]' va bien? – curiousguy

+0

Je ne dirais pas que c'est bien, car il est encore sémantiquement, mais avec un pointeur brut, vous pouvez accéder à la mémoire consécutivement. Essayer de trouver un auteur doc poêle sur cette allocation –

Répondre

1

Une copie du modèle binaire d'un pointeur peut-elle être sémantiquement dirigée vers un objet lorsque l'original ne le fait que physiquement?

Il n'y a pas une telle distinction efficace, parce que a[0] + S est le même que a[1], en supposant que réseau intérieur est déclarée avec S taille.

Le suivant:

int a[2][S]; 

déclare tableau à deux éléments, où chaque élément est un tableau de S Éléments de Type int. Les tableaux sont stockés de manière contiguë et il n'y a pas de remplissage avant/entre/après ses éléments.

Nous allons prouver que a[0] + S == a[1] tient. Il peut être réécrite comme:

*(a + 0) + S == *(a + 1) 

par l'arithmétique des pointeurs, RHS ajoute 1 * sizeof(*a) octets à a, qui est la même que la taille du tableau interne. LHS est un peu plus complexe, que l'addition est effectuée après déréférencement de a, il ajoute ainsi:

S * sizeof(**a) octets,

Les deux côtés sont garantis être égaux quand ils pointent vers le même objet (la même mémoire emplacement), du même type.Par conséquent, vous pouvez réécrire en « absolue » sous forme d'un octet comme:

(char *)a + S * sizeof(**a) == (char *)a + sizeof(*a) 

Cela réduit en:

S * sizeof(**a) == sizeof(*a) 

Nous savons que sous-ensemble *a a S éléments de type de **a (c.-à- int), donc les deux décalages sont les mêmes. Q.E.D.

1

Compte tenu

int blah(int x, int y) 
{ 
    int a[2][5]; 
    a[1][0] = x; 
    a[0][y] = 9; 
    return a[1][0]; 
} 

rien dans la norme interdirait à un compilateur de recodage que int blah(int x, int y) { return x; }, ni le piégeage (ou faire quoi que ce soit) quand y>=5, depuis a[0] et a[1] sont des tableaux distincts de cinq éléments chacun. Dans les cas où le dernier élément d'une structure à accès indirect est un tableau à un seul élément, les compilateurs ont généralement inclus du code pour permettre à l'arithmétique du pointeur sur ce tableau de générer un pointeur vers le stockage à l'extérieur de la structure. Bien qu'une telle arithmétique de pointeur soit interdite par la norme, elle permet des constructions utiles qui ne pourraient pratiquement pas être implémentées de manière conforme aux normes avant C99.

Notez que l'ajout 5-a[0] produirait un int* qui compare identique à a[1], mais le fait qu'un pointeur « un passé » compare égale à un pointeur qui Identifes l'objet suivant dans la mémoire ne signifie pas qu'il peut être utilisé en toute sécurité pour accéder à ce dernier objet. De tels accès peuvent souvent fonctionner, mais cela ne signifie pas que les compilateurs sont tenus de les faire.

+0

Je comprends que les pointeurs ayant une valeur numérique égale peuvent avoir des valeurs sémantiques distinctes, mais je copie la valeur numérique avec 'memcpy'. Est-ce que la magie 'memcpy'? Peut-il transférer la valeur sémantique? – curiousguy

+0

@curiousguy: La théorie moderne du compilateur semble suggérer que les compilateurs sont libres d'avoir 'memcpy' ou' memmove' qui amène ou non la provenance d'un pointeur comme ils le jugent bon. Le seul type de "scrubbing" que je connaisse de ce memcpy est nécessaire pour résoudre tous les problèmes d'alias strict concernant le contenu copié. Étant donné 'void * p = malloc (sizeof (long)); long * lp = p; court * sp = p; * lp = 1234; memmove (sp, lp, sizeof (court)); printf ("% d", * sp); 'le' memmove' rendrait l'accès à '* sp' bien défini. Sans 'memmove' ce serait Undefined Behavior. Notez que 'memcpy' donnerait UB directement. – supercat