2014-12-27 9 views
3

Чтобы упростить разработку будущих школьных заданий, я решил создать API (это то, что вы бы назвали его?) Для двух структур данных, которые я обычно использую - связанный список и хеш-таблицу.Стиль кодирования - перейдите по ссылке или перейдите по значению?

При разработке каждого из них я в конечном итоге со следующими двумя вставными функциями:

int list_insert(list *l, char *data, unsigned int idx); 
int hash_insert(hash_table **ht, char *data); 

list_insert() функции (и всех функций списка) заканчивал тем, что передача по значению, так как я никогда не было необходимо непосредственно изменить сам файл list *, если я не был malloc'ing или free'ing его. Однако, поскольку я хотел включить автоматическое переигрывание в моей хеш-таблице, я обнаружил, что мне пришлось передавать таблицу-ссылку вместо значения-значения в любой функции, которая могла бы заставить переименовать. Теперь я в конечном итоге с синтаксисом следующих:

list_insert(l, "foo", 3); 
hash_insert(&ht, "foo"); 

Разница кажется мне немного странным, и я задавался вопросом, должен ли я изменить функции списка будет проходить по ссылке, а ради Консистенция в - - хотя ни одна из моих функций не понадобится использовать ее. Какой здесь типичный консенсус? Должен ли я передавать только по ссылке, если моя функция действительно нуждается в изменении своих аргументов или должна ли я передавать по ссылке для согласованности?

определения структуры:

typedef struct list_node list_node; 
struct list_node { 
    char *data; 
    list_node *next; 
    list_node *prev; 
}; 

typedef struct list list; 
struct list { 
    list_node *head; 
    list_node *tail; 
    size_t size; 
}; 

typedef struct hash_table hash_table; 
struct hash_table { 
    list **table; 
    size_t entries; 
    size_t buckets; 
    float maxLoad; 
    unsigned int (*hash)(char*, unsigned int); 
}; 

Список функций:

list *list_createList(); 
list_node *list_createNode(); 
void list_destroyList(list *l); 
void list_destroyNode(list_node *n); 
int list_append(list *l, char *data); 
int list_insert(list *l, char *data, unsigned int idx); 
int list_remove(list *l, char *data, int (*compar)(const void*, const void*)); 
void list_push(list *l, char *data); 
char *list_pop(list *l); 
int list_count(list *l, char *data, int (*compar)(const void*, const void*)); 
int list_reverse(list *l); 
int list_sort(list *l, int (*compar)(const void*, const void*)); 
int list_print(list *l, void (*print)(char *data)); 

Хэш функции:

hash_table *hash_createTable(size_t buckets, float maxLoad, unsigned int (*hash)(char*, unsigned int)); 
void hash_destroyTable(hash_table *ht); 
list *hash_list(const hash_table **ht); 
int hash_checkLoad(hash_table **ht); 
int hash_rehash(hash_table **ht); 
int hash_insert(hash_table **ht, char *data); 
void hash_stats(hash_table *ht); 
int hash_print(hash_table *ht, void (*print)(char*)); 
+0

Какая информация хранит 'hash_table' соответственно. «список» содержит? Возможно, изменение «list *», переданное по ссылке, имеет такой же смысл, как и модификация «hash_table *» на дальнейшее рассмотрение ... – Deduplicator

+0

Можете ли вы сообщить, что именно делают ваши API и как выглядят ваши структуры? – Gopi

+4

Предложите [Обзор кода] (http://codereview.stackexchange.com), а не здесь. – chux

ответ

1

Вот общее правило:

  • проход по значению, если его typdef является собственным типом (символ, короткий, Int, длинные, длинный длинный, двойной или поплавок)
  • проход по ссылке, если это объединение, структура или массив

Дополнительные соображения для передачи по ссылке:

  • использование сопзЬ, если он не будет изменен
  • использование ограничение, если указатели не указывают на тот же адрес

Иногда структура/объединение кажется как соответствующего типа, но может быть заменен с массивами, если типы схожи. Это может помочь с оптимизацией (например, векторизация векторов)

+0

Вы предполагаете, что структура списка, например, не нужна? И что было бы лучше вместо этого объявить массив из трех указателей? Кроме того, что вы думаете о распределении корня списка/хэш-таблицы в куче в отличие от стека? – user2506293

+0

Хотя я использовал массивы для списков, я больше обращался к структурам с r, g, b, a или x, y, dx, dy, где они все одинаковы. Что касается кучи v. Stack, я пытаюсь организовать новый код, чтобы использовать стек, где это возможно, поэтому я не могу забыть освободиться, но иногда * alloc неизбежно, особенно с существующим кодом. В любом случае, хорошо иметь возможность анализировать сборку для сравнения, поскольку современные компиляторы могут оптимизировать вызовы malloc для использования эффективных встроенных функций. – technosaurus

1

Это до вас, и занимает немного интуиции. При передаче больших структур я передаю ссылку, чтобы я не ест лишнее пространство стека и циклы записи, копируя структуру. Но с небольшими стойками, как у вас, может быть более эффективно использовать стек в зависимости от вашего целевого процессора, как часто вы используете значения и что делает ваш компилятор. Ваш компилятор может сломать эту структуру и поместить ее значения в регистры.

Но если вы передаете по ссылке и не намерены изменять значение, лучше всего передать указатель на const, например: const list * l. Таким образом, вы не рискуете случайно изменить значение, и это сделает интерфейс чище - теперь вызывающий абонент знает, что значение не изменится.

Консистенция хорошая, и я лично склоняюсь в этом направлении, особенно на большом интерфейсе, потому что в конечном итоге это упростит, но я определенно буду использовать const. При этом вы позволяете компилятору обнаруживать любые случайные задания, чтобы впоследствии вам не нужно было отслеживать сложную ошибку.

Смотрите также: Passing a struct to a function in C

+0

Он не передает структуры, но указывает только на структуры (список *) и указывает на указатели на структуры (hash_table **). – netcat

+0

@netcat он спрашивает, должен ли он передать список структур напрямую, если он не намерен его изменять или если он должен передать указатель на структуру, как я понял вопрос. Это неправильно? – Nick