【问题标题】:Dynamic allocation isn't working in a loop动态分配不在循环中工作
【发布时间】:2019-10-16 00:04:14
【问题描述】:

我正在尝试将包含条形符号| 的字符串str 分解为字符串数组(output),分隔符为|,它不会包含在字符串数组中. output 中将有 20 个元素。这段代码属于一个会返回output指针的函数,这就是我需要动态分配的原因。

我正在尝试不使用使用sscanf() 函数来做到这一点。

示例:如果str"abc|def||jk",那么这就是output 最后的样子(出于演示目的,少于20 个元素):

输出[0]=="abc"

输出[1]=="def"

输出[2]==""

输出[3]=="jk"

但是,我总是收到错误退出代码,例如:

进程以退出代码 -1073740940 (0xC0000374) 结束

调试的时候发现第一个字符串元素被正确解析为output,但是第二个元素有时会正确生成,有时我遇到了麻烦。

代码如下:

char **output = (char**) calloc(20, 20*sizeof(char));
int begin = 0;
int end = 1;
// index that counts output element
int arrayIndex = 0;
int i;
for(i = 0; i < strlen(str); i++) {
    end = i;
    bool endOfString = false;
    // there is no '|' symbol at the end of the string
    if (*(str+i) == '|' || (endOfString = (i+1)==strlen(str))) {
        end = endOfString ? (end+1):end;

        // problem here. Assembly code poped up when debugging (see image below)
        char *target = (char*) calloc(end-begin+1, sizeof(char));

        // give proper value to target
        strcpy(target, str+begin);
        *(target+end-begin) = '\0';
        *(output+arrayIndex) = target;
        // increase proper indexes
        begin = i + 1;
        arrayIndex++;

    }
}

最糟糕的是我无法调试它,因为一个带有汇编代码的窗口会弹出我在调试时跳过calloc 函数的实例。

我也用过gdb,但是没用:

56 char target = (char) calloc(length, sizeof(char));

(gdb)n

警告:检测到严重错误 c0000374

线程 1 收到信号 SIGTRAP,跟踪/断点陷阱。

0x00007ffded8191f3 在?? ()

(gdb) bt

#0 0x00007ffded8191f3 在?? ()

回溯停止:前一帧与本帧相同(损坏的堆栈?)

(gdb) 继续

继续。

gdb:0x7ffded819269 处的未知目标异常 0xc0000374

【问题讨论】:

  • 这只是calloc 函数的程序集。你可以忽略它,它没有意义。相反,请使用真正的命令行调试器,例如 gdb
  • @JL2210 对不起,我的意思是说“越过”而不是“进入”。我使用了 gdb,它也没有工作。刚刚编辑了我的问题。
  • 收到SIGTRAP 后,输入btcontinue
  • @JL2210 它显示“回溯停止:前一帧与本帧相同(损坏的堆栈?)”
  • 看起来你正在破坏堆栈。我得走了。晚安。

标签: c loops debugging dynamic-memory-allocation string-parsing


【解决方案1】:

(编辑)索引没问题。几个注意事项:sizeof(char) 始终为1——只需使用1。再者,malloc 的返回也不需要强制转换,没有必要。请参阅:Do I cast the result of malloc?。另外,你给strlen(str)打了多少次电话? (希望优化将循环条件限制为一次调用,但您可能会为每次迭代调用strlen(str))。与endOfString = (i+1)==strlen(str) 相同。在进入循环之前保存字符串的长度,然后使用保存的值进行比较。

虽然您可以按现有方式计算索引并逐个字符循环查找分隔符,但让strchr (pointer, '|') 前进到下一个分隔符(或返回NULL 表示不再有分隔符)效率更高。然后不用担心索引,只需保留一个指针 p 和结束指针 ep 以向下推进字符串,例如

