将数据分配给 C 中的 typedef 结构时出现指针错误

乔恩

我正在研究一系列 C 函数以允许用户动态构建数组。该库的核心位于Array struct其中包含一个包含数组数据的指针变量arraylen其中包含数组的长度size,它是数组的总内存分配elem,它包含每个索引的内存分配,以及指针变量name其中dtype包含描述数组名称和数组类型的字符串。目前我已经限制了范围,因此只能考虑intfloatdoublechar数组。

到目前为止,我已经定义并单独测试了以下功能;

  1. array_mem_alloc其中包含为数组分配内存的代码。

  2. init_array这是一个包装器,array_mem_alloc它实例化一个Array struct,确定数据类型并将数据类型返回Array给用户。

  3. append_array它允许用户一次动态地增长一个数组,或者添加一个已经定义的数组。

  4. free_array释放所有内存并重置struct变量

  5. int_array_val它在索引处对数据进行类型转换并返回给用户。我有所有相关数据类型的这个函数的版本,但是对于这个问题我只会使用这个版本。

  6. 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_testfind_int_array_indices函数,一切正常,它还返回另一个Array名为p. 但是,当我尝试使用该int_array_val函数检索整数变量时,它失败了,因为它无法将变量识别array->dtype为包含 string "int"但是,当我在 main 函数内部和内部测试容器时find_int_array_indices,变量确实包含 string "int"这告诉我我可能有一个指针错误,但我没有看到它。任何建议都会非常有用。我想知道我是否需要回到开始并定义namedtype作为固定长度数组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_indicesindice_arr返回时完成错误main

一些选项:

  • 严格使用指向具有静态存储持续时间的字符串的指针。
  • 更改您的结构定义以包含这些字符串的空间(或分配它),并执行字符串复制。
  • 请改用枚举类型
  • 通过一般支持所有内存大小来抛弃这种基于字符串的、类型受限的范例(尽管命名功能仍然是一个问题)。

一个相当冗长的延续,详细说明使用枚举类型

这个想法是定义一组较小的可接受值,您的库可以使用这些值,并让用户更加了解这些值。正如我们所看到的,您已经部分使用字符串完成了这项工作,但实现存在一些问题,因为字符串通常很笨重。字符串的一些问题:

  • 无法控制库用户使用的字符串(这会导致您必须退出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;

对结构的名称成员实际上无能为力。如果您想要用户定义的名称标签,那么是的,如前所述,您需要为它们分配空间。

我们的初始化函数的工作方式类似,但我们可以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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

将字符串分配给 C 中的结构变量时出现分段错误

将指针分配给变量vs将指针分配给C中的数组

分配给C中的字符串的索引解引用指针时出现段错误

无法将内存分配给c中的指针

将数组分配给C中的指针

munmap_chunk(): 在 C++ 中创建数组数据结构时出现无效指针错误

将结构指针传递给 C 中的函数时出现奇怪的错误

将值分配给指针 C++ 时出现分段错误

使用链表中的指针结构时出现C分段错误

C中的结构,typedef和指针

无法将结构的地址分配给类型为指向该结构的指针的typedef的变量

如何将分配给结构数组的结构包含在C中的结构中?

在 C 中,如果将双指针分配给值,为什么指针变量为 0?

无法将字符值分配给结构中的指针

将结构实例分配给C中的内部嵌套结构

将参数发送的指针的值分配给C中的本地指针时,为什么会出错

如何在C ++中的char指针中将数据分配给JSONCPP

将全零分配给C中的嵌套结构(GCC版本<5)

C程序中的typedef结构错误

将值分配给c中的全局变量会导致错误

将内存分配给 C 中动态增长的字符数组会引发分段错误

C#将值分配给结构错误的属性

C中的typedef结构继承

C 中的结构和 typedef

C-将数组分配给指针时出现“不兼容类型”警告

尝试访问指针列表中的指针时出现 C 总线错误,该指针是指向指针的结构体

当从数据库中检索记录时,如何将图像分配给学生记录?

将值分配给加载页面中的文本框时无法更新数据

无法分配给 C++ 中的指针数组