我即将分享的是我在C语言开发中的个人实践经验。它们绝不是唯一组织自己的方式。我只是概述了一种方式而不是唯一的方式。
好吧,从许多方面来看,“C”是一种松散的语言,因此作为开发人员,需要有很多纪律性和严格性。我已经在“C”中专业开发超过20年了,我几乎没有遇到过需要修复的生产级软件。虽然成功的很大一部分可以归功于经验,但其中相当一部分根源于一致的实践。
我遵循一套相当广泛的开发实践,涉及诸如制表符到命名约定等琐碎的事情。我将限制自己关于处理结构体以及特别是内存管理的做法。
如果我有一个在整个软件中都使用的结构体,我会为它编写创建/销毁;初始化/完成类型的函数:
struct foo * init_foo();
void done_foo(struct foo *);
并在这些函数中分配和释放结构体。
如果我在程序中直接操作结构体元素,则不使用typedef。我会在每个声明中使用struct关键字来表明它是一个结构体。只要痛苦程度不太高,我就能忍受这种方式。:-)
如果我发现该结构体非常像一个对象,那么我选择通过一个不透明的API严格地操作结构体元素;然后我为每个元素定义set/get类型的函数,通过在每个程序的头文件中创建一个“前向声明”,创建一个指向该结构体指针的不透明typedef,并仅在结构体API实现文件中声明实际的结构体。
foo.h:
struct foo;
typedef struct foo foo_t;
void set_e1(foo_t f, int e1);
int get_ei(foo_t f);
int set_buf(foo_t f, const char *buf);
char * get_buf_byref(foo_t f)
char * get_buf_byval(foo_t f, char *dest, size_t *dlen);
foo.c:
#include
struct foo {
int e1;
char *buf;
...
};
void set_e1(foo_t f, int e1) {
f->e1 = e1;
}
int get_ei(foo_t f) { return f->e1; }
void set_buf(foo_t f, const char *buf) {
if ( f->buf ) free ( f->buf );
f->buf = strdup(buf);
}
char *get_buf_byref(foo_t f) { return f->buf; }
char *get_buf_byval(foo_t f, char **dest, size_t *dlen) {
*dlen = snprintf(*dest, (*dlen) - 1, "%s", f->buf); /* copy at most dlen-1 bytes */
return *dest;
}
如果相关结构非常复杂,甚至您可能想将函数指针实现到基本结构中,然后在该结构的特定扩展中提供实际操作。
您会发现我上面概述的方法与面向对象编程有很强的相似之处。这就是意图...
如果您保持这样的干净接口,无论您是否必须在所有地方将实例变量设置为NULL都无所谓。希望代码能够更紧密的结构,从而减少愚蠢的错误。
希望这可以帮助到您。