将数据分配给 C 中的 typedef 结构时出现指针错误
Pointer error when assigning data to a typedef struct in C
我正在研究一系列 C 函数,以允许用户动态构建数组。库的核心驻留在Array
struct
其中包含一个指针变量array
包含数组数据,len
包含数组的长度,size
,这是数组的总内存分配,elem
,其中包含每个索引的内存分配,以及指针变量 name
和 dtype
,其中包含描述数组名称的字符串和数组的类型。目前我已经限制了范围,因此只能考虑 int
、float
、double
和 char
数组。
到目前为止,我已经定义并单独测试了以下功能;
array_mem_alloc
其中包含为数组分配内存的代码。
init_array
是 array_mem_alloc
的包装器,实例化 Array
struct
,确定数据类型,returns 一个Array
用户的数据类型。
append_array
允许用户一次动态增长数组一个索引,或者添加一个已经定义的数组。
free_array
释放所有内存并重置 struct
变量
int_array_val
将索引处的数据和 returns 类型转换为用户。我有适用于所有相关数据类型的此函数的版本,但对于这个问题我只会使用此版本。
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
函数,一切正常,这也是 returns 另一个名为 p
的 Array
容器。但是,当我尝试使用 int_array_val
函数检索整数变量时,它失败了,因为它无法将变量 array->dtype
识别为包含字符串 "int"
。但是,当我在 find_int_array_indices
和主函数中测试容器时,变量确实包含字符串 "int"
。这告诉我,我可能有一个指针错误,但我没有看到它。任何建议都会非常有用。我想知道我是否需要回到开头并将 name
和 dtype
定义为 Array
struct
中的固定长度数组而不是指针变量。
array.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);
array.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;
}
main.c
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";
都是局部变量,在函数return时失效。参见:Dangling pointer and Lifetime.
init_array
使用这些值就好像它们有生命周期来匹配它的 return 值
Array array;
array.dtype = dtype;
array.elem = size;
array_mem_alloc(&array, num_indices);
array.name = name;
return array;
作为结构类型,其生命周期由其调用者的上下文决定(return 毕竟是复制)。
find_int_array_indices
在returnsindice_arr
到main
.
时完成错误
部分选项:
- 严格使用指向带 static storage duration 的字符串的指针。
- 更改您的结构定义以包含这些字符串的 space(或分配它),并执行字符串复制。
- 改用 enumerated type。
- 放弃这个 string-based,通过通用地支持所有内存大小(尽管命名功能仍然是一个问题)将类型受限的范例放在一起。
一个相当long-winded的延续,详细说明如何使用enumerated types:
我们的想法是定义一组较小的图书馆可以使用的可接受值,并让用户更加了解这些值。正如我们所看到的,您已经使用字符串部分完成了该操作,但实现存在一些问题,因为字符串通常很笨重。字符串的一些问题:
- 您无法控制您图书馆的用户使用的字符串(这导致您必须退出1程序用户输入意外的事件,这很容易做到),
- 您必须考虑到它们潜在的大量或过多的内存消耗,
- 字符串比较复杂度为O(N),
- 字符串在 C 中通常是不安全的,在处理它们(赋值、比较、存储)时需要比其他基本结构更加小心。
因此,我们不使用字符串(在这些示例中为 "foo"
、"bar
”、"qux"
),而是使用枚举类型
enum OBJECT_TYPE {
OBJECT_FOO,
OBJECT_BAR,
OBJECT_QUX
};
它建立了以下内容:
- 更清楚可接受的值是什么
- 一些2 通过类型提示控制用户输入的内容
- 比较是O(1)
- 处理与任何整数类型相同
结构定义看起来像
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;
对于结构的名称成员,真的无能为力。如果您想要用户定义的名称标签,那么是的,如前所述,您需要为它们分配 space。
我们的初始化函数的工作方式类似,但我们可以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 = '[=14=]';
}
现在我们想提供一个函数,它只处理我们类型为 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
ing 来自图书馆代码的注释:不要。作为库作者,您没有合理的权利导致用户程序终止(除非请求,或者用户在您的控制之外调用 UB)。向上委托,return 错误,并让用户按自己的方式退出程序,因为他们可能需要执行自己的清理(或者如果失败是 non-critical,则可能会继续)。
2. C的枚举类型比较弱。 enum
实际上就是 int
,用户 可以 输入超出指定范围的普通整数值。这类似于从图书馆的角度调用 undefined behavior,但无论如何我们可能希望保护用户。
我正在研究一系列 C 函数,以允许用户动态构建数组。库的核心驻留在Array
struct
其中包含一个指针变量array
包含数组数据,len
包含数组的长度,size
,这是数组的总内存分配,elem
,其中包含每个索引的内存分配,以及指针变量 name
和 dtype
,其中包含描述数组名称的字符串和数组的类型。目前我已经限制了范围,因此只能考虑 int
、float
、double
和 char
数组。
到目前为止,我已经定义并单独测试了以下功能;
array_mem_alloc
其中包含为数组分配内存的代码。init_array
是array_mem_alloc
的包装器,实例化Array
struct
,确定数据类型,returns 一个Array
用户的数据类型。append_array
允许用户一次动态增长数组一个索引,或者添加一个已经定义的数组。free_array
释放所有内存并重置struct
变量int_array_val
将索引处的数据和 returns 类型转换为用户。我有适用于所有相关数据类型的此函数的版本,但对于这个问题我只会使用此版本。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
函数,一切正常,这也是 returns 另一个名为 p
的 Array
容器。但是,当我尝试使用 int_array_val
函数检索整数变量时,它失败了,因为它无法将变量 array->dtype
识别为包含字符串 "int"
。但是,当我在 find_int_array_indices
和主函数中测试容器时,变量确实包含字符串 "int"
。这告诉我,我可能有一个指针错误,但我没有看到它。任何建议都会非常有用。我想知道我是否需要回到开头并将 name
和 dtype
定义为 Array
struct
中的固定长度数组而不是指针变量。
array.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);
array.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;
}
main.c
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";
都是局部变量,在函数return时失效。参见:Dangling pointer and Lifetime.
init_array
使用这些值就好像它们有生命周期来匹配它的 return 值
Array array;
array.dtype = dtype;
array.elem = size;
array_mem_alloc(&array, num_indices);
array.name = name;
return array;
作为结构类型,其生命周期由其调用者的上下文决定(return 毕竟是复制)。
find_int_array_indices
在returnsindice_arr
到main
.
部分选项:
- 严格使用指向带 static storage duration 的字符串的指针。
- 更改您的结构定义以包含这些字符串的 space(或分配它),并执行字符串复制。
- 改用 enumerated type。
- 放弃这个 string-based,通过通用地支持所有内存大小(尽管命名功能仍然是一个问题)将类型受限的范例放在一起。
一个相当long-winded的延续,详细说明如何使用enumerated types:
我们的想法是定义一组较小的图书馆可以使用的可接受值,并让用户更加了解这些值。正如我们所看到的,您已经使用字符串部分完成了该操作,但实现存在一些问题,因为字符串通常很笨重。字符串的一些问题:
- 您无法控制您图书馆的用户使用的字符串(这导致您必须退出1程序用户输入意外的事件,这很容易做到),
- 您必须考虑到它们潜在的大量或过多的内存消耗,
- 字符串比较复杂度为O(N),
- 字符串在 C 中通常是不安全的,在处理它们(赋值、比较、存储)时需要比其他基本结构更加小心。
因此,我们不使用字符串(在这些示例中为 "foo"
、"bar
”、"qux"
),而是使用枚举类型
enum OBJECT_TYPE {
OBJECT_FOO,
OBJECT_BAR,
OBJECT_QUX
};
它建立了以下内容:
- 更清楚可接受的值是什么
- 一些2 通过类型提示控制用户输入的内容
- 比较是O(1)
- 处理与任何整数类型相同
结构定义看起来像
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;
对于结构的名称成员,真的无能为力。如果您想要用户定义的名称标签,那么是的,如前所述,您需要为它们分配 space。
我们的初始化函数的工作方式类似,但我们可以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 = '[=14=]';
}
现在我们想提供一个函数,它只处理我们类型为 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
ing 来自图书馆代码的注释:不要。作为库作者,您没有合理的权利导致用户程序终止(除非请求,或者用户在您的控制之外调用 UB)。向上委托,return 错误,并让用户按自己的方式退出程序,因为他们可能需要执行自己的清理(或者如果失败是 non-critical,则可能会继续)。
2. C的枚举类型比较弱。 enum
实际上就是 int
,用户 可以 输入超出指定范围的普通整数值。这类似于从图书馆的角度调用 undefined behavior,但无论如何我们可能希望保护用户。