고정 배열이있는 조회 테이블

다음 연습을 구현했습니다.

find(struct table*, const char*), insert(struct table*, const char*,int)remove(struct table*, const char*)와 같은 작업이 포함 된 조회 테이블. 테이블 표현은 struct 쌍의 배열 또는 배열 쌍 (const char*[] 및 ); 원하는 경우 함수의 반환 유형도 선택하세요.

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

개선 된 사항에 대해 자유롭게 의견을 보내주세요. 이 작업을 수행하는 더 좋은 방법이 있습니까?

또한 한 가지 구체적인 질문이 있습니다. assert 사용이 적절합니까?

댓글

  • 문체의 핵심으로 저는 ‘ 포인터에서 *를 권장합니다. 변수 이름 옆에 입력해야합니다 (예 : const char *word). C 유형 구문은 사용을 모방하도록 설계되었습니다. 예를 들어 ‘ d 유형 *word는 요소. 또한 int *a, b;에있는 b 유형이 더 명확 해집니다.

답변

struct

개인적으로 사용하여 올바른 선택을했다고 생각합니다. iv id =의 2 개 배열이 아닌 char*int가있는 struct 각각 “eccc22e211”>

int.Pair는 앱에있는 데이터의 개념적 단위이므로 함께 결합하는 것이 좋습니다. 두 개의 개별 어레이가 있다면 동기화가 해제되기 쉽고 왜 그런 일이 발생했는지 디버깅하기가 어렵습니다. 잘하셨습니다.

const 및 함수를 매크로보다 선호

매크로를 사용하여 배열 크기를 정의했습니다. 이렇게하면 프로그래밍하는 동안 단점이 있습니다. 값에 대한 유형 정보를 제거했습니다. 만들면 :

const int ARR_SIZE = 10; 

타입 안전성을 얻을 수 있습니다. 편집 : “그렇지 않은 C ++-ism”입니다. t는 C에서 일한다. 나의 나쁜! 그러나 다음 단락의 나머지 조언은 내가 아는 한 정확합니다.

매개 변수를 사용하는 매크로를 사용하면 매크로가 예기치 않은 방식으로 사용되어 문제를 디버깅하기 어렵게 될 위험이 있습니다. 다행히 여기서는 그렇게하지 않았습니다.하지만 일반적으로 매크로에 도달하는 경우 상수 또는 함수를 사용하는 것이 더 나은지 자문 해보십시오. 거의 항상 그럴 것입니다 (상수를 사용할 수 있다고 가정하면 원하는 방식으로).

오류

코드에 오류가 있습니다. make_pair()에서는 확인하지 않습니다. malloc() 성공한 경우. 실패하면 메모리가 할당되지 않고 pairNULL를 가리 킵니다. pair->val 또는 pair->word를 할당하려고하면 충돌이 발생합니다.

수정 한 경우 table_insert()는 먼저 NULL인지 확인하지 않고 make_pair()의 결과를 사용합니다. t 즉시 충돌이 발생합니다. “tbl->pairs[tbl->sz]에서 pair의 값을 갖도록 배열 포인터를 할당하고 있기 때문입니다. 다른 항목을 검색, 인쇄 또는 삽입하려고하면 “테이블의 해당 항목을 반복하고 작업을 수행 할 때 충돌이 발생합니다.

이 오류를 불가능하게 만들 수 있습니다. 배열 항목을 동적으로 할당합니다. 배열을 포인터가 아닌 Pair 구조체의 배열로 만들기 만하면됩니다.

이름 지정

많은 이름이 정말 좋습니다. . PairTable는이 작업에 대해 적절하고 읽기 쉬운 이름입니다. make_pair(), table_insert() 등은 정보를 제공합니다. 하지만 일부는 개선 될 수 있습니다. tbltable보다 많은 입력을 절약하지 않습니다. 전체 단어를 사용하십시오. szsize와 동일합니다. i는 루프 변수 이름으로 허용되지만 더 설명적인 경우 더 좋습니다. 예 : entry_index 또는 pair_index. 반복되는 내용을 설명해야합니다.

