Passerer pekere / referanser til structs inn funksjoner

stemmer
12

Dette kommer til å høres ut som et dumt spørsmål, men jeg er fortsatt læring C, så kan du bære med meg. :)

Jeg jobber med kapittel 6 i K & R (structs), og så langt gjennom boken har sett stor suksess. Jeg bestemte meg for å jobbe med structs ganske tungt, og derfor gjorde mye arbeid tidlig i kapitlet med poenget og rect eksempler. En av de tingene jeg ønsket å prøve var å endre canonrectfunksjon (2. utgave, s 131) jobbe via pekere, og dermed returnere void.

Jeg har dette fungerer, men kjørte inn i en hikke jeg håpet dere kunne hjelpe meg med. Jeg ønsket canonRectå skape en midlertidig rektangel objekt, utføre sine endringer, deretter tilordne pekeren det er gått til det midlertidige rektangel, og dermed forenkle koden.

Men hvis jeg gjør det, gjør rect endres ikke. I stedet finner jeg meg selv manuelt repopulating feltene i rect jeg gikk inn, noe som gjør arbeidet.

Koden følger:

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

Så her er spørsmålene:

  1. Hvorfor r = &temp;ikke? (Jeg tror dette er fordi jeg passere i en referanse i stedet for en peker, har jeg rett i å tenke at referansene ikke er modifiserbare, men pekere er?)
  2. Hvorfor kan jeg få en ulovlig indirekte kompilering-time error hvis jeg prøver å passere i en peker til canonRect? (IE, hvis jeg hadde canonRect(*r);i main().)

Jeg mistenker at jeg allerede vet svaret på # 1, men # 2 perplexes meg - jeg trodde det var lovlig å passere pekere rundt.

Uansett ... tilgi C newb.

Publisert på 01/11/2008 klokken 21:38
kilden bruker
På andre språk...                            


6 svar

stemmer
18

Jeg tror det du ønsker å gjøre er dette:

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

I koden ovenfor er du setter * r som er av typen rect til temp som er av typen rect.

Re 1: Hvis du ønsker å endre hva r peker du trenger å bruke en peker til en peker. Hvis det er virkelig hva du ønsker (se ovenfor, det er egentlig ikke hva du vil) så du må sørge for å peke det til noe på haugen. Hvis du peker den til noe som ikke er laget med 'nye' eller malloc så vil det falle ut av omfanget og du vil bli peker til minne som ikke lenger brukes for denne variabelen.

Hvorfor fungerer ikke koden arbeid med r = & temp?

På grunn r er av rect * type. Det betyr at r er en variabel som holder en minneadresse som er minnet inneholder en rect. Hvis du endrer hva r peker på, er det helt greit, men det endrer ikke vedtatt i variabel.

Re 2: * når den ikke brukes i en type erklæringen er dereferanse ensartet operatør. Dette betyr at det vil slå opp hva som er inne i adressen til pekeren. Så ved å sende * r du ikke passerer en peker i det hele tatt. I ansiktet fordi R ikke er en peker, som er ugyldig syntaks.

Svarte 01/11/2008 kl. 21:41
kilden bruker

stemmer
12

Det er også verdt å merke seg variabelen 'konstruere rec temp' kommer til å gå ut av omfang så snart som metode canonRect slutter og du vil peke til ugyldige minne.

Svarte 01/11/2008 kl. 21:43
kilden bruker

stemmer
4

Det høres ut som du er forvirrende 'dereferanse' operator ( *) med 'adressen' operator ( &).

Når du skriver &r, som får adressen til r og returnerer en peker til r (en peker er bare et minne adressen til en variabel). Så du virkelig passerer en peker inn i funksjonen.

Når du skriver *r, er du prøver å dereference r. Hvis R er en peker, som gir den verdi som r peker til. Men r er ikke en peker, det er en rect, slik at du får en feilmelding.

For å gjøre ting mer forvirrende, den *er tegnet også brukes når erklære pekervariabler. I denne funksjonen erklæring:

void canonRect(struct rect *r) {

rer erklært å være en peker til en struct rect. Dette er helt forskjellig fra å bruke *som dette:

canonRect(*r); 

I begge tilfeller betyr tegnet * noe helt annet.

Svarte 01/11/2008 kl. 21:53
kilden bruker

stemmer
2

Først gjør K & R c ikke har et konsept med "referanser", bare pekere. Den &operatør betyr "ta adressen til".


Dernest, ri cannonRect()en lokal variabel, og er ikke det ri main(). Endre hvor de lokale rpunktene ikke påvirke ri kallet rutine.


Til slutt, som allerede nevnt den lokale struct rectfordeles på stakken, og går ut-av-ramme ved klammeparentes,

Svarte 01/11/2008 kl. 21:56
kilden bruker

stemmer
2

Du ønsker kanskje å lese opp på forskjellige måter parametrene kan (konseptuelt) skal sendes til funksjoner. C er ringe-by-verdi , så når du passerer peker til en rect inn i funksjonen du passerer en kopi av pekeren. Eventuelle endringer funksjonen gjør til verdien r direkte (ikke indirekte) vil ikke være synlig for den som ringer.

Hvis du vil at funksjonen skal gi den som ringer med en ny struct så er det to måter å gjøre det: 1. De kan sende rect: 2. du kan sende en peker til en peker til en rect i:

Den første måten ville være mer naturlig:

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

Den andre måten ville være:

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

og den som ringer vil da bruke:

   canonRect(&r);

Men den som ringer mister opprinnelige peker det hadde, og du må være forsiktig med å lekke structs.

Uansett hvilken teknikk du bruker, vil funksjonen må tildele minne for den nye struct på haugen bruke malloc. Du kan ikke tildele plass på stakken ved bare å erklære en struct fordi det minnet blir ugyldig når funksjonen returnerer.

Svarte 01/11/2008 kl. 21:54
kilden bruker

stemmer
-1

2.Why kan jeg få en ulovlig indirekte kompilering-time error hvis jeg prøver å passere i en peker til canonRect? (IE, hvis jeg hadde canonRect (* r),. I main ())

Fordi, det er ikke hensikten med pekeren .

Svarte 04/11/2008 kl. 14:13
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more