Keresőtábla fix tömb

A következő gyakorlatot hajtottam végre:

keresőtábla olyan műveletekkel, mint find(struct table*, const char*), insert(struct table*, const char*,int) és remove(struct table*, const char*). A táblázat ábrázolása lehet egy struct pár tömbje vagy egy tömbpár (const char*[] és ); választja, válassza ki a függvények visszatérési típusait is.

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

Nyugodtan kommentáljon minden fejlesztést. Van erre jobb módszer?

Van egy konkrét kérdésem is: megfelelőek-e a assert használata?

Megjegyzések

  • Stílusos kiválasztóként ‘ d ajánlom egy mutatóban a * -t típus legyen a változó neve mellett (pl. const char *word). A C típusú szintaxist a használat utánzására tervezték, ahol például ‘ d type *word a elemet. Vegye figyelembe azt is, hogy ez világosabbá teszi, hogy milyen típusú b van a int *a, b; típusban.

Válasz

A struct

használata Személy szerint úgy gondolom, hogy jól választott egy struct egy char* és int be, nem pedig a char* és int. A Pair az alkalmazás fogalmi adategysége, ezért érdemes ezeket összeállítani. Ha 2 külön tömbje lenne, akkor könnyű lenne számukra kijutni a szinkronból, és nehéz hibakeresni, hogy ez miért történt. Szép munka!

A const és a funkciók előnyben részesítése a makrókkal szemben

A tömb méretét makró segítségével határozta meg. Ez hátrányos helyzetben van a programozás közben. Eltávolította az érték típusinformációit. Azáltal, hogy elkészíti:

const int ARR_SIZE = 10; 

a típusbiztonságot kapja. Szerkesztés: Ez a “C ++ – ism, ami nem” ne dolgozzon C-ben. Rossz! De a következő bekezdésben szereplő többi tanács helyes, ha jól tudom.

A paramétereket felvevő makrókkal fennáll annak a veszélye, hogy váratlan módon használják őket, és nehéz hibakeresési problémákat okoznak. Szerencsére még nem tetted ezt itt. De általában, ha azt találod, hogy makró után nyúlsz, kérdezd meg magadtól, hogy jobb lenne-e egy állandóval vagy egy függvénnyel. Szinte mindig az lesz (feltételezve, hogy használhatod az állandót a kívánt módon).

hibák

Van néhány hiba a kódban. A make_pair() részben nem ellenőrizheti, hogy ha a malloc() sikerrel járt. Ha nem sikerül, akkor nincs memória kiosztva, és pair a NULL pontra mutat. Amikor megpróbálja hozzárendelni a pair->val vagy a pair->word rendszert, összeomlik.

Ha kijavította, table_insert() a make_pair() eredményét használja anélkül, hogy ellenőrizné, hogy “s NULL-e először. Ez nyert” Azonnal összeomlik, mert csak a tbl->pairs[tbl->sz] fájlhoz rendeli a tömbmutatót, hogy pair értéke legyen. Ami később történik, megpróbál keresni, kinyomtatni vagy beilleszteni egy másik elemet, összeomlik, amikor a táblázatban szereplő bejegyzést ismétli, és megpróbál bármit is csinálni vele.

Ezeket a hibákat nem teheti meg, ha dinamikusan lefoglalja a tömb bejegyzéseket. Egyszerűen tegye a tömböt egy Pair struktúrák tömbjévé, nem pedig mutatóvá.

Elnevezés

Sok név nagyon jó . A Pair és a Table tisztességes, olvasható nevek ehhez a feladathoz. A make_pair(), table_insert() stb. informatív jellegűek. Néhány azonban javítható. A tbl nem sokat takarít meg a gépelés során: table. Csak használja az egész szót. Ugyanez a sz és a size. A i elfogadható ciklusváltozó névként, de még jobb lenne, ha leíróbb lenne. Például: entry_index vagy pair_index. Le kell írnia, hogy mit csinálsz újra.

