Domanda

Sembrerà una domanda sciocca, ma sto ancora imparando C, quindi per favore abbi pazienza. :)

Sto lavorando al capitolo 6 di K & amp; R (struct), e finora il libro ha visto un grande successo. Ho deciso di lavorare con le strutture piuttosto pesantemente, e quindi ho lavorato molto all'inizio del capitolo con gli esempi puntuali e corretti. Una delle cose che volevo provare era cambiare la funzione canonrect (2nd Edition, p 131) tramite puntatori, e quindi restituire void .

Ho funzionato, ma ho avuto un singhiozzo con cui speravo che potreste aiutarmi. Volevo canonRect per creare un oggetto rettangolo temporaneo, eseguire le sue modifiche, quindi riassegnare il puntatore che è passato al rettangolo temporaneo, semplificando così il codice.

Tuttavia, se lo faccio, il rettangolo non cambia. Invece, mi ritrovo a ripopolare manualmente i campi del rettangolo in cui sono passato, che funziona.

Il codice è il seguente:

#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;
}

Quindi, ecco le domande:

  1. Perché r = & amp; temp; non funziona? ( penso questo perché passo un riferimento anziché un puntatore; ho ragione nel pensare che i riferimenti non sono modificabili ma i puntatori lo sono?)
  2. Perché è possibile che venga visualizzato un errore di compilazione indiretta illegale se provo a passare un puntatore a canonRect ? (IE, se avessi canonRect (* r); in main () .)

Sospetto di conoscere già la risposta al n. 1, ma il n. 2 mi sconcerta: ho pensato che fosse legale passare i puntatori in giro.

Comunque ... ti prego di perdonare il newb C.

È stato utile?

Soluzione

Penso che ciò che vuoi fare sia questo:

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; 
}

Nel codice sopra si imposta * r che è di tipo rect su temp che è di tipo rect.

Ri 1: Se vuoi cambiare ciò che r punta a, devi usare un puntatore a un puntatore. Se è davvero quello che vuoi (vedi sopra, non è proprio quello che vuoi), allora dovresti assicurarti di indicarlo a qualcosa sul mucchio. Se lo punti a qualcosa che non è stato creato con 'nuovo' o malloc, cadrà fuori dall'ambito e indicherai la memoria che non viene più utilizzata per quella variabile.

Perché il tuo codice non funziona con r = & amp; temp?

Perché r è di tipo rect *. Ciò significa che r è una variabile che contiene un indirizzo di memoria la cui memoria contiene un retto. Se cambi ciò a cui punta r, va bene ma ciò non cambia la variabile passata.

Ri 2: * quando non utilizzato in una dichiarazione di tipo è l'operatore unario di differenza. Ciò significa che cercherà ciò che si trova all'interno dell'indirizzo del puntatore. Quindi passando * r non si passa affatto un puntatore. In faccia poiché r non è un puntatore, questa è una sintassi non valida.

Altri suggerimenti

Vale anche la pena notare che la variabile 'struct rec temp' andrà al di fuori dell'ambito non appena termina quel metodo canonRect e si punta a memoria non valida.

Sembra che tu stia confondendo l'operatore "dereference" ( * ) con l'operatore "address of" ( & amp; ).

Quando scrivi & amp; r , questo ottiene l'indirizzo di r e restituisce un puntatore a r (un puntatore è solo un indirizzo di memoria di una variabile). Quindi stai davvero passando un puntatore alla funzione.

Quando scrivi * r , stai provando a dereference r. Se r è un puntatore, verrà restituito il valore a cui punta r. Ma r non è un puntatore, è un rettangolo, quindi otterrai un errore.

Per rendere le cose più confuse, il carattere * viene utilizzato anche per dichiarare le variabili del puntatore. In questa dichiarazione di funzione:

void canonRect(struct rect *r) {

r è dichiarato puntatore a struct rect . Questo è completamente diverso dall'uso di * in questo modo:

canonRect(*r); 

In entrambi i casi, il carattere * significa qualcosa di completamente diverso.

Innanzitutto, K & amp; R c non ha un concetto di "riferimenti", solo puntatori. L'operatore & amp; indica " prendi l'indirizzo di " ;.


In secondo luogo, il r in cannonRect () è una variabile locale ed è non il r in main () . La modifica della posizione dei punti r locali non influisce sul r nella routine di chiamata.


Infine, come già notato, il struct rect locale viene allocato nello stack e non rientra nell'ambito della parentesi chiusa,

Potresti voler leggere i diversi modi in cui i parametri possono (concettualmente) passare alle funzioni. C è call-by-value , quindi quando passi il puntatore a un rect in la funzione che stai passando una copia del puntatore. Qualsiasi modifica apportata dalla funzione al valore r direttamente (non indirettamente) non sarà visibile al chiamante.

Se vuoi che la funzione fornisca al chiamante una nuova struttura, allora ci sono due modi per farlo:   1. puoi restituire un retto:   2. puoi passare un puntatore a un puntatore a un rettangolo in:

Il primo modo sarebbe più naturale:

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

Il secondo modo sarebbe:

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

e il chiamante userebbe quindi:

   canonRect(&r);

Ma il chiamante perde il puntatore originale che aveva e dovresti stare attento a non perdere le strutture.

Qualunque sia la tecnica utilizzata, la funzione dovrà allocare memoria per la nuova struttura sull'heap usando malloc. Non è possibile allocare lo spazio nello stack dichiarando semplicemente una struttura perché quella memoria diventa non valida quando viene restituita la funzione.

  

2.Perché potrei ottenere un errore di compilazione indiretto illegale se provo a passare un puntatore a canonRect? (IE, se avessi canonRect (* r); in main ().)

Perché, non è lo scopo di puntatore .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top