Hakutaulukko, jossa on kiinteä taulukko

Toteutin seuraavan harjoituksen:

Toteuta a hakutaulukko, jossa on toimintoja, kuten find(struct table*, const char*), insert(struct table*, const char*,int) ja remove(struct table*, const char*). Taulukon esitys voi olla struct -parin tai matriisiparin taulukko (const char*[] ja int*); valitset, valitse myös palautustyypit toiminnoillesi.

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #define ARR_SIZE 10 struct Pair { const char* word; int val; }; struct Table { struct Pair* pairs[ARR_SIZE]; size_t sz; }; struct Pair* make_pair(const char* word, int val) { assert(word); struct Pair* pair = (struct Pair*)malloc(sizeof(struct Pair)); pair->val = val; pair->word = word; return pair; } void table_empty(struct Table* tbl) { assert(tbl); size_t i = 0; for (i = 0; i < tbl->sz; ++i) { free(tbl->pairs[i]); tbl->pairs[i] = NULL; } tbl->sz = 0; } int search_word(struct Table* tbl, const char* word) { assert(tbl); assert(word); size_t i = 0; for (i = 0; i < tbl->sz; ++i) { //printf("%s %i\n", tbl->pairs[i]->word,i); if (strcmp(tbl->pairs[i]->word, word) == 0) { return i; } } return -1; // error } void table_insert(struct Table* tbl, const char* word, int val) { assert(tbl); assert(word); int i = search_word(tbl, word); if (i != -1) { // replace val tbl->pairs[i]->val = val; } else { // add new pair struct Pair* pair = make_pair(word, val); tbl->pairs[tbl->sz] = pair; // add pair at the last position ++tbl->sz; } } int table_find(struct Table* tbl, const char* word, int* return_val) { assert(tbl); assert(word); assert(return_val); int i = search_word(tbl, word); if (i != -1) { *return_val = tbl->pairs[i]->val; return 0; } return -1; // error not found } int table_remove(struct Table* tbl, const char* word) { assert(tbl); assert(word); int i = search_word(tbl, word); if (i == -1) return -1; free(tbl->pairs[i]); // free value at current pos tbl->pairs[i] = tbl->pairs[tbl->sz - 1]; // put address of last word at the pos of the current --tbl->sz; // "erase" last word return 0; } void table_print(struct Table* tbl) { assert(tbl); printf("\n"); printf("table size = %i\n", tbl->sz); for (int i = 0; i < tbl->sz; ++i) printf("%s %i\n", tbl->pairs[i]->word, tbl->pairs[i]->val); fflush(stdout); } void print_search_result(struct Table* tbl, const char* word) { assert(tbl); assert(word); int val = 0; if (table_find(tbl, word, &val) == 0) printf("%s %i\n",word, val); else printf("%s not found in table\n", word); printf("\n"); fflush(stdout); } void print_remove_result(struct Table* tbl, const char* word) { assert(tbl); assert(word); if (table_remove(tbl, word) == -1) printf("%s not deleted\n", word); else printf("%s deleted\n", word); printf("\n"); fflush(stdout); } int main() { struct Table table = { 0 }; int val = 0; table_insert(&table, "Hello", 10); table_insert(&table, "Test", 15); table_insert(&table, "Hello", 18); // testing overrite val table_insert(&table, "What", 5); table_insert(&table, "is", 3); table_insert(&table, "going", 4); table_insert(&table, "on", 77); table_print(&table); print_search_result(&table, "Hello"); print_search_result(&table, "Test"); print_search_result(&table, "keyword"); print_remove_result(&table, "Hello"); print_remove_result(&table, "Hello"); // double delete == false print_remove_result(&table, "What"); print_remove_result(&table, "going"); print_remove_result(&table, "is"); print_remove_result(&table, "on"); print_remove_result(&table, "Test"); table_print(&table); table_insert(&table, "Hello", 10); table_insert(&table, "Test", 15); table_insert(&table, "Hello", 18); // testing overrite val table_insert(&table, "What", 5); table_insert(&table, "is", 3); table_insert(&table, "going", 4); table_insert(&table, "on", 77); table_print(&table); table_empty(&table); table_print(&table); getchar(); } 

Kommentoi parannuksia. Onko olemassa parempi tapa tehdä tämä?

Minulla on myös yksi erityinen kysymys: sopivatko assert -tapani käyttämiseen?

