删除资源时,最后编程崩溃。 C中伪泛型动态数组列表的实现
Programming crashing at the end, when deleting the resources. Implementation of pseudo-generic dynamic array list in C
我已经在 C 中为原始类型实现了一个伪泛型动态数组。它采用枚举常量作为每种类型的标识符 - INT
、LONG_INT
、DECIMAL
、CHAR
。主结构 list
有一个成员 _assets
,它是一个 void
指针(具有抽象级别)。这个 void
指针在 C 文件实现中被类型转换为另一个结构(这是容器的主要内部工作)。我测试了所有类型的列表,它工作得很好。当我创建 2 个列表,执行一些操作,然后按照创建它们的相同顺序将这两个列表一起删除时,问题就出现了。它给了我一个错误:- Critical error detected c0000374
;并说,unable to open 'free-base.cpp'
。删除通过 free_assets
方法进行。当我在使用第二个列表对象之前删除第一个列表对象时,这两个列表工作得很好,这很不寻常,因为它们是两个不同的对象。
primitive_list.h
#ifndef PRIMITIVE_LIST_H
#define PRIMITIVE_LIST_H
typedef enum { INT, LONG_INT, DECIMAL, CHAR } list_types;
typedef struct
{
void *_assets;
} list;
list *create_list(list_types type);
void push(list **self, ...);
void pop(list **self);
void*at(list *self, const int index);
int empty(list *self);
unsigned length(list *self);
void print_list(list *self);
void free_assets(list **self);
#endif
primitive_list.c
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "primitive_list.h"
typedef struct
{
int capacity;
int size;
void *arr;
list_types type;
int empty;
} _list;
_list _create_list(list_types type)
{
_list res;
res.type = type;
res.capacity = 1;
res.size = 0;
res.empty = 1;
return res;
}
void _alloc(_list *self)
{
switch ((self)->type)
{
int n = (self)->capacity;
case INT:
(self)->arr = calloc(n, sizeof(int));
break;
case LONG_INT:
(self)->arr = calloc(n, sizeof(long long));
break;
case DECIMAL:
(self)->arr = calloc(n, sizeof(double));
break;
case CHAR:
(self)->arr = calloc(n, sizeof(char));
break;
}
return;
}
void _realloc(_list *self, size_t Buffer_size)
{
(self)->capacity = Buffer_size;
list_types type = (self)->type;
int n = (self)->capacity;
int s = (self)->size;
if (type == INT) {
int *new_array = (int *)calloc(n, sizeof(int));
for (size_t i = 0; i < s; i++)
{
new_array[i] = ((int *)(self)->arr)[i];
}
free((self)->arr);
(self)->arr = (void *)new_array;
} else if (type == LONG_INT) {
long long *new_array = (long long *)calloc(n, sizeof(long long));
for (size_t i = 0; i < s; i++)
{
new_array[i] = ((long long *)(self)->arr)[i];
}
free((self)->arr);
(self)->arr = (void *)new_array;
} else if (type == DECIMAL) {
double *new_array = (double *)calloc(n, sizeof(double));
for (size_t i = 0; i < s; i++)
{
new_array[i] = ((double *)(self)->arr)[i];
}
free((self)->arr);
(self)->arr = (void *)new_array;
} else if (type == CHAR) {
char *new_array = (char *)calloc(n, sizeof(char));
for (size_t i = 0; i < s; i++)
{
new_array[i] = ((char *)(self)->arr)[i];
}
free((self)->arr);
(self)->arr = (void *)new_array;
}
return;
}
void _push(_list *self, ...)
{
if ((self)->empty)
{
(self)->empty = 0;
_alloc(self);
}
if ((self)->size == (self)->capacity)
_realloc(self, (self)->capacity * 2);
va_list arg;
va_start(arg, self);
switch ((self)->type)
{
case INT:
((int *)(self)->arr)[(self)->size] = va_arg(arg, int);
break;
case LONG_INT:
((long long *)(self)->arr)[(self)->size] = va_arg(arg, long long);
break;
case DECIMAL:
((double *)(self)->arr)[(self)->size] = va_arg(arg, double);
break;
case CHAR:
((char *)(self)->arr)[(self)->size] =(char)va_arg(arg, int);
break;
}
(self)->size++;
va_end(arg);
return;
}
void _pop(_list *self)
{
if ((self)->empty)
{
fprintf(stderr,"List is empty!\n");
return;
}
(self)->size--;
return;
}
void *_at(_list *self, const int index)
{
void *res;
switch ((self)->type)
{
case INT:
res = malloc(sizeof(int));
*((int *)res) = ((int *)(self)->arr)[index];
break;
case LONG_INT:
res = malloc(sizeof(long long));
*((long long *)res) = ((long long *)(self)->arr)[index];
break;
case DECIMAL:
res = malloc(sizeof(double));
*((double *)res) = ((double *)(self)->arr)[index];
break;
case CHAR:
res = malloc(sizeof(char));
*((char *)res) = ((char *)(self)->arr)[index];
break;
}
return res;
}
int _empty(_list *self)
{
return self->empty;
}
unsigned _length(_list *self)
{
return self->size;
}
void _print_list(_list *self)
{
for (size_t i = 0; i < self->size; i++)
{
switch(self->type)
{
case INT:
printf("%d ", ((int *)self->arr)[i]);
break;
case LONG_INT:
printf("%lld ", ((long long *)self->arr)[i]);
break;
case DECIMAL:
printf("%lf ", ((double *)self->arr)[i]);
break;
case CHAR:
printf("%c ",((char*)self->arr)[i]);
break;
}
}
printf("\n");
return;
}
void _free_list(_list *self)
{
free((self)->arr);
}
list *create_list(list_types type)
{
static list res;
_list obj = _create_list(type);
res._assets = malloc(sizeof(_list));
*((_list*)res._assets) = obj;
return &res;
}
void push(list **self, ...)
{
va_list arg;
va_start(arg, self);
switch (((_list *)(*self)->_assets)->type)
{
case INT:
_push(((_list *)(*self)->_assets), va_arg(arg, int));
break;
case LONG_INT:
_push(((_list *)(*self)->_assets), va_arg(arg, long long));
break;
case DECIMAL:
_push(((_list *)(*self)->_assets), va_arg(arg, double));
break;
case CHAR:
_push(((_list *)(*self)->_assets), (char)va_arg(arg, int));
break;
}
va_end(arg);
return;
}
void pop(list **self)
{
_pop(((_list *)(*self)->_assets));
return;
}
void *at(list *self, const int index)
{
return _at((_list *)self->_assets, index);
}
int empty(list *self)
{
return _empty((_list *)self->_assets);
}
unsigned length(list *self)
{
return _length((_list *)self->_assets);
}
void print_list(list *self)
{
_print_list((_list *)self->_assets);
return;
}
void free_assets(list **self)
{
_free_list(((_list *)(*self)->_assets));
free((*self)->_assets);
}
test.c
#include <stdio.h>
#include <stdlib.h>
#include "primitive_list.h"
int main(int argc, char **argv)
{
//Decimal List
list *nums = create_list(DECIMAL);
push(&nums, 3.14159);
push(&nums, 6.25);
push(&nums, 22.2222);
push(&nums, 100.0);
print_list(nums);
//Character list
list *chars = create_list(CHAR);
push(&chars, 'A');
push(&chars, 'w');
push(&chars, 'Z');
push(&chars, 'q');
push(&chars, 'P');
print_list(chars);
//Code causing the error
free_assets(&nums);
free_assets(&chars);
return 0;
}
你这里有问题:
list*create_list(list_types type)
{
static list res;
_list obj=_create_list(type);
res._assets=malloc(sizeof(_list));
*((_list*)res._assets)=obj;
return &res;
}
这个函数在 main()
中被调用了两次,但是 return 是一个指向静态变量的指针,这意味着它每次被调用时都会 return 相同的地址。所以当 main()
看起来创建指向两个不同列表的指针时,它们实际上是指向单个列表的指针,即 static list res
.
您的代码有点混乱,因为您似乎无缘无故地将底层结构掩埋在另一个结构中。没有理由你不能简单地(如果你想将实现细节保密)在 header 中写 typedef struct list list;
并且在你的实现中有
struct list {
int capacity;
int size;
void *arr;
list_types type;
int empty;
};
这样,处理 _assets
指针的困惑就会少很多;您可以简单地直接处理列表成员而无需取消引用和转换(这是不必要的混淆)。
这里的主要问题实际上是由于 create_list
函数的实现导致内存管理不善。具体来说,这两行:
list *create_list(list_types type)
{
static list res;
/* ... */
return &res;
}
static
关键字在这里的作用是定义一个在该函数的所有调用之间共享的全局变量。也就是说,每次调用 create_list
时,res
变量都会设置为与函数最后一次 return
时相同的值。这就是为什么你可以从这个函数 return &res
得到函数外部的有效指针:res
变量存储在程序的全局内存中而不是函数的堆栈帧中。
此程序存在(至少!)两个问题:
- 您创建的每个
list
的指针都是相同的,因此创建的每个 list
都会覆盖最后一个 list
的内存,这不仅会泄漏内存(指针_assets
结构),但仅使最后创建的列表有效。事实上,您的 nums
和 chars
列表应该具有完全相同的值 chars
is created.
- 您在未使用
malloc
分配的内存上调用 free
。因为您 return 来自 create_list
函数的 bona-fide 全局变量的地址,所以您 return 指向操作系统创建的内存的指针,当您的程序第一次运行时加载。您不拥有此内存,并且尝试对其调用 free
与您在此处看到的效果完全相同:它会使您的程序崩溃。
这两个问题都可以通过相同的方式解决:正确管理您的内存。一般来说,如果你想在 C 的内存中创建一个结构,你应该用适当的大小调用 malloc
,初始化新内存的值,然后 return 将指向它的指针返回给呼叫者。这可以在您的案例中实现为:
list *create_list(list_types type)
{
list res = malloc(sizeof(list));
_list obj=_create_list(type);
res._assets=malloc(sizeof(_list));
*((_list *)res._assets)=obj;
return res;
}
或者,如果您按照我上面的建议更改 list
的布局,只需:
list *create_list(list_types type)
{
list res = malloc(sizeof(list);
res->type = type;
res->capacity = 1;
// ...
return res;
}
您的代码中存在多个问题:
向用户隐藏实现不需要发布的间接,您可以只声明 struct list
而无需定义。
create_list
returns static
对象的地址,毫不奇怪释放它有未定义的行为,更不用说所有列表对象对象都是实际上是同一个对象。
永远不会执行 _alloc()
中 switch
块开头的行 int n = (self)->capacity;
。将此行移到 switch
语句之前。
您不应使用以 _
开头的标识符。
括号 (self)
无用且令人困惑。
这是一个简化版本:
primitive_list.h:
#ifndef PRIMITIVE_LIST_H
#define PRIMITIVE_LIST_H
typedef enum { INT, LONG_INT, DECIMAL, CHAR } list_type;
typedef struct list list;
list *list_create(list_type type);
int list_push(list *self, ...);
void list_pop(list *self);
int list_get(const list *self, int index, ...);
int list_set(list *self, int index, ...);
int list_empty(const list *self);
int list_length(const list *self);
void list_print(const char *msg, const list *self);
void list_free(list **self);
#endif
primitive_list.c:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "primitive_list.h"
struct list {
list_type type;
int capacity;
int length;
void *arr;
};
list *list_create(list_type type) {
list *res = malloc(sizeof(*res));
if (res) {
res->type = type;
res->capacity = 0;
res->length = 0;
res->arr = NULL;
} else {
fprintf(stderr, "list_create: out of memory!\n");
}
return res;
}
static int list_element_size(list_type type) {
switch (type) {
case INT: return sizeof(int);
case LONG_INT: return sizeof(long long);
case DECIMAL: return sizeof(double);
case CHAR: return sizeof(char);
}
return 0;
}
int list_push(list *self, ...) {
va_list arg;
if (self->length == self->capacity) {
if (self->capacity == 0) {
self->capacity = 1;
self->arr = calloc(self->capacity, list_element_size(self->type));
if (self->arr == NULL) {
fprintf(stderr, "list_push: out of memory!\n");
return 0;
}
} else {
size_t size = self->capacity * list_element_size(self->type);
void *new_arr = realloc(self->arr, size * 2);
if (new_arr == NULL) {
fprintf(stderr, "list_push: out of memory!\n");
return 0;
}
memset((char *)new_arr + size, 0, size);
self->arr = new_arr;
self->capacity *= 2;
}
}
va_start(arg, self);
switch (self->type) {
case INT:
((int *)self->arr)[self->length++] = va_arg(arg, int);
break;
case LONG_INT:
((long long *)self->arr)[self->length++] = va_arg(arg, long long);
break;
case DECIMAL:
((double *)self->arr)[self->length++] = va_arg(arg, double);
break;
case CHAR:
((char *)self->arr)[self->length++] =(char)va_arg(arg, int);
break;
}
va_end(arg);
return self->length;
}
void list_pop(list *self) {
if (self->length == 0) {
fprintf(stderr, "list_pop: list is empty!\n");
} else {
self->length--;
}
}
int list_get(const list *self, int index, ...) {
int rc = 0;
if (index >= 0 && index < self->length) {
va_list arg;
va_start(arg, index);
switch (self->type) {
case INT:
*va_arg(arg, int *) = ((int *)self->arr)[index];
rc = 1;
break;
case LONG_INT:
*va_arg(arg, long long *) = ((long long *)self->arr)[index];
rc = 1;
break;
case DECIMAL:
*va_arg(arg, double *) = ((double *)self->arr)[index];
rc = 1;
break;
case CHAR:
*va_arg(arg, char *) = ((char *)self->arr)[index];
rc = 1;
break;
}
va_end(arg);
}
return rc;
}
int list_set(list *self, int index, ...) {
int rc = 0;
if (index >= 0 && index < self->length) {
va_list arg;
va_start(arg, index);
switch (self->type) {
case INT:
((int *)self->arr)[index] = va_arg(arg, int);
rc = 1;
break;
case LONG_INT:
((long long *)self->arr)[index] = va_arg(arg, long long);
rc = 1;
break;
case DECIMAL:
((double *)self->arr)[index] = va_arg(arg, double);
rc = 1;
break;
case CHAR:
((char *)self->arr)[index] = (char)va_arg(arg, int);
rc = 1;
break;
}
va_end(arg);
}
return rc;
}
int list_empty(const list *self) {
return self->length == 0;
}
int list_length(const list *self) {
return self->length;
}
void list_print(const char *msg, const list *self) {
if (msg) {
printf("%s: ", msg);
}
for (int i = 0; i < self->length; i++) {
switch (self->type) {
case INT:
printf("%d ", ((int *)self->arr)[i]);
break;
case LONG_INT:
printf("%lld ", ((long long *)self->arr)[i]);
break;
case DECIMAL:
printf("%f ", ((double *)self->arr)[i]);
break;
case CHAR:
printf("%c ", ((char *)self->arr)[i]);
break;
}
}
printf("\n");
}
void list_free(list **self) {
if (*self) {
free((*self)->arr);
free(*self);
*self = NULL;
}
}
test.c:
#include <stdio.h>
#include <stdlib.h>
#include "primitive_list.h"
int main(int argc, char **argv) {
//Decimal List
list *nums = list_create(DECIMAL);
list_push(nums, 3.14159);
list_push(nums, 6.25);
list_push(nums, 22.2222);
list_push(nums, 100.0);
//Character list
list *chars = list_create(CHAR);
list_push(chars, 'A');
list_push(chars, 'w');
list_push(chars, 'Z');
list_push(chars, 'q');
list_push(chars, 'P');
// print the lists
list_print("nums", nums);
list_print("chars", chars);
double x;
char c;
if (list_get(nums, 2, &x))
printf("nums[2] = %f\n", x);
if (list_get(chars, 2, &c))
printf("chars[2] = '%c'\n", c);
if (list_set(nums, 3, 123.45))
printf("set nums[3] to %f\n", 123.45);
if (list_set(chars, 3, '*'))
printf("set chars[3] to '%c'\n", '*');
// print the lists
list_print("nums", nums);
list_print("chars", chars);
// free the lists
list_free(&nums);
list_free(&chars);
return 0;
}
输出:
nums: 3.141590 6.250000 22.222200 100.000000
chars: A w Z q P
nums[2] = 22.222200
chars[2] = 'Z'
set nums[3] to 123.450000
set chars[3] to '*'
nums: 3.141590 6.250000 22.222200 123.450000
chars: A w Z * P
我已经在 C 中为原始类型实现了一个伪泛型动态数组。它采用枚举常量作为每种类型的标识符 - INT
、LONG_INT
、DECIMAL
、CHAR
。主结构 list
有一个成员 _assets
,它是一个 void
指针(具有抽象级别)。这个 void
指针在 C 文件实现中被类型转换为另一个结构(这是容器的主要内部工作)。我测试了所有类型的列表,它工作得很好。当我创建 2 个列表,执行一些操作,然后按照创建它们的相同顺序将这两个列表一起删除时,问题就出现了。它给了我一个错误:- Critical error detected c0000374
;并说,unable to open 'free-base.cpp'
。删除通过 free_assets
方法进行。当我在使用第二个列表对象之前删除第一个列表对象时,这两个列表工作得很好,这很不寻常,因为它们是两个不同的对象。
primitive_list.h
#ifndef PRIMITIVE_LIST_H
#define PRIMITIVE_LIST_H
typedef enum { INT, LONG_INT, DECIMAL, CHAR } list_types;
typedef struct
{
void *_assets;
} list;
list *create_list(list_types type);
void push(list **self, ...);
void pop(list **self);
void*at(list *self, const int index);
int empty(list *self);
unsigned length(list *self);
void print_list(list *self);
void free_assets(list **self);
#endif
primitive_list.c
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "primitive_list.h"
typedef struct
{
int capacity;
int size;
void *arr;
list_types type;
int empty;
} _list;
_list _create_list(list_types type)
{
_list res;
res.type = type;
res.capacity = 1;
res.size = 0;
res.empty = 1;
return res;
}
void _alloc(_list *self)
{
switch ((self)->type)
{
int n = (self)->capacity;
case INT:
(self)->arr = calloc(n, sizeof(int));
break;
case LONG_INT:
(self)->arr = calloc(n, sizeof(long long));
break;
case DECIMAL:
(self)->arr = calloc(n, sizeof(double));
break;
case CHAR:
(self)->arr = calloc(n, sizeof(char));
break;
}
return;
}
void _realloc(_list *self, size_t Buffer_size)
{
(self)->capacity = Buffer_size;
list_types type = (self)->type;
int n = (self)->capacity;
int s = (self)->size;
if (type == INT) {
int *new_array = (int *)calloc(n, sizeof(int));
for (size_t i = 0; i < s; i++)
{
new_array[i] = ((int *)(self)->arr)[i];
}
free((self)->arr);
(self)->arr = (void *)new_array;
} else if (type == LONG_INT) {
long long *new_array = (long long *)calloc(n, sizeof(long long));
for (size_t i = 0; i < s; i++)
{
new_array[i] = ((long long *)(self)->arr)[i];
}
free((self)->arr);
(self)->arr = (void *)new_array;
} else if (type == DECIMAL) {
double *new_array = (double *)calloc(n, sizeof(double));
for (size_t i = 0; i < s; i++)
{
new_array[i] = ((double *)(self)->arr)[i];
}
free((self)->arr);
(self)->arr = (void *)new_array;
} else if (type == CHAR) {
char *new_array = (char *)calloc(n, sizeof(char));
for (size_t i = 0; i < s; i++)
{
new_array[i] = ((char *)(self)->arr)[i];
}
free((self)->arr);
(self)->arr = (void *)new_array;
}
return;
}
void _push(_list *self, ...)
{
if ((self)->empty)
{
(self)->empty = 0;
_alloc(self);
}
if ((self)->size == (self)->capacity)
_realloc(self, (self)->capacity * 2);
va_list arg;
va_start(arg, self);
switch ((self)->type)
{
case INT:
((int *)(self)->arr)[(self)->size] = va_arg(arg, int);
break;
case LONG_INT:
((long long *)(self)->arr)[(self)->size] = va_arg(arg, long long);
break;
case DECIMAL:
((double *)(self)->arr)[(self)->size] = va_arg(arg, double);
break;
case CHAR:
((char *)(self)->arr)[(self)->size] =(char)va_arg(arg, int);
break;
}
(self)->size++;
va_end(arg);
return;
}
void _pop(_list *self)
{
if ((self)->empty)
{
fprintf(stderr,"List is empty!\n");
return;
}
(self)->size--;
return;
}
void *_at(_list *self, const int index)
{
void *res;
switch ((self)->type)
{
case INT:
res = malloc(sizeof(int));
*((int *)res) = ((int *)(self)->arr)[index];
break;
case LONG_INT:
res = malloc(sizeof(long long));
*((long long *)res) = ((long long *)(self)->arr)[index];
break;
case DECIMAL:
res = malloc(sizeof(double));
*((double *)res) = ((double *)(self)->arr)[index];
break;
case CHAR:
res = malloc(sizeof(char));
*((char *)res) = ((char *)(self)->arr)[index];
break;
}
return res;
}
int _empty(_list *self)
{
return self->empty;
}
unsigned _length(_list *self)
{
return self->size;
}
void _print_list(_list *self)
{
for (size_t i = 0; i < self->size; i++)
{
switch(self->type)
{
case INT:
printf("%d ", ((int *)self->arr)[i]);
break;
case LONG_INT:
printf("%lld ", ((long long *)self->arr)[i]);
break;
case DECIMAL:
printf("%lf ", ((double *)self->arr)[i]);
break;
case CHAR:
printf("%c ",((char*)self->arr)[i]);
break;
}
}
printf("\n");
return;
}
void _free_list(_list *self)
{
free((self)->arr);
}
list *create_list(list_types type)
{
static list res;
_list obj = _create_list(type);
res._assets = malloc(sizeof(_list));
*((_list*)res._assets) = obj;
return &res;
}
void push(list **self, ...)
{
va_list arg;
va_start(arg, self);
switch (((_list *)(*self)->_assets)->type)
{
case INT:
_push(((_list *)(*self)->_assets), va_arg(arg, int));
break;
case LONG_INT:
_push(((_list *)(*self)->_assets), va_arg(arg, long long));
break;
case DECIMAL:
_push(((_list *)(*self)->_assets), va_arg(arg, double));
break;
case CHAR:
_push(((_list *)(*self)->_assets), (char)va_arg(arg, int));
break;
}
va_end(arg);
return;
}
void pop(list **self)
{
_pop(((_list *)(*self)->_assets));
return;
}
void *at(list *self, const int index)
{
return _at((_list *)self->_assets, index);
}
int empty(list *self)
{
return _empty((_list *)self->_assets);
}
unsigned length(list *self)
{
return _length((_list *)self->_assets);
}
void print_list(list *self)
{
_print_list((_list *)self->_assets);
return;
}
void free_assets(list **self)
{
_free_list(((_list *)(*self)->_assets));
free((*self)->_assets);
}
test.c
#include <stdio.h>
#include <stdlib.h>
#include "primitive_list.h"
int main(int argc, char **argv)
{
//Decimal List
list *nums = create_list(DECIMAL);
push(&nums, 3.14159);
push(&nums, 6.25);
push(&nums, 22.2222);
push(&nums, 100.0);
print_list(nums);
//Character list
list *chars = create_list(CHAR);
push(&chars, 'A');
push(&chars, 'w');
push(&chars, 'Z');
push(&chars, 'q');
push(&chars, 'P');
print_list(chars);
//Code causing the error
free_assets(&nums);
free_assets(&chars);
return 0;
}
你这里有问题:
list*create_list(list_types type)
{
static list res;
_list obj=_create_list(type);
res._assets=malloc(sizeof(_list));
*((_list*)res._assets)=obj;
return &res;
}
这个函数在 main()
中被调用了两次,但是 return 是一个指向静态变量的指针,这意味着它每次被调用时都会 return 相同的地址。所以当 main()
看起来创建指向两个不同列表的指针时,它们实际上是指向单个列表的指针,即 static list res
.
您的代码有点混乱,因为您似乎无缘无故地将底层结构掩埋在另一个结构中。没有理由你不能简单地(如果你想将实现细节保密)在 header 中写 typedef struct list list;
并且在你的实现中有
struct list {
int capacity;
int size;
void *arr;
list_types type;
int empty;
};
这样,处理 _assets
指针的困惑就会少很多;您可以简单地直接处理列表成员而无需取消引用和转换(这是不必要的混淆)。
这里的主要问题实际上是由于 create_list
函数的实现导致内存管理不善。具体来说,这两行:
list *create_list(list_types type)
{
static list res;
/* ... */
return &res;
}
static
关键字在这里的作用是定义一个在该函数的所有调用之间共享的全局变量。也就是说,每次调用 create_list
时,res
变量都会设置为与函数最后一次 return
时相同的值。这就是为什么你可以从这个函数 return &res
得到函数外部的有效指针:res
变量存储在程序的全局内存中而不是函数的堆栈帧中。
此程序存在(至少!)两个问题:
- 您创建的每个
list
的指针都是相同的,因此创建的每个list
都会覆盖最后一个list
的内存,这不仅会泄漏内存(指针_assets
结构),但仅使最后创建的列表有效。事实上,您的nums
和chars
列表应该具有完全相同的值chars
is created. - 您在未使用
malloc
分配的内存上调用free
。因为您 return 来自create_list
函数的 bona-fide 全局变量的地址,所以您 return 指向操作系统创建的内存的指针,当您的程序第一次运行时加载。您不拥有此内存,并且尝试对其调用free
与您在此处看到的效果完全相同:它会使您的程序崩溃。
这两个问题都可以通过相同的方式解决:正确管理您的内存。一般来说,如果你想在 C 的内存中创建一个结构,你应该用适当的大小调用 malloc
,初始化新内存的值,然后 return 将指向它的指针返回给呼叫者。这可以在您的案例中实现为:
list *create_list(list_types type)
{
list res = malloc(sizeof(list));
_list obj=_create_list(type);
res._assets=malloc(sizeof(_list));
*((_list *)res._assets)=obj;
return res;
}
或者,如果您按照我上面的建议更改 list
的布局,只需:
list *create_list(list_types type)
{
list res = malloc(sizeof(list);
res->type = type;
res->capacity = 1;
// ...
return res;
}
您的代码中存在多个问题:
向用户隐藏实现不需要发布的间接,您可以只声明
struct list
而无需定义。create_list
returnsstatic
对象的地址,毫不奇怪释放它有未定义的行为,更不用说所有列表对象对象都是实际上是同一个对象。永远不会执行
_alloc()
中switch
块开头的行int n = (self)->capacity;
。将此行移到switch
语句之前。您不应使用以
_
开头的标识符。括号
(self)
无用且令人困惑。
这是一个简化版本:
primitive_list.h:
#ifndef PRIMITIVE_LIST_H
#define PRIMITIVE_LIST_H
typedef enum { INT, LONG_INT, DECIMAL, CHAR } list_type;
typedef struct list list;
list *list_create(list_type type);
int list_push(list *self, ...);
void list_pop(list *self);
int list_get(const list *self, int index, ...);
int list_set(list *self, int index, ...);
int list_empty(const list *self);
int list_length(const list *self);
void list_print(const char *msg, const list *self);
void list_free(list **self);
#endif
primitive_list.c:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "primitive_list.h"
struct list {
list_type type;
int capacity;
int length;
void *arr;
};
list *list_create(list_type type) {
list *res = malloc(sizeof(*res));
if (res) {
res->type = type;
res->capacity = 0;
res->length = 0;
res->arr = NULL;
} else {
fprintf(stderr, "list_create: out of memory!\n");
}
return res;
}
static int list_element_size(list_type type) {
switch (type) {
case INT: return sizeof(int);
case LONG_INT: return sizeof(long long);
case DECIMAL: return sizeof(double);
case CHAR: return sizeof(char);
}
return 0;
}
int list_push(list *self, ...) {
va_list arg;
if (self->length == self->capacity) {
if (self->capacity == 0) {
self->capacity = 1;
self->arr = calloc(self->capacity, list_element_size(self->type));
if (self->arr == NULL) {
fprintf(stderr, "list_push: out of memory!\n");
return 0;
}
} else {
size_t size = self->capacity * list_element_size(self->type);
void *new_arr = realloc(self->arr, size * 2);
if (new_arr == NULL) {
fprintf(stderr, "list_push: out of memory!\n");
return 0;
}
memset((char *)new_arr + size, 0, size);
self->arr = new_arr;
self->capacity *= 2;
}
}
va_start(arg, self);
switch (self->type) {
case INT:
((int *)self->arr)[self->length++] = va_arg(arg, int);
break;
case LONG_INT:
((long long *)self->arr)[self->length++] = va_arg(arg, long long);
break;
case DECIMAL:
((double *)self->arr)[self->length++] = va_arg(arg, double);
break;
case CHAR:
((char *)self->arr)[self->length++] =(char)va_arg(arg, int);
break;
}
va_end(arg);
return self->length;
}
void list_pop(list *self) {
if (self->length == 0) {
fprintf(stderr, "list_pop: list is empty!\n");
} else {
self->length--;
}
}
int list_get(const list *self, int index, ...) {
int rc = 0;
if (index >= 0 && index < self->length) {
va_list arg;
va_start(arg, index);
switch (self->type) {
case INT:
*va_arg(arg, int *) = ((int *)self->arr)[index];
rc = 1;
break;
case LONG_INT:
*va_arg(arg, long long *) = ((long long *)self->arr)[index];
rc = 1;
break;
case DECIMAL:
*va_arg(arg, double *) = ((double *)self->arr)[index];
rc = 1;
break;
case CHAR:
*va_arg(arg, char *) = ((char *)self->arr)[index];
rc = 1;
break;
}
va_end(arg);
}
return rc;
}
int list_set(list *self, int index, ...) {
int rc = 0;
if (index >= 0 && index < self->length) {
va_list arg;
va_start(arg, index);
switch (self->type) {
case INT:
((int *)self->arr)[index] = va_arg(arg, int);
rc = 1;
break;
case LONG_INT:
((long long *)self->arr)[index] = va_arg(arg, long long);
rc = 1;
break;
case DECIMAL:
((double *)self->arr)[index] = va_arg(arg, double);
rc = 1;
break;
case CHAR:
((char *)self->arr)[index] = (char)va_arg(arg, int);
rc = 1;
break;
}
va_end(arg);
}
return rc;
}
int list_empty(const list *self) {
return self->length == 0;
}
int list_length(const list *self) {
return self->length;
}
void list_print(const char *msg, const list *self) {
if (msg) {
printf("%s: ", msg);
}
for (int i = 0; i < self->length; i++) {
switch (self->type) {
case INT:
printf("%d ", ((int *)self->arr)[i]);
break;
case LONG_INT:
printf("%lld ", ((long long *)self->arr)[i]);
break;
case DECIMAL:
printf("%f ", ((double *)self->arr)[i]);
break;
case CHAR:
printf("%c ", ((char *)self->arr)[i]);
break;
}
}
printf("\n");
}
void list_free(list **self) {
if (*self) {
free((*self)->arr);
free(*self);
*self = NULL;
}
}
test.c:
#include <stdio.h>
#include <stdlib.h>
#include "primitive_list.h"
int main(int argc, char **argv) {
//Decimal List
list *nums = list_create(DECIMAL);
list_push(nums, 3.14159);
list_push(nums, 6.25);
list_push(nums, 22.2222);
list_push(nums, 100.0);
//Character list
list *chars = list_create(CHAR);
list_push(chars, 'A');
list_push(chars, 'w');
list_push(chars, 'Z');
list_push(chars, 'q');
list_push(chars, 'P');
// print the lists
list_print("nums", nums);
list_print("chars", chars);
double x;
char c;
if (list_get(nums, 2, &x))
printf("nums[2] = %f\n", x);
if (list_get(chars, 2, &c))
printf("chars[2] = '%c'\n", c);
if (list_set(nums, 3, 123.45))
printf("set nums[3] to %f\n", 123.45);
if (list_set(chars, 3, '*'))
printf("set chars[3] to '%c'\n", '*');
// print the lists
list_print("nums", nums);
list_print("chars", chars);
// free the lists
list_free(&nums);
list_free(&chars);
return 0;
}
输出:
nums: 3.141590 6.250000 22.222200 100.000000
chars: A w Z q P
nums[2] = 22.222200
chars[2] = 'Z'
set nums[3] to 123.450000
set chars[3] to '*'
nums: 3.141590 6.250000 22.222200 123.450000
chars: A w Z * P