我正在研究一系列 C 函数以允许用户动态构建数组。该库的核心位于Array
struct
其中包含一个包含数组数据的指针变量array
,len
其中包含数组的长度size
,它是数组的总内存分配elem
,它包含每个索引的内存分配,以及指针变量name
其中dtype
包含描述数组名称和数组类型的字符串。目前我已经限制了范围,因此只能考虑int
、float
、double
和char
数组。
到目前为止,我已经定义并单独测试了以下功能;
array_mem_alloc
其中包含为数组分配内存的代码。
init_array
这是一个包装器,array_mem_alloc
它实例化一个Array
struct
,确定数据类型并将数据类型返回Array
给用户。
append_array
它允许用户一次动态地增长一个数组,或者添加一个已经定义的数组。
free_array
释放所有内存并重置struct
变量
int_array_val
它在索引处对数据进行类型转换并返回给用户。我有所有相关数据类型的这个函数的版本,但是对于这个问题我只会使用这个版本。
find_int_array_indices
它查找特定整数在数组中存在的位置,并将索引号记录到另一个数组中,该数组返回给用户。
出于测试的目的,find_int_array_indices
我调用init_array
了一个名为的变量arr_test
并将其附加 7 个整数int a[7] = {6, 1, 3, 6, 6, 4, 5}
。我将Array
容器传递arr_test
给find_int_array_indices
函数,一切正常,它还返回另一个Array
名为p
. 但是,当我尝试使用该int_array_val
函数检索整数变量时,它失败了,因为它无法将变量识别array->dtype
为包含 string "int"
。但是,当我在 main 函数内部和内部测试容器时find_int_array_indices
,变量确实包含 string "int"
。这告诉我我可能有一个指针错误,但我没有看到它。任何建议都会非常有用。我想知道我是否需要回到开始并定义name
并dtype
作为固定长度数组Array
struct
而不是指针变量。
数组.h
typedef struct
{
void *array; // Pointer to array
size_t len; // Active length of array
size_t size; // Number of allocated indizes
int elem; // Memory consumption per indice
char *name; // The array name
char *dtype; // A string representing the datatype
} Array;
void array_mem_alloc(Array *array, size_t num_indices);
Array init_array(char *dtype, size_t num_indices, char *name);
int append_array(Array *array, void *elements, size_t count);
void free_array(Array *array);
int int_array_val(Array *array, int indice);
Array find_int_array_indices(Array *array, int integer);
数组.c
void array_mem_alloc(Array *array, size_t num_indices) {
// Determine the total memory allocation and assign to pointer
void *pointer;
pointer = malloc(num_indices * array->elem);
// If memory is full fail gracefully
if (pointer == NULL) {
printf("Unable to allocate memory, exiting.\n");
free(pointer);
exit(0);
}
// Allocate resources and instantiate Array
else {
array->array = pointer;
array->len = 0;
array->size = num_indices;
}
}
// --------------------------------------------------------------------------------
Array init_array(char *dtype, size_t num_indices, char *name) {
// Determine memory blocks based on data type
int size;
if (strcmp(dtype, "float") == 0) size = sizeof(float);
else if (strcmp(dtype, "int") == 0) size = sizeof(int);
else if (strcmp(dtype, "double") == 0) size = sizeof(double);
else if (strcmp(dtype, "char") == 0) size = sizeof(char);
else {
printf("Data type not correctly entered into init_array, exiting program!\n");
exit(0);
}
// Allocate indice size and call array_mem_alloc
Array array;
array.dtype = dtype;
array.elem = size;
array_mem_alloc(&array, num_indices);
array.name = name;
return array;
}
// --------------------------------------------------------------------------------
int append_array(Array *array, void *elements, size_t count) {
// Allocae more memory if necessary
if (array->len + count > array->size) {
size_t size = (array->len + count) * 2;
void *pointer = realloc(array->array, size * array->elem);
// If memory is full return operations
if (pointer == NULL) {
printf("Unable to allocate memory, exiting.\n");
return 0;
}
// Allocate memory to variables and increment array size
array->array = pointer;
array->size = size;
}
// Append variables and increment the array length
memcpy((char *)array->array + array->len * array->elem, elements, count * array->elem);
array->len += count;
return 1;
}
// --------------------------------------------------------------------------------
void free_array(Array *array) {
// Free all memory in the array
free(array->array);
// Reset all variables in the struct
array->array = NULL;
array->size = 0;
array->len = 0;
array->elem = 0;
}
// --------------------------------------------------------------------------------
int int_array_val(Array *array, int indice) {
// Ensure array contains integers
printf("%s\n", array->dtype);
if (strcmp(array->dtype, "int") != 0) {
printf("Function can only return integer values, exiting function!\n");
exit(0);
}
// Cast value to an integer and return
int a = ((int *)array->array)[indice];
return a;
}
Array find_int_array_indices(Array *array, int integer) {
int number = 0;
int input;
for (int i = 0; i < array->len; i++) {
if (integer == int_array_val(array, i)) {
number++;
}
}
char dtype[7] = "int";
char name[9] = "indices";
Array indice_arr = init_array(dtype, number, name);
for (int i = 0; i < array->len; i++) {
input = i;
if (integer == int_array_val(array, i)) {
append_array(&indice_arr, &input, 1);
}
}
return indice_arr;
}
主程序
size_t indices = 10;
char name[6] = "array";
char dtype[7] = "int";
Array arr_test = init_array(dtype, indices, name);
int a[7] = {6, 1, 3, 6, 6, 4, 5};
append_array(&arr_test, a, 7);
Array p = find_int_array_indices(&arr_test, 6);
printf("%s\n", p.dtype); // This shows that p does contain dtype "int"
int d = int_array_val(&p, 0); // This fails in function, because it does not see dtype = "int"???
printf("%d\n", d);
在find_int_array_indices
char dtype[7] = "int";
char name[9] = "indices";
都是局部变量,在函数返回时失效。请参阅:悬空指针和Lifetime。
init_array
使用这些值,就好像它们有一个生命周期来匹配它的返回值
Array array;
array.dtype = dtype;
array.elem = size;
array_mem_alloc(&array, num_indices);
array.name = name;
return array;
作为结构类型,它的生命周期由调用者的上下文决定(毕竟返回是副本)。
find_int_array_indices
indice_arr
返回时完成错误main
。
一些选项:
一个相当冗长的延续,详细说明使用枚举类型:
这个想法是定义一组较小的可接受值,您的库可以使用这些值,并让用户更加了解这些值。正如我们所看到的,您已经部分使用字符串完成了这项工作,但实现存在一些问题,因为字符串通常很笨重。字符串的一些问题:
因此,我们不使用字符串(在这些示例中为"foo"
, "bar
" "qux"
),而是使用枚举类型
enum OBJECT_TYPE {
OBJECT_FOO,
OBJECT_BAR,
OBJECT_QUX
};
它确立了以下内容:
然后结构定义看起来像
typedef struct {
/* ... whatever members are needed for the structure */
size_t something_based_on_type;
enum OBJECT_TYPE type;
char debug_name[MAX_DEBUG_NAME];
} Object;
对结构的名称成员实际上无能为力。如果您想要用户定义的名称标签,那么是的,如前所述,您需要为它们分配空间。
我们的初始化函数的工作方式类似,但我们可以2利用整数类型的一些属性。
void object_init(Object *object, enum OBJECT_TYPE type, const char *debug_name) {
/* ... accept other arguments, whatever is needed to initialize */
size_t value_translations[] = { 42, 51, 99 };
object->type = type;
/* while neat, this is somewhat naive, see footnotes */
object->something_based_on_type = value_translations[type];
if (debug_name && strlen(debug_name) < MAX_DEBUG_NAME)
strcpy(object->debug_name, debug_name);
else
*object->debug_name = '\0';
}
现在我们想提供一个函数来处理我们只有类型的通用数据OBJECT_FOO
(比如你的int_array_val
)。同样,比较更容易理解。
void object_print_foo(Object *o) {
if (OBJECT_FOO != o->type)
/* handle type mismatch */;
}
尽管最好提供一个object_print
基于o->type
.
完整性的主要功能:
int main(void) {
Object a;
object_init(&a, OBJECT_QUX, "object_a");
object_print_foo(&a);
}
这是使用枚举类型的一般思想。
尽管如此,我认为这并没有比仅仅处理任意数据大小更好,包括风险。就像是
const void *array_get(Array *array, size_t index) {
if (index >= array->length)
return NULL;
return (char *) array->array + index * array->elem;
}
如果用户遵守const
合同并使用正确的类型(他们也需要记住他们使用特定类型的 getter 的类型),则可以工作。
无论如何,C 中的通用数据结构有点信服。
1.所以exit
从库代码中注意:不要。作为库作者,您没有合理的权利导致用户程序终止(除非请求,或者用户在您的控制之外调用 UB)。向上委托,返回错误,并让用户按照自己的条件退出程序,因为他们可能需要执行自己的清理工作(或者如果故障不严重,可能会继续进行)。
2. C的枚举类型比较弱。enum
实际上只是int
,并且用户可以输入指定范围之外的纯整数值。从库的角度来看,这类似于调用未定义的行为,但无论如何我们可能希望保护用户。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句