如何设计用ANSI C编写的库?

时间:2011-09-28 23:01:53

标签: c malloc

我想用ANSI C开发一个库。

我有string struct

struct libme_string
{
  char* buffer;
  int length;
};

我想编写一个函数libme_create_string(),它创建并初始化string(就像C ++中的构造函数一样)。

哪种方法更适合设计libme_create_string()


方法#1

libme_create_string()中为字符串对象分配内存并将其返回:

struct libme_string* libme_create_string(int length)
{
  // Check arguments...

  // Allocate memory for object.
  struct libme_string* str = malloc(sizeof(struct libme_string));

  // Handle memory allocation errors...

  str->buffer = malloc(length);
  str->length = length;

  // Handle memory allocation errors...

  return str;
}

void libme_delete_string(struct libme_string* str)
{
    // Check arguments...

    free(str->buffer);
    free(str);
}

使用

struct libme_string* str;
str = libme_create_string(1024);

// ...

libme_delete_string(str);
str = NULL;

方法#2

不要在libme_create_string()函数中为字符串对象分配内存,将其作为参数接受:

struct void libme_create_string(libme_string* str, int length)
{
  // Check arguments...

  // Just allocate memory for members.
  str->buffer = malloc(length);
  str->length = length;

  // Handle memory allocation errors...
}

void libme_delete_string(struct libme_string* str)
{
  // Check arguments...

  free(str->buffer);
}

使用

struct libme_string str; // << different, not a pointer!
libme_create_string(&str, 1024);

// ...

libme_delete_string(&str);

备注

  • string只是一个样本。
  • 方法#2 更快,不是吗?

最后,有没有很好的设计指南来设计用C语言编写的库?

3 个答案:

答案 0 :(得分:4)

就个人而言,我认为第二个版本不太直观且容易出错。

如果你最努力地封装实例化(无论如何你应该这样做),那么第一个真正是唯一的方法 - 一步,完成。第二个版本意味着为了拥有一个完全初始化的变量,您不仅需要实例化它,还需要立即调用它上面的辅助函数。额外的步骤是等待发生的错误。

答案 1 :(得分:3)

我个人更喜欢第一种方法。同意:它有点像C ++,但是......

thing_t *thing_new(...);
void thing_delete(thing_t *ptr);

我认为所有“size”或“count”成员都应该是无符号的,最好是size_t。 另外:你最后一段代码试图释放()一个自动变量。这是不使用它的一个很好的理由。

编辑:

有(至少)第三种方式:将整个对象作为值返回。我并不特别喜欢这种方法,但它至少避免了双重分配。它是这样的:

typedef struct {
  StrLen length;
  StrType type;      /* type is not stored in the brainfile 
                     **but recomputed on   loading */
  char *word;
} STRING;

STATIC STRING new_string(char *str, size_t len)
{
STRING this;

if (str) {
     if (!len) len = strlen(str);
     if (len) { this.word = malloc(len); memcpy(this.word, str, len); }
     else { this.word = malloc(1); memset(this.word, 0, 1); }
     this.length = len;
     this.type = word_classify(this);
     }
else        {
     this.word = NULL;
     this.length = 0;
     this.type = 0;
     }
return this;
}

典型用法如下:

if (*np == WORD_NIL) {
  STRING this;
  *np = dict->size++;
  this = new_string(word.word, word.length);
  dict->entry[*np].string = this;
  dict->entry[*np].hash = hash_word(this);
  }

(继承自megahal的代码,在wakkerbot中重用) 正如我所说,我不喜欢这种方法,但结构分配肯定有其优点。

答案 2 :(得分:2)

为什么不将过程分为两个功能,因此您可以根据需要使用:

struct libme_string * create_string();
void destroy_string(struct libme_string *);

struct libme_string * init_string(struct libme_string * str, unsigned int length);
struct limbe_string * deinit_string(struct libme_string * str);

用法#1,所有动态分配:

struct libme_string * str = init_string(create_string(), 10);
destroy_string(deinit_string(str));

用法#2,自动外部结构:

struct libme_string str;
init_string(&str);
deinit_string(&str);

确保init函数返回指针,以便您像我一样编写调用。

如果deinit()也将指针设置为零,那么如果指针非零,则可以使destroy()调用deinit(),尽管这会破坏对称性。