Question

Cela va sembler une question idiote, mais je suis toujours en train d’apprendre le C, alors je vous en prie, supportez-moi. :)

Je travaille sur le chapitre 6 de K & R (structs) et jusqu’à présent, le livre a connu un franc succès. J'ai décidé de travailler assez lourdement avec les structures, et j'ai donc beaucoup travaillé au début du chapitre avec les exemples point et rect. Une des choses que je voulais essayer était de changer la fonction canonrect (2e édition, p. 131) via des pointeurs, et donc de renvoyer void .

Je travaille bien, mais j'ai eu un hoquet que j'espérais que vous pourriez m'aider. Je voulais que canonRect crée un objet rectangle temporaire, effectue ses modifications, puis réaffecte le pointeur transféré au rectangle temporaire, simplifiant ainsi le code.

Cependant, si je le fais, le rect ne change pas. Au lieu de cela, je me retrouve à repeupler manuellement les champs du dossier dans lequel je suis passé, ce qui fonctionne.

Le code suit:

#include <stdio.h>

#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))

struct point {
    int x;
    int y;
};

struct rect {
    struct point lowerLeft;
    struct point upperRight;
};

// canonicalize coordinates of rectangle
void canonRect(struct rect *r);

int main(void) {
    struct point p1, p2;
    struct rect r;

    p1.x = 10;
    p1.y = 10;
    p2.x = 20;
    p2.y = 40;
    r.lowerLeft = p2; // note that I'm inverting my points intentionally
    r.upperRight = p1;

    printf("Rectangle, lower left: %d, %d; upper right: %d %d\n\n", 
        r.lowerLeft.x, r.lowerLeft.y, r.upperRight.x, r.upperRight.y);

    // can't pass a pointer, only a reference. 
    // (Passing pointers results in illegal indirection compile time errors)
    canonRect(&r); 
    printf("Rectangle, lower left: %d, %d; upper right: %d %d\n\n", 
        r.lowerLeft.x, r.lowerLeft.y, r.upperRight.x, r.upperRight.y);    
}

void canonRect(struct rect *r) {
    struct rect temp;
    temp.lowerLeft.x = min(r->lowerLeft.x, r->upperRight.x);
    temp.lowerLeft.y = min(r->lowerLeft.y, r->upperRight.y);
    temp.upperRight.x = max(r->lowerLeft.x, r->upperRight.x);
    temp.upperRight.y = max(r->lowerLeft.y, r->upperRight.y);

    r = &temp; // doesn't work; my passed-in rect remains the same

    // I wind up doing the following instead, to reassign all 
    // the members of my passed-in rect
    //r->lowerLeft = temp.lowerLeft;
    //r->upperRight = temp.upperRight;
}

Alors voici les questions:

  1. Pourquoi r = & amp; temp; ne fonctionne-t-il pas? (Je pense que c’est parce que je passe une référence à la place d’un pointeur; ai-je raison de penser que les références ne sont pas modifiables mais que les pointeurs le sont?)
  2. Pourquoi est-ce que je pourrais obtenir une erreur de compilation indirectionnelle illégale si je tente de passer un pointeur sur canonRect ? (IE, si j'avais canonRect (* r); dans main () .)

Je suppose que je connais déjà la réponse à la question n ° 1, mais la position n ° 2 me laisse perplexe. Je pensais qu'il était légal de faire passer des pointeurs.

Quoi qu'il en soit ... s'il vous plaît pardonnez le C newb.

Était-ce utile?

La solution

Je pense que ce que vous voulez faire est le suivant:

void canonRect(struct rect *r) {
    struct rect temp;
    temp.lowerLeft.x = min(r->lowerLeft.x, r->upperRight.x);
    temp.lowerLeft.y = min(r->lowerLeft.y, r->upperRight.y);
    temp.upperRight.x = max(r->lowerLeft.x, r->upperRight.x);
    temp.upperRight.y = max(r->lowerLeft.y, r->upperRight.y);

    *r = temp; 
}

Dans le code ci-dessus, vous définissez * r qui est de type rect à temp qui est de type rect.

Re 1: Si vous voulez changer ce que r pointe vers vous, vous devez utiliser un pointeur sur un pointeur. Si c'est vraiment ce que vous voulez (voir ci-dessus, ce n'est pas vraiment ce que vous voulez), vous devez alors vous assurer de le pointer vers quelque chose sur le tas. Si vous le dirigez vers quelque chose qui n'a pas été créé avec 'new' ou malloc, il tombera hors de portée et vous indiquerez une mémoire qui n'est plus utilisée pour cette variable.

