【问题标题】:2D Array Malloc二维数组 Malloc
【发布时间】:2011-10-30 07:49:05
【问题描述】:

下面是较长代码的一部分,其中完成了二维数组的 malloc'ing。谁能告诉这是否正确?如果我引入静态值,则代码可以正常工作。否则,段错误...

enum { LEN = 1024*8 };

char **tab = NULL;
int cur_LEN = LEN;
int count_lineMax = 0;


tab = malloc(count_lineMax * sizeof(*tab));
      memset(tab, 0, count_lineMax * sizeof(*tab));

if(tab == NULL && count_lineMax) {
    printf("Mem_check\n");
    exit(1);
}

for(k=0;k<count_lineMax;k++) {
    tab[k] = malloc(cur_LEN*sizeof(*tab[k]));
    memset(tab[k], 0, cur_LEN*sizeof(*tab[k]));

    if(tab[k] == NULL) {
        printf("Mem_check*\n");
        exit(1);
    }
}
for(l=0;l<count_lineMax;l++) {
    free(tab[l]);
}
free(tab);

【问题讨论】:

  • count_lineMax 为零,因此您什么也不分配。
  • 看起来不错;即使设置 count_lineMax 也对我有用。话虽如此,您可以分配一个块。
  • 您遇到了 seg 错误,因为您分配了 0 个字节而不进行检查(malloc 每次都会返回 null)。如果你真的想要一个二维数组,你可以在一行中一次分配整个块。如果你想要一个可变长度的 C 字符串数组(这就是你想要的样子),你需要 malloc 使用 lineMax > 0 的第一个列表,然后在每个元素处为可变长度分配足够的空间该位置的 C 字符串(不要忘记为 '\0' 添加一个额外的字符)
  • 你需要在使用memset()之前检查内存分配——你应该考虑使用calloc()而不是malloc(),因为它会为你做归零。还不清楚您是否从归零中受益 tab,因为接下来您要做的是将值分配到最近归零的空间中。
  • 您创建的数据结构不是二维数组。它是一个指向 [the first elements of] 一维数组的指针数组。

标签: c


【解决方案1】:
int count_lineMax = 0;

tab = malloc(count_lineMax * sizeof(*tab));

这是什么?你要 malloc 0 个字节吗?

【讨论】:

  • 我也应该添加这个循环:while(fgets(line, cur_LEN, file1) != NULL) { count_lineMax++; }
  • 你在 malloc 之前添加了那个循环吗??
  • 哎呀!我的意思是在malloc之前!对不起
  • 您还有问题吗?那么请使用 -g 选项编译您的代码并使用 valgrind 运行。 (如果您不知道 valgrind,请在 google 上搜索)
【解决方案2】:

至少有两种方法可以在读取行时构建表格。一个使用realloc() 的属性,如果它的第一个参数是空指针,它将表现得像malloc() 并分配请求的空间(因此代码可以自启动,单独使用realloc())。该代码可能如下所示:

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

enum { LEN = 1024*8 };

static void error(const char *fmt, ...);
static char *xstrdup(const char *str);

int main(void)
{
    char line[LEN];
    char **tab = NULL;
    int tabsize = 0;
    int lineno = 0;

    while (fgets(line, sizeof(line), stdin) != 0)
    {
        if (lineno >= tabsize)
        {
            size_t newsize = (tabsize + 2) * 2;
            char **newtab = realloc(tab, newsize * sizeof(*newtab));
            if (newtab == 0)
                error("Failed to allocate %zu bytes of memory\n", newsize * sizeof(*newtab));
            tab = newtab;
            tabsize = newsize;
        }
        tab[lineno++] = xstrdup(line);
    }

    /* Process the lines */
    for (int i = 0; i < lineno; i++)
        printf("%d: %s", i+1, tab[i]);

    /* Release the lines */
    for (int i = 0; i < lineno; i++)
        free(tab[i]);
    free(tab);

    return(0);
}

static void error(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
    exit(1);
}

static char *xstrdup(const char *str)
{
    size_t len = strlen(str) + 1;
    char *copy = malloc(len);
    if (copy == 0)
        error("Failed to allocate %zu bytes of memory\n", len);
    memmove(copy, str, len);
    return(copy);
}

当表为空时,替代方案显式使用malloc(),并且最简单的编码为:

int main(void)
{
    char line[LEN];
    int tabsize = 4;
    int lineno = 0;
    char **tab = malloc(tabsize * sizeof(*tab));

    if (tab == 0)
        error("Failed to allocate %zu bytes of memory\n", tabsize * sizeof(*tab));

    ...

其他一切都可以保持不变。

请注意,让函数 xmalloc()xrealloc() 保证永远不会返回空指针会很方便,因为它们会报告错误:

static void *xmalloc(size_t nbytes)
{
    void *space = malloc(nbytes);
    if (space == 0)
        error("Failed to allocate %zu bytes of memory\n", nbytes);
    return(space);
}

static void *xrealloc(void *buffer, size_t nbytes)
{
    void *space = realloc(buffer, nbytes);
    if (space == 0)
        error("Failed to reallocate %zu bytes of memory\n", nbytes);
    return(space);
}

从长远来看(大型程序),这会减少您编写“内存不足”错误消息的总次数。另一方面,如果您必须从内存分配失败中恢复(保存用户的工作等),那么这不是一个合适的策略。

上面的代码创建了一个参差不齐的数组; tab 中的不同条目具有不同的长度。如果你想要同质长度(如在原始代码中),那么你必须替换或修改xstrdup()函数来分配最大长度。

您可能已经注意到xstrdup() 中的代码使用memmove() 而不是strcpy()。那是因为strlen() 已经测量了字符串的长度,所以不需要复制代码来测试每个字节是否需要复制。我使用了memmove(),因为它永远不会出错,即使字符串重叠,即使在这种情况下很明显字符串永远不会重叠,所以memcpy() - 如果字符串重叠,则不能保证正常工作 -可以使用,因为字符串不能重叠。

分配(oldsize + 2) * 2 新条目的策略意味着内存重新分配代码在测试过程中得到足够频繁的执行,而不会过度影响生产中的性能。请参阅 Kernighan 和 Pike 的 The Practice of Programming,了解为什么这是一个好主意。

我几乎总是使用一组类似于error() 函数的函数,因为它极大地简化了错误报告。我通常使用的函数是记录和报告程序名称(来自argv[0])的包的一部分,并且具有相当广泛的替代行为。

【讨论】:

  • 感谢您提供的所有信息。我必须打印出来并仔细检查所有内容。再次感谢。
猜你喜欢
  • 1970-01-01
  • 2013-11-30
  • 1970-01-01
  • 2014-06-25
  • 1970-01-01
  • 2018-03-20
  • 2016-08-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多