Avatar billede bolmer Nybegynder
11. februar 2006 - 19:05 Der er 23 kommentarer

initialisering af pointer til pointer

Jeg er lidt i tvivl om hvordan man initialisere en pointer til en pointer.

Jeg har denne struct

typedef struct _node_t {
  void *content;
  struct _node_t **kids; // Makes a pointer to a pointer of node_ts.
} node_t;


int main(void)
{
  node_t *np;
  np=malloc(sizeof(node_t));
  np->content = "test";
  np->kids[0] = NULL;

  return 0;
}


Ovenstående giver ikke nogen fejl ved kompilering, men linien:

  np->kids[0] = NULL;

giver en segmentation fault når jeg kører programmet.

Jeg kan løses problemet ved at skrive:

  np->kids=malloc(sizeof(node_t*))

Men jeg kunne godt tænke mig at vide hvorfor:

  np->kids[0] = NULL;

giver et crash når programmet køres.
Avatar billede erikjacobsen Ekspert
11. februar 2006 - 19:27 #1
fordi du ikke har lavet en malloc på kids
Avatar billede bolmer Nybegynder
11. februar 2006 - 19:30 #2
Det er jeg med på men hvorfor??

Det giver fx ikke en segmentation fault når jeg udelader malloc i dette eksempel:

int main(void)
{
  int **ip;
  ip[2] = NULL;
  return 0;
}

Her sætter jeg også en pointer til en pointer til NULL uden at have brugt malloc først, og der kommer ikke nogen segmentation fault. Det gør der derimod i mit oprindelige eksempel.
Avatar billede erikjacobsen Ekspert
11. februar 2006 - 19:42 #3
I begge tilfælde bruger du en uinitialiseret pointer. Muligvis peger den på noget RAM du må pille i, og så får du umiddelbart ingen fejl (men det er en fejl, for du ændrer på noget du ikke ved hvad er), eller også peger den udenfor den RAM du må pille i, og så får du en rigtig fejl med det samme.

Pointere skal pege på noget, før de bruges.
Avatar billede bolmer Nybegynder
11. februar 2006 - 19:45 #4
"må" man så heller ikke lave:

int *ip;
ip = NULL;

og skal man i stedet lave:

int *ip;
ip = malloc(sizeof(int));
ip = NULL;
Avatar billede erikjacobsen Ekspert
11. februar 2006 - 19:46 #5
nt *ip;
ip = NULL;

er helt fint, for du bruger ikke hvad ip peger på. Det andet vil virke, men allokere overflødigt RAM.
Avatar billede erikjacobsen Ekspert
11. februar 2006 - 19:50 #6
Og så lige: hvis du vil bruge kids som et array, så skal du huske at allokere så mange pladser, som du for brug for. Skal du bruge kids[0] til kids[9], så

np->kids=malloc(10*sizeof(node_t*))

Du får næppe umiddelbart en fejl hvis du så bruger kids[10], men du ændrer i noget, der kan overraske dig senere.

Alt dette bøvl kan så nemt laves forkert af en programmør, og kan være svært at opdage ved tests, og svært at rette, at C-programmer ofte er "ustabile". Man gik et skridt videre med Java, hvor det ganske enkelt ikke kan lade sig gøre at lave denne slags fejl, uden at blive gjort opmærksom på det.
Avatar billede bertelbrander Novice
11. februar 2006 - 19:51 #7
Dette må du gerne:
int *ip;
ip = NULL;

Men du må ikke tilgå det ip peger på. dvs:
*ip = 123;

Dette er ikke så smart:
int *ip;
ip = malloc(sizeof(int));
ip = NULL;

Du sætter ip til at pege på dynamisk allokeret plads, og der på til at pege på null, derved har du glemt adressen på det du allokerer, og har ikke vundet noget.

Her sætter du ip til at pege på noget (tre forskellige ting):
int *ip;
ip = malloc(123*sizeof(int));
ip = NULL;
int i;
ip = &i;

Her ændrer du det ip peger på:
*ip = 123;

For at man kan det skal ip pege på noget gyldigt, og ikke NULL.
Avatar billede bolmer Nybegynder
11. februar 2006 - 19:53 #8
ok nu er jeg forvirret. Prøver lige med et lidt andet eksempel:

typedef struct test{
  int x;

}test;

int main(void)
{
  // TILLADT
  test *tp;
  tp = NULL;

  // IKKE-TILLADT
  test **tp;
  tp[0] = NULL;

  return 0;
}


Det vil altså sige at så længe man kun brugere "normale" pointere behøver man ikke malloc. Men når man bruger pointere til pointere så skal man bruge malloc.
Avatar billede erikjacobsen Ekspert
11. februar 2006 - 19:56 #9
Reglen er simpel: en pointer skal pege på noget hvis du bruger den med *p eller p->
Hvis du ikke bruger den på den måde kan du selvfølgelig sætte den til NULL.