Pourquoi votre code ne fonctionne-t-il pas avec r = & amp; temp?

Parce que r est de type rect *. Cela signifie que r est une variable qui contient une adresse mémoire dont la mémoire contient un rect. Si vous modifiez ce que r pointe, c'est correct, mais cela ne modifie pas la variable passée.

Re 2: * lorsqu'il n'est pas utilisé dans une déclaration de type, est l'opérateur unaire de déréférence. Cela signifie qu'il recherchera ce qui se trouve à l'intérieur de l'adresse du pointeur. Donc, en passant * r, vous ne passez pas du tout un pointeur. En face, puisque r n'est pas un pointeur, c'est une syntaxe non valide.

Autres conseils

Cela vaut également la peine de noter que votre variable 'struct rec temp' va sortir de sa portée dès que cette méthode canonRect se termine et que vous pointerez sur une mémoire invalide.

On dirait que vous confondez l'opérateur 'déréférencement' ( * ) avec l'opérateur 'adresse de' ( & amp; ).

Lorsque vous écrivez & amp; r , cela donne l'adresse de r et renvoie un pointeur sur r (un pointeur est simplement une adresse mémoire d'une variable). Donc, vous passez vraiment un pointeur sur la fonction.

Lorsque vous écrivez * r , vous essayez de déréférencer r. Si r est un pointeur, cela retournera la valeur que r pointe vers. Mais r n’est pas un pointeur, mais un rectangle. Vous obtiendrez donc une erreur.

Pour rendre les choses plus confuses, le caractère * est également utilisé lors de la déclaration de variables de pointeur. Dans cette déclaration de fonction:

void canonRect(struct rect *r) {

r est déclaré être un pointeur sur un struct rect . C'est complètement différent d'utiliser * comme ceci:

canonRect(*r); 

Dans les deux cas, le caractère * signifie quelque chose de complètement différent.

Tout d’abord, K & R c n’a pas de concept de "références", mais uniquement de pointeurs. L'opérateur & amp; signifie "prendre l'adresse de".

Deuxièmement, le r dans cannonRect () est une variable locale et n'est pas le r dans main () . Changer le point local r n'affecte pas le r de la routine appelante.

Enfin, comme indiqué précédemment, le struct rect local est alloué sur la pile et sort du champ d'application à l'accolade voisine,

Vous voudrez peut-être lire les différentes manières dont les paramètres peuvent (conceptuellement) être passés aux fonctions. C est valeur d'appel par la valeur , donc lorsque vous passez le pointeur sur un recto dans la fonction que vous transmettez une copie du pointeur. Toute modification apportée par la fonction à la valeur r directement (et non indirectement) ne sera pas visible pour l'appelant.

Si vous voulez que la fonction fournisse une nouvelle structure à l'appelant, vous pouvez le faire de deux manières:   1. vous pouvez retourner un rect:   2. vous pouvez passer un pointeur à un pointeur à un rect dans:

La première solution serait plus naturelle:

struct rect* canonRect(struct rect* r)
{
  struct rect* cr = (struct rect*) malloc(sizeof(struct rect));
  ...
  return cr;
}

La deuxième façon serait:

void canonRect(struct rect** r)
{
  *r = (struct rect*) malloc(sizeof(struct rect));
}

et l'appelant utiliserait alors:

   canonRect(&r);

Mais l'appelant perd le pointeur d'origine qu'il possédait et vous devez faire attention à ne pas laisser filtrer les structures.

Quelle que soit la technique utilisée, la fonction devra allouer de la mémoire pour la nouvelle structure sur le tas à l'aide de malloc. Vous ne pouvez pas allouer de l’espace sur la pile en déclarant simplement une structure car cette mémoire devient invalide lorsque la fonction retourne.

  

2.Pourquoi puis-je obtenir une erreur de compilation indirectionnelle illégale si j'essaie de transmettre un pointeur à canonRect? (IE si j'avais canonRect (* r); dans main ().)

Parce que ce n'est pas le but du pointeur .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top