Kommentit

  • Tyylikkäinä suosittelijoina ’ suosittelen osoitinta * tyyppi on muuttujan nimen vieressä (esim. const char *word). C-tyypin syntaksin tarkoituksena on jäljitellä käyttöä, jossa esimerkiksi ’ d tyyppi *word käyttääksesi const char -elementti. Huomaa myös, että tämä tekee selvemmäksi, minkä tyyppinen b on ryhmässä int *a, b;.

Vastaa

Käyttämällä struct

Henkilökohtaisesti luulen, että teit oikean valinnan käyttämällä struct, jossa on char* ja int kuin kaksi char* ja int. Pair on käsitteellinen tietoyksikkö sovelluksessasi, joten on järkevää koota ne yhteen. Jos sinulla olisi 2 erillistä taulukkoa, heidän olisi helppo päästä pois synkronoinnista ja vaikea selvittää miksi niin tapahtui. Hyvää työtä!

Mieluummin const ja toiminnot makroille

Olet määrittänyt matriisin koon makrolla. Tämä asettaa olet epäedullisessa asemassa ohjelmoinnin aikana. Olet poistanut arvon tyyppitiedot. Tekemällä sen:

const int ARR_SIZE = 10; 

saat tyyppiturvallisuuden. Muokkaa: Se ”sa C ++ – ism, joka ei tee” ei toimi C. Mutta loput seuraavassa kappaleessa annetuista neuvoista ovat oikein, sikäli kuin tiedän.

Parametreja käyttävillä makroilla on vaarana, että niitä käytetään odottamattomilla tavoilla ja aiheuttaen vaikeita virheenkorjausongelmia. Onneksi et ole tehnyt niin täällä. Mutta yleensä, jos huomaat etsivän makroa, kysy itseltäsi, olisiko sinulle parempi, jos sinulla olisi vakio vai funktio. Olet melkein aina (olettaen, että voit käyttää vakiota halutulla tavalla).

Virheet

Koodissasi on joitain virheitä. Kohdassa make_pair() et tarkista jos malloc() onnistui. Jos se epäonnistuu, muistia ei kohdenneta ja pair osoittaa kohtaan NULL. Kun yrität määrittää pair->val tai pair->word, kaatut.

Jos olet korjannut tämän, table_insert() käyttää make_pair() -tulosta tarkistamatta, onko se ensin ”s NULL. Tämä voitti” Älä kaadu heti, koska määrität vain taulukon osoittimen ryhmässä tbl->pairs[tbl->sz] arvon pair. Mitä tapahtuu myöhemmin, kun yrität etsiä tai tulostaa tai lisätä toisen kohteen, saat kaatumisen, kun iteroit kyseisen taulukon merkinnän ja yrität tehdä mitään sen kanssa.

Voit estää näitä virheitä tekemättä dynaamisesti allokoimalla taulukon merkinnät. Tee matriisista yksinkertaisesti Pair -rakenteiden matriisi eikä osoitin heille.

Nimeäminen

Monet nimesi ovat todella hyviä . Pair ja Table ovat kunnollisia, luettavia nimiä tälle tehtävälle. make_pair(), table_insert() jne. ovat informatiivisia. Joitakin voitaisiin kuitenkin parantaa. tbl ei säästä paljon kirjoittamalla table. Käytä vain koko sanaa. Sama sz vs. size kanssa. i on hyväksyttävä silmukamuuttujan nimellä, mutta olisi vielä parempi, jos se olisi kuvailevampi. Esimerkiksi entry_index tai pair_index. Sen tulisi kuvata mitä iteroit.

Kommentit

  • kun vaihdan määritelmän arvoksi const int ARR_SIZE = 10; se antaa minulle virheen struct Table { struct Pair* pairs[ARR_SIZE]; size_t sz; };, että ARR_SIZE ei ole const. Joten miten sitä käytetään tässä tapauksessa sitten?
  • Anteeksi – että ’ sa C ++ – ism. Luulin, että tämä oli C ++ -kysymys. Minun huono. Päivitän vastaukseni ’.
  • I ’ olen vankka kuvailevien muuttujien nimien kannattaja, mutta tämän ohjelman silmukoiden yksinkertaisuudella luulen, että muuttujan nimi i toimii hienosti.
  • Jos malloc() epäonnistuu, määritys pair->val tai pair->word saattaa kaatua jos olet onnekas . Se voi vain jatkua ja antaa vääriä tuloksia, tai se voi tehdä jotain aivan odottamatonta. Se ’ on määrittelemättömän käyttäytymisen ilo!

