【问题标题】:How to fix? Triggers exception when trying to free dynamic allocated memory怎么修?尝试释放动态分配的内存时触发异常
【发布时间】:2018-12-29 20:58:07
【问题描述】:

这是我第一次发布问题,我确实试图找到解决方案,但是,即使我找到了,我也不认识它。

所以,正如标题所说,问题出在这个触发的异常“lab10.exe 中的 0x0F26372D (ucrtbased.dll) 引发的异常:0xC0000005:访问冲突读取位置 0xCCCCCCC4。

如果有针对此异常的处理程序,则程序可以安全地继续。”,当我进入 line -> free(word) 时会发生这种情况。

这在我学习 malloc 时确实发生过几次,但我忽略了它 - 认为还有其他问题。但现在我发现我做错了什么。

程序的重点是 - 编写结构“word”。我需要输入句子并将其“切割”成单词,然后将每个单词与单词中的字母大小和单词的序数一起放入结构中。

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

struct word {
    char text_word[50];
    unsigned sizee; //number of letters of the word
    unsigned number; //ordinal number of the word
};

void cutting_sentence(struct word *p, char *sen) { //sen is sentence
    int size_sen, i, j;

    size_sen = strlen(sen) + 1; //size of sentence

    p = (struct word*)malloc(size_sen * sizeof(struct word)); 
    if (p == NULL) {
        printf("\nNot enaugh memory!");
        return 0;
    }

    strcpy(p[0].text_word, strtok(sen, " ,.!?")); 
    p[0].sizee = strlen(p[0].text_word);
    p[0].number = 1;

    printf("word:%s \t size:%u \t ordinal number of the word:%u\n", 
        p[0].text_word, p[0].sizee, p[0].number);

    for (i = p[0].sizee - 1, j = 1;i < size_sen;++i) {
        if (*(sen + i) == ' ' || *(sen + i) == '.' || *(sen + i) == ','
        || *(sen + i) == '?' || *(sen + i) == '!') {
            strcpy(p[j].text_word, strtok(NULL, " ,.!?"));
            p[j].sizee = strlen(p[j].text_word);
            p[j].number = j + 1;

            printf("word:%s \t size:%u \t ordinal number of the 
                        word:%u\n", p[j].text_word, p[j].sizee, p[j].number);

            j++;
        }
    }
}

int main() {
    char sentence[1024];
    struct word *word;

    printf("Sentence: ");
    gets(sentence);

    cutting_sentence(&word, sentence);

    free(word);  //here is exception triggered

    return 0;
}

【问题讨论】:

  • 您的编译器应该抱怨类型不匹配...在main 函数中,word 的类型是什么?那么&amp;word 的类型是什么? cutting_sentence 的第一个参数的类型是什么?似乎您尝试在 C 中模拟传递引用,但并没有一路走下去。
  • 永远不要使用gets函数! a dangerous function 甚至已从 C 规范中删除。使用例如改为fgets(但请注意它与gets 的区别)。
  • words 的类型是struct word *。那么&amp;words 的类型(作为words 的指针)必须是struct word **。不完全是函数所期望的。
  • 这是operator precedence的问题。在函数内部,一旦你更正了参数类型,那么表达式*p[j].sizee 实际上等于(*p[j]).sizee。 IE。它试图取消引用p[j] 作为指针,但事实并非如此。相反,您需要使用显式括号,例如 (*p)[j].sizee
  • 更糟糕的是,mallocfree 首先都没有被正确地拉入命名空间。此代码没有#include &lt;stdlib.h&gt;。如果您将强制转换为 malloc (discussed here) 并且代码 pukes 无法编译,那就是您做错了什么的重要线索。

标签: c


【解决方案1】:

您正在更改传递的指针参数的本地值,您需要更改其目标处的内存,以便调用者发现分配内存的位置。由于您没有这样做,因此您正在尝试释放存储在 main() 堆栈中的局部变量 word

首先要解决的问题是不要有一个与类型名称相同的变量,那是邪恶的。

然后改变函数原型,传递一个双指针:

void cutting_sentence(struct word **p, char *sen);

请记住,您在使用 p 的位置现在需要使用 *p 或首先分配一个本地 (word *),其中包含地址值。

void cutting_sentence(struct word **p, char *sen) { //sen is sentence
    int size_sen, i, j;

    size_sen = strlen(sen) + 1; //size of sentence

    *p = (struct word*)malloc(size_sen * sizeof(struct word)); 
    if (*p == NULL) {
        printf("\nNot enaugh memory!");
        return; //void cannot return a value
    }

以此类推,将p 的每次用法更改为*p

然后

int main() {
    char sentence[1024];
    struct word *words;

    printf("Sentence: ");
    gets(sentence);

    cutting_sentence(&words, sentence);

    if (words != NULL)
       free(words);  //now valid

    return 0;
}

【讨论】:

  • 是的,我做到了。但是现在我在这些行中都有很多错误: *p[0].number = 1;或 p[j].number = 1;和其他例子。它告诉我,因为它是指针,我应该使用'->',但我不明白我应该如何使用它,因为我有'[j]'。那么我不应该能够使用'。' ?哦,谢谢你解释双指针!
  • p[0] 变为 (*p)[0] 或者如前所述,您可以分配一个本地 word *,这比总是执行 word ** 所暗示的额外取消引用要快。因此,例如,您可以将struct word **p_ptr) 放入原型中,然后将struct word *p = malloc(...); 交给调用者*p_ptr = p;
  • 是的!就是这样!非常感谢!这使得代码更容易阅读和编写。我会处理我的指针,因为它们真的很令人困惑。代码有效,现在我也理解问题了。
【解决方案2】:

还有一些问题比之前讨论的要多。

[正如已经指出的] 你的第一个参数应该是struct word **。但是,更简单的方法是消除它并将返回类型更改为struct word *。这使得函数内的代码更简单(即没有指针的双重取消引用)

虽然分配与输入字符串中的字符一样多的单词结构会起作用,但这有点不寻常。

更好的方法[至少更惯用]是在循环中使用realloc

在任何一种情况下,都可以通过最终的realloc 修剪数组大小以仅使用它需要的大小。

我认为您扫描sen 以查找分隔符的循环过于复杂。只需在循环中使用strtok 即可获得相同的效果,但复杂度更低。

此外,您没有传达回count的字数。一种方法是向数组中添加一个大小为零的额外元素(例如,列表结束标记)


这是一个重构的版本,应该会有所帮助:

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

struct word {
    char text_word[50];
    unsigned sizee;                     // number of letters of the word
    unsigned number;                    // ordinal number of the word
};

struct word *
cutting_sentence(char *sen)
{                                       // sen is sentence
    int curcnt = 0;
    int maxcnt = 0;
    char *token;
    struct word *word;
    struct word *words;

    while (1) {
        token = strtok(sen," ,.!?\n");
        if (token == NULL)
            break;
        sen = NULL;

        if (curcnt >= maxcnt) {
            maxcnt += 100;
            words = realloc(words,sizeof(struct word) * (maxcnt + 1));
        }

        word = &words[curcnt];
        strcpy(word->text_word,token);
        word->number = curcnt;
        word->sizee = strlen(token);

        ++curcnt;
    }

    words = realloc(words,sizeof(struct word) * (curcnt + 1));

    // set end-of-list
    word = &words[curcnt];
    word->sizee = 0;

    return words;
}

int
main()
{
    char sentence[1024];
    struct word *words;
    struct word *word;

    printf("Sentence: ");
    fflush(stdout);

    fgets(sentence,sizeof(sentence),stdin);

    words = cutting_sentence(sentence);

    for (word = words;  word->sizee != 0;  ++word)
        printf("main: number=%u sizee=%u text_word='%s'\n",
            word->number,word->sizee,word->text_word);

    free(words);

    return 0;
}

【讨论】:

  • 没有。您是从一个根本错误的前提下写作的——请再次查看 malloc 调用,看看它为输入中的字符数量的结构分配空间。这比需要的多,而不是更少。
  • @ChrisStratton Yikes,是的,你是对的。有足够的空间。但是,真的吗?当然,这可以更干净地完成。而且,应该向OP指出[至少在解释中]。但是,我会相应地编辑我的解释。
【解决方案3】:

以下建议的代码:

  1. 消除冗余代码
  2. 正确检查错误
  3. 正确输出错误信息(以及系统认为发生错误的文本原因stderr
  4. 执行所需的功能
  5. 正确初始化struct word指针
  6. 正确更新struct word指针
  7. int sizee 更改为size_t sizee,因为函数:strlen() 返回size_t,而不是int
  8. int i更改为unsigned i,因为结构字段number的声明被声明为unsigned
  9. 记录包含每个头文件的原因
  10. sentence 中的每个字符分配struct word 的一个实例@ 这是“矫枉过正”。如果每个单词都只有一个字符,那么最可能的单词数量是。因此,立即分配内存的大小可以减少一半。计算单词分隔符的循环将导致分配的内存量正确。您可以轻松添加该功能。
  11. 注意函数的使用方式:strtok()。 IE。循环前的初始调用,然后循环结束时的调用

现在建议的代码:

#include <stdio.h>   // printf(), fgets(), NULL
#include <stdlib.h>  // exit(), EXIT_FAILURE, malloc(), free()
#include <string.h>  // strlen(),  strtok()


struct word 
{
    char text_word[50];
    size_t sizee; //number of letters of the word
    unsigned number; //ordinal number of the word
};

// notice the `**p` so can access the pointer in `main()` so it can be updated
void cutting_sentence(struct word **p, char *sen) 
{ //sen is sentence
    size_t size_sen = strlen(sen); //size of sentence
    struct word *wordptr = *p;

    wordptr = malloc(size_sen * sizeof(struct word)); 
    if ( !wordptr ) 
    {
        perror("malloc failed");
        exit( EXIT_FAILURE );
    }


    unsigned i = 0;
    char * token = strtok(sen, " ,.!?");
    while( token )
    {
        strcpy( wordptr[i].text_word, token ); 
        wordptr[i].sizee = strlen( token );
        wordptr[i].number = i;

        printf("word:%s\t Length:%lu]tordinal number of the word:%u\n", 
                wordptr[i].text_word, 
                wordptr[i].sizee, 
                wordptr[i].number);

        token = strtok( NULL, " ,.!?");
        i++;
    }
}

int main( void ) 
{
    char sentence[1024];
    struct word *wordArray = NULL;

    printf("Sentence: ");
    if( !fgets(sentence, sizeof( sentence ), stdin ) )
    {
        perror( "fgets failed" );
        exit( EXIT_FAILURE );
    }

    // remove trailing new line
    sentence[ strcspn( sentence, "\n") ]  = '\0';
    cutting_sentence(&wordArray, sentence);

    free( wordArray );  //here is exception triggered

    return 0;
}

典型的代码运行结果:

Sentence: hello my friend
word:hello   Length:5   ordinal number of the word:0
word:my  Length:2   ordinal number of the word:1
word:friend  Length:6   ordinal number of the word:2

请注意,“短”字会导致输出不均匀。您可能需要更正它。

【讨论】:

    猜你喜欢
    • 2018-10-16
    • 2011-03-17
    • 1970-01-01
    • 2013-11-22
    • 1970-01-01
    • 2012-11-01
    • 2013-04-14
    • 1970-01-01
    相关资源
    最近更新 更多