Megjegyzések

  • amikor a definíciót const int ARR_SIZE = 10; hibát ad a struct Table { struct Pair* pairs[ARR_SIZE]; size_t sz; }; fájlban, hogy az ARR_SIZE nem konst. Tehát akkor ebben az esetben hogyan kell használni?
  • Sajnálom – hogy ‘ sa C ++ – ism. Azt gondoltam, hogy ez egy C ++ kérdés. Rossz. Én ‘ frissítem a válaszomat.
  • I ‘ a leíró változónevek meggyőződéses támogatója, de a program ciklusainak egyszerűségével szerintem a remekül működik.
  • Ha a malloc() nem sikerül, a pair->val vagy a pair->word összeomolhat, ha szerencséje van . Lehet, hogy csak folytatódik, és rossz eredményeket ad, vagy valami egészen váratlan eseményt is elérhet. Ez ‘ okozza a meghatározatlan viselkedés örömét!

Válasz

  • Ne adja le a malloc eredményét.

    malloc visszatér a void *, és C-ben érvényes a void * bármely mutatóra konvertálása. Ha elveti az egész szám mutató egérgé alakításával kapcsolatos figyelmeztetés elnyomását, az azt jelenti, hogy a fordító nem rendelkezik malloc prototípussal, és alapértelmezés szerint visszaadja egy int (ez egy ősi C konvenció). Most, ha az int és a mutató különböző méretű, a malloc visszatérési értéke csonka lesz, minden csúnya következménnyel jár.

  • Nem ajánlott sizeof(type). Előnyben részesíti a sizeof(expression) -t. Esetében fontolja meg a

     struct Pair * pair = malloc(sizeof(*pair)); 
  • table_insert vakon beszúr egy teljes táblába. Meg kell vizsgálnia a tbl->sz < ARR_SIZE és adjon meg hibajelzést, ha nem.

  • A tényleges beszúrás

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

    valóban egyetlen sor legyen:

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

Megjegyzések

  • miért nem jó ötlet mallocot önteni? A c ++ verzióban akár fordítói hiba is előfordulna?
  • @Sa ndro4912 A kérdés címkéje C. A C ++ egy másik nyelv, különösen ebben a tekintetben. Ha a C fordító panaszkodik, az azt jelenti, hogy hiányzik a malloc prototípus, ami csúnya problémákhoz vezethet.
  • igen, tudom, hogy c. csak arra voltam kíváncsi, hogy itt miért nem jó a típusváltások szerepeltetéssel történő feltüntetése
  • @ Sandro4912 Lásd a szerkesztést.
  • @ Sandro4912 pair = malloc(sizeof *pair) könnyebb az első alkalommal helyesen kódolni, könnyebben áttekinthető és karbantartható.

Válasz

Az összes engedélyezése fordító figyelmeztetései

Egy jól engedélyezett fordítóval for (int i = 0; i < tbl->sz; ++i) figyelmeztetnie kellett volna a i és tbl->sz valamint a tartomány. Takarítson meg időt és engedélyezze az összes figyelmeztetést, és használja a következőt: for (size_t i = 0; i < tbl->sz; ++i).

Általánosságban elmondható, hogy a kód szinte felcserélhető a int,size_t használatával. . “Újraépíteném és csak a size_t -t használnám.

Sekély és mély másolat vegyes használata

make_pair(const char* word, int val) egy teljesen új struct Pair (mély másolat) allokációt alkot, de mégsem másolja azt, amire word mutat.

Talán

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

Használja a const

search_word() nem módosítja a *tbl elemet, ezért használja a 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) 

Elnevezés

Kód const char* word; -t használ, mégsem egy “szó”, hanem egy karakterlánc a strcmp().

—– Kiegészítések

Szerződésszegés?

A “Keresési tábla végrehajtása olyan műveletekkel, mint …” követelményekben semmi nem jelzi, hogy const char* egy string re mutató mutató. Tehát a strcmp() kérdéses, hacsak nincsenek megadva követelmények. char * kódként a kód egyszerű összehasonlítást használhat

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

A assert()

megfelelőek-e az állításom használata?

Ha a hozzáadni / keresni kívánt char * mutató nincs megadva karakterlánc nak, assert(word); nem megfelelő, mivel word == NULL érvénytelen.

A assert(return_val) a table_find(struct Table* tbl, const char* word, int* return_val) rendben van, mégis áttervezném, hogy engedélyezzem a return_val == NULL

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

Vélemény, hozzászólás?

Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük