【问题标题】:Expanding size of array to growing number of variables将数组的大小扩展到越来越多的变量
【发布时间】:2018-07-07 00:53:06
【问题描述】:

我已经阅读了 C 语言中的动态扩展数组以及很多答案,但我找不到问题的根源。我尝试使用空格作为分隔符进行标记并将每个字符串存储在一个数组中,因此"this is string" 变为{ "this", "is", "string" }

我可以使用预定义的数组大小来执行此操作,但是当传入的字数超过声明的字数时无法​​扩展它。下面是代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define DEFAULT_SIZE 3 /* default size of an array of words */

// array object
typedef struct {
    char pointer[DEFAULT_SIZE][256];
    int used;
    int size;
} Array;

void createArray(Array *array, int start_size) {
    array->used = 0;
    array->size = start_size;
}

void append(Array *array, char* elem) {
    if (array->used >= array->size) { // if every slot is used, then we expand
        array->size += 2; // expand by 2 slots

        Array *array = malloc(sizeof(Array));
        array = realloc(array, sizeof(array) + 2 * 256);
    }

    strcpy(array->pointer[array->used] , elem); // insert new element
    array->used++; // <-- wrong value here. increment info about used slots
}

char * args_str; // passed string
Array args_list; // expandable array holding words

void parse(char* command) {
    createArray(&args_list, DEFAULT_SIZE);
    args_str = strtok(command, " ");

    while (args_str != NULL) {
        append(&args_list, args_str);
        args_str = strtok(NULL, " ");
    }

return ;
}

我注意到array-&gt;used 的值会正确递增,直到数组扩展。如果项目超过 3 个,则为 1、2、3、66123(或此处的其他随机值)。我怀疑这是由于错误的realloc,但我

【问题讨论】:

  • 但是我。 . . ?什么
  • 您认为array 中的realloc 会完成什么?您的结构具有固定大小并且不包含指针(结构中的pointer 是一个二维数组)。您只能分配sizeof *array 字节。向其添加更多存储无济于事。你没有办法解决新的记忆。您可以创建一个结构数组并从每个结构的 DEFAULT_SIZE 字符串的存储中受益,但您可以调整单个结构的大小。

标签: c arrays multidimensional-array realloc


【解决方案1】:

您遇到的一个问题是您对动态分配的内存可以扩展(或为此使用)什么有基本的了解。您的 typedef 结构 Array固定大小。如果您分配给Array *array,则此后您不能将realloc 分配给array。它将永远是sizeof *array。为它分配更多内存没有好处,因为它不包含指向任何额外内存的指针,并且会破坏类型的指针算术。

您唯一能做的就是分配Array 的任何数组。但这会比较麻烦,因为每个元素最多只能存储 DEFAULT_SIZE 令牌。

如果我了解您要完成的工作,而不是让您的结构包含固定大小的二维数组 pointer,您需要 pointer 实际上是一个 指向数组的指针 char[256] 的 em>。例如,你想要:

#define DEFAULT_SIZE 3      /* default size of an array of words */
#define TOKEN_SIZE 256

typedef struct {
    char (*pointer)[TOKEN_SIZE];
    int used;
    int size;
} arr;

(注意: C 避免使用camelCasePascalCase 变量名,而保留大写所有小写 /em> 用于宏和常量的名称。)

现在,您确实可以在Array 中调整大小(标准情况下是我的arr)。您可以分配arr 的实例,然后为arr-&gt;pointer 分配(和realloc arr-&gt;pointer arr-&gt;used &gt;= arr-&gt;size 时)

重做您的示例,为每个内存分配添加适当的验证,并使用realloc 方案,每次arr-&gt;used &gt;= arr-&gt;sizerealloc arr-&gt;pointer2 * arr-&gt;size,您可以这样做类似于以下内容:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define DEFAULT_SIZE 3      /* default size of an array of words */
#define TOKEN_SIZE 256

typedef struct {
    char (*pointer)[TOKEN_SIZE];
    int used;
    int size;
} arr;

arr *createArray (int start_size) 
{
    arr *array = malloc (sizeof *array);

    if (!array) {
        perror ("malloc - array");
        exit (EXIT_FAILURE);
    }

    array->used = 0;
    array->size = start_size;
    array->pointer = malloc (start_size * sizeof *array->pointer);

    if (!array->pointer) {
        perror ("malloc - array->pointer");
        exit (EXIT_FAILURE);
    }

    return array;
}

void append (arr *array, char *elem) 
{
    if (array->used >= array->size) {

        void *tmp = realloc (array->pointer, array->size * 2 * sizeof *array->pointer);
        if (!tmp) {
            perror ("realloc - array->pointer");
            return;  /* you should change function type provide success/failure */
        }
        array->pointer = tmp;
        array->size *= 2;       /* update size */
    }

    if (strlen (elem) + 1 < sizeof *array->pointer) {
        strcpy (array->pointer[array->used], elem); // insert new element
        array->used++; // <-- wrong value here. increment info about used slots
    }
    else
        fprintf (stderr, "error: token too long.\n");
}

arr *parse (char* command)
{
    char *args_str;
    arr *args_list = createArray (DEFAULT_SIZE);

    args_str = strtok (command, " ");

    while (args_str != NULL) {
        append (args_list, args_str);
        args_str = strtok (NULL, " ");
    }

    return args_list;
}

/* simple print function */
void prnarr (arr *array)
{
    int i;
    for (i = 0; i < array->used; i++)
        printf ("array[%2d] : %s\n", i, array->pointer[i]);
}

/* simple function to free allocated memory */
void freearr (arr *array)
{
    free (array->pointer);
    free (array);
}

int main (void) {

    char str[] = "my dog has fleas my cat has none lucky cat";
    arr *args = parse (str);

    prnarr  (args);
    freearr (args);

    return 0;
}

使用/输出示例

$ ./bin/arr_struct_arr_of_ptrs
array[ 0] : my
array[ 1] : dog
array[ 2] : has
array[ 3] : fleas
array[ 4] : my
array[ 5] : cat
array[ 6] : has
array[ 7] : none
array[ 8] : lucky
array[ 9] : cat

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您都有 2 个职责:(1)始终保留指向起始地址的指针内存块,因此 (2) 当不再需要它时可以释放

您必须使用内存错误检查程序来确保您不会尝试访问内存或写入超出/超出分配块的边界,尝试读取或基于未初始化的值进行条件跳转,最后,以确认您释放了已分配的所有内存。

对于 Linux,valgrind 是正常的选择。每个平台都有类似的内存检查器。它们都易于使用,只需通过它运行您的程序即可。

$ valgrind ./bin/arr_struct_arr_of_ptrs
==9128== Memcheck, a memory error detector
==9128== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==9128== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==9128== Command: ./bin/arr_struct_arr_of_ptrs
==9128==
array[ 0] : my
array[ 1] : dog
array[ 2] : has
array[ 3] : fleas
array[ 4] : my
array[ 5] : cat
array[ 6] : has
array[ 7] : none
array[ 8] : lucky
array[ 9] : cat
==9128==
==9128== HEAP SUMMARY:
==9128==     in use at exit: 0 bytes in 0 blocks
==9128==   total heap usage: 4 allocs, 4 frees, 5,392 bytes allocated
==9128==
==9128== All heap blocks were freed -- no leaks are possible
==9128==
==9128== For counts of detected and suppressed errors, rerun with: -v
==9128== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认您已释放已分配的所有内存并且没有内存错误。

查看一下,如果您还有其他问题,请告诉我。

【讨论】:

    【解决方案2】:

    你用错了realloc

    Array *array = malloc(sizeof(Array));
    array = realloc(array, sizeof(array) + 2 * 256);
    

    realloc 用于调整(增长或缩小)动态分配的内存量。它使 首先分配然后在下一步重新分配没有什么意义。为什么不 你一开始就分配了足够的内存?

    这就是你应该如何重新分配更多内存:

    Array *append(Array *array, char* elem) {
        size_t size;
        if(array == NULL)
            size = 2;
        else if (array->used >= array->size)
            size = array->size + 2;
    
        Array *tmp = realloc(array, size * sizeof *tmp);
        if(tmp == NULL)
            return NULL;  // telling the caller that something went wrong
    
        array = tmp;
        array->size = size;
    
    
        // I don't understand what you are trying to do here
        // strcpy(array->pointer[array->used] , elem); // insert new element
        // array->used++; // <-- wrong value here. increment info about used slots
    
        return array;
    }
    

    我不知道你从哪里得到 realloc 的 256。现在拨打append时:

    Array *tmp = append(array, element);
    if(tmp == NULL)
    {
        // something went wrong
        // do error handling,
        // do NOT continue
    }
    
    array = tmp;
    ...
    

    说实话,我真的不明白你想用 Array 结构。 pointer 是一个不能动态调整大小的数组, 因为它的大小是固定的。相反,您正在创建越来越多的Array 已经有一个二维数组的对象。我看不出原因 为此。

    无论如何,这就是我会使用的:

    typedef struct {
        char **words;
        size_t size;
        size_t used;
    } Array;
    
    Array *createArray(size_t size) {
        Array *array = malloc(sizeof *array);
        if(array == NULL)
            return NULL;
    
        array->used = 0;
        array->size = size;
        array->words = calloc(size, sizeof *array->words);
    
        if(array->words == NULL)
        {
            free(array);
            return NULL;
        }
    
        return array;
    }
    
    // it can make grow and shrink the array
    int resize_array(Array *array, size_t newsize)
    {
        if(array == NULL)
            return 0;
    
        if(array->size == newsize)
            return 1;
    
        char **tmp = realloc(array->words, newsize * sizeof *tmp);
        if(tmp == NULL)
            return 0;
    
        array->words = tmp;
    
        if(array->size < newsize)
        {
            // setting new fields to 0
            memset(array->words + array->size, 0, newsize - array->size); 
        } else {
            if(array->used > newsize)
                array->used = newsize;
        }
    
        array->size = newsize;
        return 1;
    }
    
    int append(Array *array, char* elem) {
        if(array == NULL || elem == NULL)
            return 0;
    
        if(array->used >= array->size)
        {
            // adding 2 more spaces
            if(!resize_array(array, array->size + 2))
                return 0;
        }
    
        array->words[array->used++] = strdup(elem);
    
        return 1;
    }
    
    void free_array(Array *array)
    {
        if(array == NULL)
            return;
    
        for(int i = 0; i < array->size; ++i)
            free(array->words[i]);
        free(array->words);
        free(array);
    }
    

    不要忘记释放内存,请不要使用全局变量,这是一种不好的做法。你必须使用 仅在真正需要时使用全局变量。

    【讨论】:

    • 不幸的是,大多数编译器都要求您在不同范围内重用相同的变量名时发出警告(因为它完全“合法”)。使用 gcc,您可以通过 -Wshadow 来启用警告。 (您必须检查其他编译器的选项,尽管/Wall 会为 VS 获取它)
    • @DavidC.Rankin 是吗?我不知道。我用我的编译器(GCC)void foo(int a) { int a=9; } 对其进行了测试,得到了error: ‘a’ redeclared as different kind of symbol。我只是使用了gcc a.c,没有选项。
    • 已声明变量在不同范围内的重新声明和遮蔽是不同的。 (例如int a, b=10; while (b--) { int a; a = 7; })只会被标记为-Wshadow
    • @DavidC.Rankin 啊,我现在明白了。因为Array *arrayif{} 块内,所以它是一个不同的范围,这就是它可以工作的原因。感谢您的解释。我修正了我的答案。
    猜你喜欢
    • 1970-01-01
    • 2017-02-08
    • 1970-01-01
    • 2019-05-15
    • 2016-07-18
    • 2022-11-02
    • 1970-01-01
    • 2014-05-02
    • 2021-06-03
    相关资源
    最近更新 更多