#define NFIELDS 20
...
char **splitstr (const char *s, const char delim, size_t *n)
{
    const char  *p = s,     /* pointer for parsing */
                *ep = s;    /* end pointer for parsing */
    *n = 0;                                 /* zero string counter */
    while (*n < NFIELDS && *p) {            /* loop while less than NFIELDS */
        size_t len;
        if ((ep = strchr (p, delim)))       /* get pointer to delim */
            len = ep - p;
        else
            len = strlen (p);               /* or get length of final string */
        if (!(output[*n] = malloc (len + 1))) { /* allocated for string */
        ...
        memcpy (output[*n], p, len);        /* copy chars to output[n] */
        output[(*n)++][len] = 0;            /* nul-terminate to make string */
        if (!ep)                            /* if delim not found, last */
            break;
        p = ++ep;                           /* update p to 1-past delim */
    }
    ...

添加适当的错误检查并返回指向分配字符串的指针,您可以这样做:

char **splitstr (const char *s, const char delim, size_t *n)
{
    const char  *p = s,     /* pointer for parsing */
                *ep = s;    /* end pointer for parsing */
    char **output = calloc (NFIELDS, sizeof *output);   /* pointer to output */

    if (!output) {
        perror ("calloc-output");
        return NULL;
    }

    *n = 0;                                 /* zero string counter */
    while (*n < NFIELDS && *p) {            /* loop while less than NFIELDS */
        size_t len;
        if ((ep = strchr (p, delim)))       /* get pointer to delim */
            len = ep - p;
        else
            len = strlen (p);               /* or get length of final string */
        if (!(output[*n] = malloc (len + 1))) { /* allocated for string */
            perror ("malloc-output[n]");
            while ((*n)--)          /* free prior allocations on failure */
                free (output[*n]);
            free(output);
            return NULL;
        }
        memcpy (output[*n], p, len);        /* copy chars to output[n] */
        output[(*n)++][len] = 0;            /* nul-terminate to make string */
        if (!ep)                            /* if delim not found, last */
            break;
        p = ++ep;                           /* update p to 1-past delim */
    }

    return output;      /* return pointer to allocated strings */
}

在您的情况下,一个完整的简短示例是:

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

#define NFIELDS 20
#define MAXC  1024

char **splitstr (const char *s, const char delim, size_t *n)
{
    const char  *p = s,     /* pointer for parsing */
                *ep = s;    /* end pointer for parsing */
    char **output = calloc (NFIELDS, sizeof *output);   /* pointer to output */

    if (!output) {
        perror ("calloc-output");
        return NULL;
    }

    *n = 0;                                 /* zero string counter */
    while (*n < NFIELDS && *p) {            /* loop while less than NFIELDS */
        size_t len;
        if ((ep = strchr (p, delim)))       /* get pointer to delim */
            len = ep - p;
        else
            len = strlen (p);               /* or get length of final string */
        if (!(output[*n] = malloc (len + 1))) { /* allocated for string */
            perror ("malloc-output[n]");
            while ((*n)--)          /* free prior allocations on failure */
                free (output[*n]);
            free(output);
            return NULL;
        }
        memcpy (output[*n], p, len);        /* copy chars to output[n] */
        output[(*n)++][len] = 0;            /* nul-terminate to make string */
        if (!ep)                            /* if delim not found, last */
            break;
        p = ++ep;                           /* update p to 1-past delim */
    }

    return output;      /* return pointer to allocated strings */
}

int main (void) {

    char buf[MAXC],         /* buffer for input */
        **output = NULL;    /* pointer to split/allocated strings */
    size_t n = 0;           /* number of strings filled */

    if (!fgets (buf, MAXC, stdin)) {    /* validate input */
        fputs ("error: invalid input.\n", stderr);
        return 1;
    }
    buf[strcspn (buf, "\n")] = 0;       /* trim newline from buf */
    /* split buf into separate strings on '|' */
    if (!(output = splitstr (buf, '|', &n))) {
        fputs ("error: splitstr() failed.\n", stderr);
        return 1;
    }

    for (size_t i = 0; i < n; i++) {    /* loop outputting each & free */
        printf ("output[%2zu]: %s\n", i, output[i]);
        free (output[i]);   /* free strings */
    }
    free (output);          /* free pointers */
}

使用/输出示例

$ echo "abc|def||jk" | ./bin/splitstr
output[ 0]: abc
output[ 1]: def
output[ 2]:
output[ 3]: jk

内存使用/错误检查

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

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

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

$ echo "abc|def||jk" | valgrind ./bin/splitstr
==32024== Memcheck, a memory error detector
==32024== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==32024== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==32024== Command: ./bin/splitstr
==32024==
output[ 0]: abc
output[ 1]: def
output[ 2]:
output[ 3]: jk
==32024==
==32024== HEAP SUMMARY:
==32024==     in use at exit: 0 bytes in 0 blocks
==32024==   total heap usage: 5 allocs, 5 frees, 172 bytes allocated
==32024==
==32024== All heap blocks were freed -- no leaks are possible
==32024==
==32024== For counts of detected and suppressed errors, rerun with: -v
==32024== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-29
    • 1970-01-01
    • 2023-04-06
    • 2022-01-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多