Vastaa

  • Älä suorita malloc -tulosta.

    malloc palauttaa arvon void *, ja C: ssä on kelvollinen muuntaa void * mihin tahansa osoittimeen. Jos heität estämään varoituksen kokonaisluvun osoittimen muuntamisesta, se tarkoittaa, että kääntäjällä ei ole malloc prototyyppiä, ja oletuksena palauttaa int (se on muinainen C-käytäntö). Jos nyt int ja osoitin ovat erikokoisia, malloc-palautusarvo katkaistaan kaikilla ikävillä seurauksilla.

  • Ei ole suositeltavaa sizeof(type). Pidä mieluummin sizeof(expression). Harkitse tapauksessasi

     struct Pair * pair = malloc(sizeof(*pair)); 
  • table_insert lisää sokeasti täyteen taulukkoon. Sen pitäisi testata tbl->sz < ARR_SIZE ja palauta virheilmoitus, jos se ei ole.

  • Varsinainen lisäys

     struct Pair* pair = make_pair(word, val); tbl->pairs[tbl->sz] = pair; // add pair at the last position 

    pitäisi olla oikeastaan yksi rivi:

     tbl->pairs[tbl->sz] = make_pair(word, val); 

kommentit

  • miksi mallocin näyttäminen ei ole hyvä idea? C ++: ssa se saattaisi jopa kääntäjävirheen?
  • @Sa ndro4912 Kysymys on merkitty tunnisteella C. C ++ on eri kieli, erityisesti tässä suhteessa. Jos C-kääntäjä valittaa, se tarkoittaa, että malloc -prototyyppi puuttuu, mikä voi johtaa ikäviin ongelmiin.
  • kyllä tiedän olevan c. ihmettelin vain, miksi tässä ei ole hyvä käytäntö ilmoittaa tyyppimuutokset näyttelijöillä
  • @ Sandro4912 Katso muokkaus.
  • @ Sandro4912 pair = malloc(sizeof *pair) on helpompi koodata oikein ensimmäisellä kerralla, helpompi tarkistaa ja ylläpitää.

Vastaa

Ota kaikki käyttöön kääntäjän varoitukset

Kun kääntäjä on hyvin käytössä, for (int i = 0; i < tbl->sz; ++i) olisi pitänyt varoittaa i ja tbl->sz sekä alue. Säästä aikaa ja ota kaikki varoitukset käyttöön ja käytä for (size_t i = 0; i < tbl->sz; ++i).

Yleensä koodi on sekava, kun int,size_t käytetään melkein vaihdettavissa . Arkistoitin uudelleen ja käytän vain size_t.

Pienen ja syvän kopion sekakäyttö

make_pair(const char* word, int val) jakaa ja muodostaa kokonaan uuden struct Pair (syvä kopio), mutta ei kopioi sitä, mihin word osoittaa.

Ehkä

// pair->word = word; pair->word = strdup(word); 

Käytä const

search_word() ei muokkaa *tbl, joten käytä const to convey that. Same for table_find () , table_print () , print_search_result () `.

// int search_word(struct Table* tbl, const char* word) int search_word(const struct Table* tbl, const char* word) 

Nimeäminen

Koodi käyttää const char* word;, mutta se ei ole ”sana”, vaan merkkijono , jota käytetään kohdassa strcmp().

—– Lisäykset

Sopimusrikkomus?

Mikään vaatimuksissa ”Ota käyttöön hakutaulukko, kuten …” – toiminnot osoittavat, että const char* on osoitin merkkijonolle . Joten kutsumalla strcmp() on kyseenalainen, ellei ilmoittamattomia vaatimuksia ole. char * -koodina voisi käyttää yksinkertaista vertailua

// if (strcmp(tbl->pairs[i]->word, word) == 0) if (tbl->pairs[i]->word == word) 

assert()

sopivatko väitteeni käyttötarkoitukset?

Jos char * lisättävän / haettavan osoittimen ei ole määritelty olevan merkkijono , assert(word); ei ole oikea, koska word == NULL ei tiedetä olevan virheellinen.

assert(return_val) ryhmässä table_find(struct Table* tbl, const char* word, int* return_val) on OK, mutta suunnittelen uudelleen sallimalla return_val == NULL

if (i != -1) { // add if (return_val) { *return_val = tbl->pairs[i]->val; } return 0; } 

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *