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)
ésremove(struct table*, const char*)
. A táblázat ábrázolása lehet egystruct
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
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 astruct 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, apair->val
vagy apair->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 avoid *
, és C-ben érvényes avoid *
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 rendelkezikmalloc
prototípussal, és alapértelmezés szerint visszaadja egyint
(ez egy ősi C konvenció). Most, ha azint
é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 asizeof(expression)
-t. Esetében fontolja meg astruct Pair * pair = malloc(sizeof(*pair));
-
table_insert
vakon beszúr egy teljes táblába. Meg kell vizsgálnia atbl->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; }
*
-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 aint *a, b;
típusban.