Ongelma jota en vain saa omin enkä kaverin avuin ratkastua: Kun käännän alla olevan koodin (Ubuntu ja gcc), niin kääntäjä ei herjaa mitään. Kuitenkin, seuraavanlainen virheilmoitus tulee esille ja ohjelma kaatuu:
*** glibc detected *** /home/aatu/workspace/library_debug/Debug/library_debug: double free or corruption (fasttop): 0x094ab008 ***
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6[0x65bff1]
/lib/tls/i686/cmov/libc.so.6[0x65d6f2]
/lib/tls/i686/cmov/libc.so.6[0x6613b1]
/lib/tls/i686/cmov/libc.so.6(realloc+0xdd)[0x66182d]
/home/aatu/workspace/library_debug/Debug/library_debug[0x80488af]
/home/aatu/workspace/library_debug/Debug/library_debug[0x804870f]
/home/aatu/workspace/library_debug/Debug/library_debug[0x80486bd]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0x607b56]
/home/aatu/workspace/library_debug/Debug/library_debug[0x8048581]
======= Memory map: ========
Tämä valitusvirsi jatkuu vielä litanialla kaikkea tekstiä.
Virheilmoituksen mukaan ongelma olisi realloc:in kanssa. Kuitenkin kaatumista ei tule, jos alempi malloc poistetaan koodista. Koodin on osa isompaa ohjelmaa,ja sen tarkoitus on vain selvittää, mistä yllä oleva virhe johtuu.
Mikä ihme tässä mättää/on väärin?
Tässäpä koodi:
#include <stdio.h> #include <errno.h> #include <stdlib.h> #include <string.h> // Globaalit struktuurit ja muuttujat typedef enum book_genre { romance=0,thriller,scifi,horror,general } GENRE; typedef enum book_status { loaned=0,available,storage,missing,reserved,removed } BOOKSTATUS; typedef enum customer_status { active,inactive,banned } CUSTOMERSTATUS; typedef struct book { int IDnumber; int IDcustomerNumber; char name[20]; char author[20]; GENRE myGenre; BOOKSTATUS myBookStatus; } BOOK; typedef struct customer { int IDnumber; char firstName[20]; char familyName[20]; char email[20]; CUSTOMERSTATUS myCustomerStatus; } CUSTOMER; void AddNewBook(BOOK []); int ReallocBook(BOOK []); int bookIdx=0; int main(void) { int exitProg=0; BOOK *myBook=NULL; CUSTOMER *myCustomer=NULL; myBook=(BOOK *)malloc(sizeof(BOOK)); if (myBook == NULL) { printf("Dynamic memory allocation: %s\n\n", strerror(errno)); return 1; } /* Kun alla olevan rivin kommentoi pois, niin ohjelma ei kaadu. Ohjelman * kannalta rivi on silti välttämätön. */ myCustomer=(CUSTOMER *)malloc(sizeof(CUSTOMER)); if (myCustomer==NULL) { printf("Dynamic memory allocation: %s\n\n", strerror(errno)); return 1; } /* Tätä luuppia,kun pyörittää niin ohjelma kaatuu muutaman iteraation jälkeen */ while(exitProg==0) { AddNewBook(myBook); } free(myBook); free(myCustomer); return 0; } // *************** Funktiot ************************* void AddNewBook(BOOK myBook[]) { /* Tällä funktiolla kysytään käyttäjältä kirjan tiedot, jotka tallennetaan * BOOK-struktuuriin. */ if (bookIdx > 0) { ReallocBook(myBook); } char buffer[20]; myBook[bookIdx].IDnumber=bookIdx; printf("Enter book's name: "); scanf("%s",buffer); strcpy(myBook[bookIdx].name, buffer); printf("Enter book's author: "); scanf("%s",buffer); strcpy(myBook[bookIdx].author, buffer); printf("Enter book's genre (0=romance, 1=thriller, 2=scifi, 3=horror,4=general): "); scanf("%d",(int *) &myBook[bookIdx].myBookStatus); myBook[bookIdx].myBookStatus=available; printf("nimi: %s\n",myBook[bookIdx].name); bookIdx++; printf("\n"); } int ReallocBook(BOOK myBook[]) { /* Tällä funktiolla varataan myBook[]:lle lisää tilaa muistista */ BOOK *tmp; tmp=NULL; tmp=(BOOK *)realloc(myBook,sizeof(BOOK)*(bookIdx+1)); if (tmp == NULL) { printf("Dynamic memory allocation: %s\n\n", strerror(errno)); return 1; } myBook=tmp; return 0; }
Kun sijoitat myBook=tmp, et suinkaan muuta tuon muuttujan arvoa toisessa funktiossa, vaan muutos tapahtuu vain paikallisesti. Jotta saisit muutettua osoitinta, sinun pitää välittää osoitin osoittimeen.
#include <stdio.h> int a = 1, b = 2; void f(int* i) { i = &b; } void g(int** i) { *i = &b; } int main() { int* i = &a; printf("%d\n", *i); // 1 f(i); printf("%d\n", *i); // 1 taas g(&i); printf("%d\n", *i); // 2, koska annettiin osoitin i:hin. }
Kiitos vastauksesta! Olit ihan oikeassa siinä, että muutos tapahtui vain paikallisesti. Ohjelma kuitenkin kaatuu edelleen, kun realloc:ia kutsutaan toisen kerran. En itse huomaa, mikä tuossa koodissa on vielä pielessä?
Alla korjattu koodi.
#include <stdio.h> #include <errno.h> #include <stdlib.h> #include <string.h> // Globaalit struktuurit ja muuttujat typedef enum book_genre { romance=0,thriller,scifi,horror,general } GENRE; typedef enum book_status { loaned=0,available,storage,missing,reserved,removed } BOOKSTATUS; typedef enum customer_status { active,inactive,banned } CUSTOMERSTATUS; typedef struct book { int IDnumber; int IDcustomerNumber; char name[20]; char author[20]; GENRE myGenre; BOOKSTATUS myBookStatus; } BOOK; typedef struct customer { int IDnumber; char firstName[20]; char familyName[20]; char email[20]; CUSTOMERSTATUS myCustomerStatus; } CUSTOMER; void AddNewBook(BOOK*); int ReallocBook(BOOK**); int bookIdx=0; int main(void) { int exitProg=0; BOOK* myBook=NULL; CUSTOMER* myCustomer=NULL; myBook=(BOOK*)malloc(sizeof(BOOK)); if (myBook == NULL) { printf("Dynamic memory allocation: %s\n\n", strerror(errno)); return 1; } /* Kun alla olevan rivin kommentoi pois, niin ohjelma ei kaadu. Ohjelman * kannalta rivi on silti välttämätön. */ myCustomer=(CUSTOMER*)malloc(sizeof(CUSTOMER)); if (myCustomer==NULL) { printf("Dynamic memory allocation: %s\n\n", strerror(errno)); //return 1; } /* Tätä luuppia,kun pyörittää niin ohjelma kaatuu muutaman iteraation jälkeen */ while(exitProg==0) { AddNewBook(myBook); } free(myBook); free(myCustomer); return 0; } // *************** Funktiot ************************* void AddNewBook(BOOK* myBook) { /* Tällä funktiolla kysytään käyttäjältä kirjan tiedot, jotka tallennetaan * BOOK-struktuuriin. */ if (bookIdx > 0) { ReallocBook(&myBook); // Välitetään myBook-osoittimen osoite } char buffer[20]; myBook[bookIdx].IDnumber=bookIdx; printf("Enter book's name: "); scanf("%s",buffer); strcpy(myBook[bookIdx].name, buffer); printf("Enter book's author: "); scanf("%s",buffer); strcpy(myBook[bookIdx].author, buffer); printf("Enter book's genre (0=romance, 1=thriller, 2=scifi, 3=horror,4=general): "); scanf("%d",(int*)&myBook[bookIdx].myBookStatus); myBook[bookIdx].myBookStatus=available; bookIdx++; printf("\n"); } int ReallocBook(BOOK** myBook) { /* Tällä funktiolla varataan myBook[]:lle lisää tilaa muistista */ BOOK* tmp; tmp=NULL; tmp=(BOOK*)realloc(*myBook,sizeof(BOOK)*(bookIdx+1)); if (tmp == NULL) { printf("Dynamic memory allocation: %s\n\n", strerror(errno)); return 1; } *myBook=tmp; // annetaan myBook-osoittimelle tmp-osoittimen arvo return 0; }
Samainen virhe jäi yhä koodiisi seuraavalle tasolle (AddNewBook). Välität nyt main-funktiosta aina saman (vanhan) osoittimen.
Suosittelen debug-keinoksi osoittimen printtausta aina varaamisen ja käytön yhteydessä.
printf("%p\n", osoitin);
Joo kiitos paljon! Nyt toimii koko ohjelma. Ei oikein ole vielä nuo pointterit ihan selkäytimessä, mutta tämä oli erittäin opettavainen kokemus.
Onnistumisesta onkin hyvä jatkaa eteenpäin. Mitä käy, kun realloc
ei onnistukaan varaamaan lisää muistia funktiossa ReallocBook
? Entä jos funktiossa AddNewBook
kirjan tai kirjailijan nimi on yli 19 tavua pitkä?
(scanf
ei ole kovin hyvä tapa lukea syötettä ihan oikealta käyttäjältä (siis ihmiseltä), sillä käyttäjillä on tapana antaa syöte väärin. Varsinkin tekstiä ("%s") lukiessa tulee varautua, tai välttää scanf
ia kokonaan, kun kyse ei ole oikeasti muotoillusta datasta.)
Aihe on jo aika vanha, joten et voi enää vastata siihen.