构造函数和析构函数
我们可以重写 lval
的构造函数,使其返回 lval
的指针,而不是其本身。这样做会使得对 lval
变量进行跟踪更加简单。为此,我们需要用到 malloc
库函数以及 sizeof
操作符为 lval
结构体在堆上申请足够大的内存区域,然后使用 ->
操作符填充结构体中的相关字段。
当我们构造一个 lval
时,它的某些指针字段可能会包含其他的在堆上申请的内存,所以我们应该小心行事。当某个 lval
完成使命之后,我们不仅需要删除它本身所指向的堆内存,还要删除它的字段所指向的堆内存。
/* Construct a pointer to a new Number lval */
lval* lval_num(long x) {
lval* v = malloc(sizeof(lval));
v->type = LVAL_NUM;
v->num = x;
return v;
}
/* Construct a pointer to a new Error lval */
lval* lval_err(char* m) {
lval* v = malloc(sizeof(lval));
v->type = LVAL_ERR;
v->err = malloc(strlen(m) + 1);
strcpy(v->err, m);
return v;
}
/* Construct a pointer to a new Symbol lval */
lval* lval_sym(char* s) {
lval* v = malloc(sizeof(lval));
v->type = LVAL_SYM;
v->sym = malloc(strlen(s) + 1);
strcpy(v->sym, s);
return v;
}
/* A pointer to a new empty Sexpr lval */
lval* lval_sexpr(void) {
lval* v = malloc(sizeof(lval));
v->type = LVAL_SEXPR;
v->count = 0;
v->cell = NULL;
return v;
}
NULL
是什么?
NULL
是一个指向内存地址 0 的特殊常量。按照惯例,它通常被用来表示空值或无数据。在上面的代码中,我们使用NULL
来表示虽然我们有一个数据指针,但它目前还没有指向任何内容。在本书的后续章节中你讲经常性地遇到这个特殊的常量,所以,请眼熟它。
为什么要使用
strlen(s) + 1
?在 C 语言中,字符串是以空字符做为终止标记的。所以,C 语言字符串的最后一个字符一定是
\0
。请确保所有的字符串都是按照这个约定来存储的,不然程序就会因为莫名其妙的错误退出。strlen
函数返回的是字符串的实际长度(所以不包括结尾的\0
终止符)。所以为了保证有足够的空间存储所有字符,我们需要在额外 +1。
现在,我们需要一个定制的函数来删除 lval*
。这个函数应该调用 free
函数来释放本身所指向的由 malloc
函数所申请的内存。但更重要的是,它应该根据自身的类型,释放所有它的字段指向的内存。所以我们只需按照上方介绍的那些构造函数对应释放相应的字段,就不会有内存的泄露了。
void lval_del(lval* v) {
switch (v->type) {
/* Do nothing special for number type */
case LVAL_NUM: break;
/* For Err or Sym free the string data */
case LVAL_ERR: free(v->err); break;
case LVAL_SYM: free(v->sym); break;
/* If Sexpr then delete all elements inside */
case LVAL_SEXPR:
for (int i = 0; i < v->count; i++) {
lval_del(v->cell[i]);
}
/* Also free the memory allocated to contain the pointers */
free(v->cell);
break;
}
/* Free the memory allocated for the "lval" struct itself */
free(v);
}
当前内容版权归 NoahDragon 译 或其关联方所有,如需对内容或内容相关联开源项目进行关注与资助,请访问 NoahDragon 译 .