댓글

  • 정의를 const int ARR_SIZE = 10; struct Table { struct Pair* pairs[ARR_SIZE]; size_t sz; };에서 ARR_SIZE가 const가 아니라는 오류가 표시됩니다. 그렇다면이 경우 어떻게 사용합니까?
  • 죄송합니다. ‘ C ++-ism입니다.이 질문이 C ++ 질문이라고 생각했습니다. 내 문제입니다. ‘ 내 답변을 업데이트하겠습니다.
  • 나는 ‘ 설명 변수 이름의 확고한 지지자이지만이 프로그램의 루프의 단순함으로 인해 변수 이름은 잘 작동합니다.
  • malloc()가 실패하면 pair->val 또는 pair->word 운이 좋다면 충돌 할 수 있습니다 . 계속해서 잘못된 결과를 내거나 완전히 예상치 못한 일을 할 수 있습니다. 이것이 ‘ 정의되지 않은 동작의 기쁨입니다!

답변

  • malloc의 결과를 전송하지 마십시오.

    mallocvoid *, C에서는 void *를 임의의 포인터로 변환하는 것이 유효합니다. 정수에서 포인터로의 변환에 대한 경고를 표시하지 않도록 캐스트하면 컴파일러에 “malloc 프로토 타입이 없으며 기본값이 (고대 C 규칙). 이제 int와 포인터의 크기가 다른 경우 malloc 반환 값이 잘리고 모든 불쾌한 결과가 발생합니다.

p>

  • sizeof(type)는 권장하지 않습니다. sizeof(expression)를 선호합니다. 귀하의 경우에는 다음을 고려하십시오.

     struct Pair * pair = malloc(sizeof(*pair)); 
  • table_insert 전체 테이블에 맹목적으로 삽입합니다.

    그렇지 않은 경우 오류 표시를 반환합니다.

  • 실제 삽입

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

    실제로 한 줄이어야합니다.

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

    • malloc을 캐스트하는 것이 좋지 않은 이유는 무엇입니까? C ++에서는 심지어 컴파일러 오류가 발생합니까?
    • @Sa ndro4912 질문은 C로 태그가 지정됩니다. C ++는 특히이 점에서 다른 언어입니다. 이제 C 컴파일러가 불평하면 malloc 프로토 타입이 누락되어 심각한 문제가 발생할 수 있습니다.
    • 예, c입니다. 왜 여기에서 캐스트로 유형 변경을 나타내는 것이 좋지 않은지 궁금했습니다.
    • @ Sandro4912 편집 참조
    • @ Sandro4912 pair = malloc(sizeof *pair)는 처음에는 올바르게 코딩하기 쉽고 검토 및 유지 관리가 더 쉽습니다.

    답변

    모두 활성화 컴파일러 경고

    잘 활성화 된 컴파일러를 사용하면 for (int i = 0; i < tbl->sz; ++i)itbl->sz 및 범위. 시간을 절약하고 모든 경고를 활성화하고 for (size_t i = 0; i < tbl->sz; ++i)를 사용합니다.

    일반적으로 int,size_t를 거의 같은 의미로 사용하면 코드가 엉망이됩니다. . 다시 설계하고 size_t 만 사용했습니다.

    얕은 카피와 딥 카피의 혼합 사용

    make_pair(const char* word, int val)는 완전히 새로운 struct Pair (전체 복사)를 할당하고 구성하지만 word가 가리키는 내용은 복사하지 않습니다.

    p>

    아마

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

    const

    *tbl를 수정하지 않으므로 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) 

    이름 지정

    코드 const char* word;를 사용하지만 단어가 아니라 strcmp()에서 사용되는 문자열 입니다. p>

    —– 추가 사항

    계약 위반?

    “…와 같은 작업으로 조회 테이블 구현”요구 사항에 const char* 문자열 에 대한 포인터입니다. 따라서 strcmp()는 명시되지 않은 요구 사항이없는 한 의심 스럽습니다. char *로서 코드는 간단한 비교를 사용할 수 있습니다.

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

    assert()

    내가 assert를 사용하는 것이 적절합니까?

    추가 / 검색을위한 char * 포인터가 문자열 으로 지정되지 않았습니다. assert(word);word == NULL는 유효하지 않은 것으로 알려져 있습니다.

    table_find(struct Table* tbl, const char* word, int* return_val) div의 assert(return_val) >는 괜찮지 만 return_val == NULL

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

    를 허용하도록 다시 디자인하겠습니다.

    답글 남기기

    이메일 주소를 발행하지 않을 것입니다. 필수 항목은 *(으)로 표시합니다