Du kan bruge malloc, eller sætte pointeren til at pege på det samme som en anden pointer peger på (som en gang tidligere er lavet med malloc)
Avatar billede erikjacobsen Ekspert
11. februar 2006 - 19:56 #10
Reglen er simpel: en pointer skal pege på noget hvis du bruger den med *p eller p-> eller p[...]
(for lige at blive mere korrekt ... )
Avatar billede bertelbrander Novice
11. februar 2006 - 19:57 #11
For at bruge det som en pointer peger på skal den pege på noget gyldigt.

I det første tilfælde ændrer du det tp peger på

I det andet tilfælde ændrer du det tp indehodler

For at sammenligne de to eksemper skulle det første eksempel være:
  test *tp;
  tp[0] = 123;
Og det er IKKE tilladt.
Avatar billede bolmer Nybegynder
11. februar 2006 - 20:02 #12
men tp[0] indeholder netop en pointer hvis jeg erklærer tp som **tp.

så tp[0] = NULL burde fungerer da tp[0] er en pointer.


Men som erik skriver så skal den altså først initialiseres når jeg bruger "[]"
Avatar billede erikjacobsen Ekspert
11. februar 2006 - 20:05 #13
test **tp;
  tp[0] = NULL;

tp peger ikke på noget lovligt. Fejl (måske får du det at vide)

test **tp;
tp = malloc(......);
tp[0] = NULL;

Ok.
Avatar billede bolmer Nybegynder
11. februar 2006 - 20:08 #14
Jep jeg tror den er ved at sive ind. Lige en detalje du skriver at når man anvender "[]" så skal den pege på noget lovligt.

Laver jeg:

int **tp;
tp = NULL

Så er det vel også ulovligt idet tp indforstået svarer til tp[0].
Avatar billede erikjacobsen Ekspert
11. februar 2006 - 20:13 #15
Nej tp er bare pointeren. Den kan du sætte til null. tp[0] er det første pointeren peger på, så det skal være lovligt.
Avatar billede bolmer Nybegynder
11. februar 2006 - 20:15 #16
ok det jeg tænker på er vist: tp=&tp[0]  der selvfølgelig ikke er det samme som tp=tp[0] som svarer til indholdet og ikke adressen.
Avatar billede bolmer Nybegynder
11. februar 2006 - 20:30 #17
...Lige en mystisk ting.

typedef struct test{
  int x;

}test;

int main(void)
{
  // test1
  int *a;
  a[0]=1;

  // test2
  test *tp;
  tp = NULL;

  return 0;
}

Dette giver en segmentation fault. Det mærkelige er at hvis jeg bare udkommentere enten test 1 eller test2 så forsvinder den (lige meget hvilken bare så længe én af dem er udkommenteret)!

Dvs at jeg i teorien ikke kan vide om det er test1 eller test2 der skyld i fejlen.

På baggrund at dette indlæg må det være test1 der er årsag til fejlen. Men hvis jeg bare udkommentere test2 så er alt fint. Det er skisme sort!
Avatar billede erikjacobsen Ekspert
11. februar 2006 - 20:35 #18
Jeg ser ingen malloc i test1. Da den så peger på noget udefineret kan det være indenfor lovlig RAM hvis test2 ikke er med. Det er sådan set fuldstændig ligemeget, for du har en uinitialiseret pointer.
Avatar billede erikjacobsen Ekspert
11. februar 2006 - 20:35 #19
....kan du se hvorfor de opfandt Java ?  ;)
Avatar billede bolmer Nybegynder
11. februar 2006 - 20:40 #20
Jo tak! Hvorfor ikke give C en facelift?

Er godt klar over at "a" i test1 ikke peger et gyldigt sted (mangler malloc) men det er netop det som er ideen for at fremprovokere fejlen. Det er bare pænt sort at den kun opstår hvis jeg samtidig laver test2. C virker som IT verdens svar på bungie jump med varierende elastik længde.
Avatar billede erikjacobsen Ekspert
11. februar 2006 - 20:43 #21
Java er sikkert (hvad det her angår i hvert fald), C er hurtigt, for selvfølgelig koster det ekstra at tjekke for alting.

Og Java er simpelthen C og specielt C++'s facelift. Du har blot genopdaget grunden til at man ikke skal stole på C-programmer.
Avatar billede erikjacobsen Ekspert
11. februar 2006 - 20:51 #22
Ok, retfærdigvis: der er forskellige tools til C og C++, der forsøger at rette op på dette. Prøv at google efter "safe pointers".
Avatar billede bertelbrander Novice
11. februar 2006 - 23:50 #23
I C++ kan man bruge std containere (vector, list, map, string, etc) til at løse mange af de problemer med pointere og dynamisk hukommelses allokering der er i C

Dermed er C++ et facelift til C.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester