为了简化未来学校作业的开发,我决定为我常用的两个数据结构创建一个API(这就是你所说的吗?) - 一个链表和一个哈希表。
在开发这些中的每一个时,我最终得到了以下两个插入函数:
int list_insert(list *l, char *data, unsigned int idx);
int hash_insert(hash_table **ht, char *data);
list_insert()
函数(以及所有列表函数)最终都是按值传递,因为我从来没有必要直接修改list *
本身,除非我是malloc'或免费的。但是,因为我想在我的哈希表中包含自动重写,我发现我必须在任何可能强制重新哈希的函数中通过引用而不是by-value传递表。现在我最终得到如下语法:
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 :(得分:1)
这取决于你并采取一点直觉。当传递大型结构时,我通过引用传递,这样我就不会占用额外的堆栈空间并且烧掉循环复制结构。但是对于像你这样的小型struts,使用堆栈可能会更有效,具体取决于你的目标处理器,你使用这些值的频率,以及编译器的作用。您的编译器可能会破坏该结构并将其值放入寄存器中。
但是如果您通过引用传递并且不打算修改值,则最好将指针传递给const,例如:const list * l
。这样就不会有意外修改值的风险,它会使界面更清晰 - 现在调用者知道值不会改变。
一致性很好,我个人会倾向于这个方向,特别是在大型界面上,因为从长远来看它可能会让事情变得更容易,但我肯定会使用const。在这样做时,您允许编译器发现任何意外分配,以便以后您不需要追踪难以发现的错误。
答案 1 :(得分:1)
以下是一般的经验法则:
通过引用传递的其他注意事项:
有时,struct / union 似乎类似于适当的类型,但如果类型相似,则可以用数组替换。这有助于优化(例如循环矢量化)