c中使用free()时出现异常,分段错误;

v8wbuo2f  于 2023-10-16  发布在  其他
关注(0)|答案(2)|浏览(176)

我的代码不知何故运行,但它没有给出预期的结果,所以为了理解它,我开始调试,它显示发生了一个异常,在我使用free()的行中出现了一个分段错误。下面是我的代码:

struct stack
{
    char ch;
    struct stack *prev;
} *top = NULL;

typedef struct stack st;

char pop();
void push(char c);
int evaluate(char arr[100]);

int main()
{
    char arr[100];
    printf("Enter the string to check format of string in wcwR");
    gets(arr);
    if (evaluate(arr) == 1)
    {
        printf("Entered string is in correct format");
    }
    else
    {
        printf("Entered string is in wrong format");
    }
}

int evaluate(char arr[100])
{
    int i;
    for (i = 0; arr[i] != 'c' && arr[i] != 'C'; i++)
    {
        push(arr[i]);
    }
    i++;
    int j = i;
    int flag = 0;
    while (arr[j] != '\0')
    {
        if (arr[j] != pop())
        {
            return 0;
        }
        else
        {
            flag = 1;
        }
    }
    if (flag == 1)
    {
        return 1;
    }
}

void push(char c)
{
    st *p;
    p = (struct stack *)malloc(sizeof(struct stack));
    p->ch = c;
    p->prev = top;
    top = p;
}

char pop()
{
    st *temp;
    char item;
    temp = top;
    item = top->ch;
    top = top->prev;
    **free(temp);** //here exception occurred 
    return item;
}

请帮助我解决这个问题,也可以有人建议任何合适的技术来解决这样的问题,我自己?谢谢

bwitn5fc

bwitn5fc1#

我将向你们展示一种处理这类问题的通用方法。我甚至不会说它是一个好的,但它对我来说一般工作,并在这里工作的第一次运行,至少在我运行的几个测试。
(This这是一篇很长的文章,因为我正在使用从代码和注解中复制和粘贴来构建示例)。

关于发布的代码

我不清楚你想干什么。一般来说,最好在代码之前发布任务的描述。通过阅读代码,我相信:

  • 你的目标函数是evaluate()
  • 这个想法是检查格式wCwR,一个字符串,如“1234C4321”
  • 中心有一个Cc
  • 如果C之前的字符串在C之后反转,则evaluate返回true
  • stack的使用是根据请求。

备注:

  • 不要使用像arr[100]这样的固定值。封装东西和使用jus指针更容易
  • 不要写交互式代码。它太慢,也太不方便。使用文件、常量、工厂函数。
  • 永远不要使用gets。几十年来,
  • 返回false0
  • 永远不要用全球性的东西
  • 在向malloc内存写入代码时,向free写入代码(并测试它)。
  • main应该是代码的第一个函数,如果可能的话,放在单独的文件中。

stack在哪里?

下面是代码中的struct

struct stack
{
  char ch;
  struct stack *prev;
} *top = NULL;

这是有问题的和脆弱的。

  • 这是Node。不是stack。在编程堆栈、列表、树或类似的容器时,更安全的做法是注意容器是节点的集合(可能是空的)。每个节点都包含(或指向)一些数据。
    *堆栈不是节点。节点不是堆栈
  • 你代码里的东西是全局的总的来说没有解决任何问题。在整个代码中只能有一个。如果你需要一对堆栈呢?一个堆栈数组?一堆结构体?

一个可能的Stack

typedef struct st_node
{
  char            ch;
  struct st_node* prev;
} Node;

typedef struct
{
  unsigned size;
  Node*    top;
} Stack;

这里我们有一堆节点。和堆栈本身中的一些元数据:尺寸
我将进一步介绍一个完整的示例,包括C代码和输出。我正在从程序中复制和粘贴。

栈不是任务

我们需要一个堆栈实现。但是我们绝对不想把程序和栈的代码混在一起,因为

  • 我们可以从语言中,从别人那里,或者从图书馆里
  • 如果我们真的需要为一个堆栈写代码,我们希望保持它的分离,以便能够在其他程序中使用它。

栈函数

我们需要最少的POP、PUSH和TOP操作,以及一个构造函数和一个析构函数。让我们考虑一下:

Stack* create();
Stack* destroy(Stack*);

char pop(Stack* stack);
int  push(char c, Stack* stack);
char top(Stack* stack);

使用指针使得在代码中编写1或1,000个堆栈变得微不足道。在C++ RAII概念中,我们将首先编写createdestroy

createdestroy的可能代码

Stack* create()
{
    Stack* stack = (Stack*)malloc(sizeof(Stack));
    if (stack == NULL) return NULL;
    stack->size = 0;  // empty
    stack->top  = NULL;
    return stack;
}

没什么特别的:我们分配一个堆栈,并通过使size = 0为空来设置它。
(题外话:请记住,在C中有一个邪教,说不要在malloc()中使用强制转换。我会的永远不会作为对程序员和读者的提醒。即使像这样非常简单的代码也会为NodeStack分配内存。编译器不需要这种类型转换,但是通过编写和重复它,你可以确定什么是什么,防止许多错误,为你保存很多麻烦。这种麻烦需要花费金钱、工作和时间。)
(This邪教是基于一个从未更新的书从90年代和一些Usenet职位甚至从以前。在网络编程或文本编辑等领域,通常会在几行代码中为许多类型的缓冲区分配内存,甚至malloc()的表达式也是一个函数调用,用于计算我们需要的缓冲区大小。就这样一个一个的投像给我们清楚的说明了什么是什么。不需要演员,但也没有工资)。

Stack* destroy(Stack* stack)
{
    if (stack == NULL) return NULL;  // no stack
    while (stack->size > 0)
    {
        Node* prev = stack->top->prev;  // save
        free(stack->top);               // destroy top
        stack->top = prev;              // reassign
        stack->size -= 1;
    }
    free(stack); // now the stack 
    return NULL;
}

正如预期的那样,但每个堆栈都有自己的size这一事实使事情变得更简单。导航堆栈释放节点,然后free堆栈。为什么总是返回NULL?这只是为了使在同一行中重置指针成为可能。每天都有成千上万的程序因为使用了无效的指针而崩溃。我们不希望有任何利润。
此程序可以运行:

int main(void)
{
    Stack* one = create();
    one   = destroy(one);  // destroy stack, clear pointer
    return 0;
}

只是精神上的支持。

top

TOP其实很简单:返回栈顶的值,栈保持不变:一行

char top(Stack* stack)
{  // return char in the top, or negative value
    if (stack == NULL) return -1;
    if (stack->size == 0) return -2;
    return stack->top->ch;
}

pop

POP删除顶部的元素并将其返回给调用者:

char pop(Stack* stack)
{
    if (stack == NULL) return -1;
    if (stack->size == 0) return -1;
    char  top  = stack->top->ch;    /// save it
    Node* temp = stack->top;        // save the pointer
    stack->top = stack->top->prev;  // go back
    stack->size -= 1;               // 1 less
    free(temp);
    return top;
}

正如预期的那样,保存元素,重新分配顶部,free节点,调整大小,只有几行。

push

int push(char c, Stack* stack)
{
    if (stack == NULL) return -1;
    Node* nw = (Node*)malloc(sizeof(Node));
    if (nw == NULL) return -1;
    nw->ch     = c;
    nw->prev   = stack->top;
    stack->top = nw;
    stack->size += 1;
    return 0;
}

PUSH是创建一个节点并将其链接到堆栈顶部的过程。

show:的帮助函数

一般来说,堆栈没有一个方法来显示它的内容,除了测试。但我们确实在测试,所以:

int show(Stack* stack, const char* msg)
{
    if (stack == NULL) return -1;
    if (msg != NULL) printf("%s", msg);
    if (stack->size == 0)
    {
        printf("    stack is empty\n");
        return 0;
    }
    printf("    %d elements in stack:\n\n", stack->size);
    Node* p = stack->top;
    for (unsigned i = 0; i < stack->size; i += 1)
    {
        printf("%8d,'%c' [%3d]\n", 1+i, p->ch, (int)p->ch);
        p = p->prev;
    }
    printf("-----\n");
    return 0;
}

这个版本有一个可选的标题,在测试中非常方便。请参阅范例。

'str_to_stack`

int str_to_stack(const char* str, Stack* stack)
{
    if (stack == NULL) return -1;
    if (str == NULL) return -2;
    const char* p = str;
    while (*p != 0)
    {
        push(*p, stack);
        p++;
    };
    return 0;
}

这只是一种将字符串压入堆栈的方法。排成一行。因此,我们可以将代码编写为:

Stack* one = create();

    str_to_stack("Stack", one);
    show(one, "\"Stack\" added =>");

    str_to_stack(" Overflow", one);
    show(one, "\" Overflow\" added =>");

只是为了看看

"Stack" added =>    5 elements in stack:

       1,'k' [107]
       2,'c' [ 99]
       3,'a' [ 97]
       4,'t' [116]
       5,'S' [ 83]
-----
" Overflow" added =>    14 elements in stack:

       1,'w' [119]
       2,'o' [111]
       3,'l' [108]
       4,'f' [102]
       5,'r' [114]
       6,'e' [101]
       7,'v' [118]
       8,'O' [ 79]
       9,' ' [ 32]
      10,'k' [107]
      11,'c' [ 99]
      12,'a' [ 97]
      13,'t' [116]
      14,'S' [ 83]
-----

这比循环或交互式代码好得多

栈文件

stuff.h栈头文件

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

typedef struct st_node
{
    char            ch;
    struct st_node* prev;
} Node;

typedef struct
{
    unsigned size;
    Node*    top;
} Stack;

Stack* create();
Stack* destroy(Stack*);

char pop(Stack* stack);
int  push(char c, Stack* stack);
// helper function to show stack contents
int show(Stack* stack, const char* msg);
// helper function to put a string in a stack
int  str_to_stack(const char* str, Stack* stack);
char top(Stack* stack);

stuff.c堆栈实现

#include "stuff.h"

Stack* create()
{
    Stack* stack = (Stack*)malloc(sizeof(Stack));
    if (stack == NULL) return NULL;
    stack->size = 0;  // empty
    stack->top  = NULL;
    return stack;
}

Stack* destroy(Stack* stack)
{
    if (stack == NULL) return NULL;  // no stack
    while (stack->size > 0)
    {
        Node* prev = stack->top->prev;  // save
        free(stack->top);               // destroy top
        stack->top = prev;              // reassign
        stack->size -= 1;
    }
    free(stack);  // now the stack 
    return NULL;
}

int push(char c, Stack* stack)
{
    if (stack == NULL) return -1;
    Node* nw = (Node*)malloc(sizeof(Node));
    if (nw == NULL) return -1;
    nw->ch     = c;
    nw->prev   = stack->top;
    stack->top = nw;
    stack->size += 1;
    return 0;
}

char pop(Stack* stack)
{
    if (stack == NULL) return -1;
    if (stack->size == 0) return -1;
    char  top  = stack->top->ch;    /// save it
    Node* temp = stack->top;        // save the pointer
    stack->top = stack->top->prev;  // go back
    stack->size -= 1;               // 1 less
    free(temp);
    return top;
}

int show(Stack* stack, const char* msg)
{
    if (stack == NULL) return -1;
    if (msg != NULL) printf("%s", msg);
    if (stack->size == 0)
    {
        printf("    stack is empty\n");
        return 0;
    }
    printf("    %d elements in stack:\n\n", stack->size);
    Node* p = stack->top;
    for (unsigned i = 0; i < stack->size; i += 1)
    {
        printf("%8d,'%c' [%3d]\n", 1+i, p->ch, (int)p->ch);
        p = p->prev;
    }
    printf("-----\n");
    return 0;
}

int str_to_stack(const char* str, Stack* stack)
{
    if (stack == NULL) return -1;
    if (str == NULL) return -2;
    const char* p = str;
    while (*p != 0)
    {
        push(*p, stack);
        p++;
    };
    return 0;
}

char top(Stack* stack)
{  // return char in the top, or negative value
    if (stack == NULL) return -1;
    if (stack->size == 0) return -2;
    return stack->top->ch;
}

因此,我们可以在任何程序中包含头文件,只需编译stuff.c即可

完整测试

int main(void)
{
    Stack* one = create();

    str_to_stack("Stack", one);
    show(one, "\"Stack\" added =>");

    str_to_stack(" Overflow", one);
    show(one, "\" Overflow\" added =>");

    printf("TOP is '%c'\n", top(one));
    show(one,"stack now: ");

    // copy to another stack
    Stack* other = create();
    printf("Reversing a stack into another\n");
    while (one->size > 0) push(pop(one), other);

    show(other, "new stack (reversed): ");
    show(one, "original (should be now empty): ");
    one = destroy(one);  // destroy stack, clear pointer
    other = destroy(other);  
    return 0;
}

这个程序

  • 创建one堆栈
  • 推送字符串“Stack”
  • 显示堆栈内容
  • push字符串Overflow
  • 显示堆栈顶部
  • 创建第二个堆栈other
  • one复制到other
  • 列出两个堆栈
  • 摧毁两个堆栈

输出

"Stack" added =>    5 elements in stack:

     1,'k' [107]
     2,'c' [ 99]
     3,'a' [ 97]
     4,'t' [116]
     5,'S' [ 83]
-----
" Overflow" added =>    14 elements in stack:

     1,'w' [119]
     2,'o' [111]
     3,'l' [108]
     4,'f' [102]
     5,'r' [114]
     6,'e' [101]
     7,'v' [118]
     8,'O' [ 79]
     9,' ' [ 32]
    10,'k' [107]
    11,'c' [ 99]
    12,'a' [ 97]
    13,'t' [116]
    14,'S' [ 83]
-----
TOP is 'w'
stack now:     14 elements in stack:

     1,'w' [119]
     2,'o' [111]
     3,'l' [108]
     4,'f' [102]
     5,'r' [114]
     6,'e' [101]
     7,'v' [118]
     8,'O' [ 79]
     9,' ' [ 32]
    10,'k' [107]
    11,'c' [ 99]
    12,'a' [ 97]
    13,'t' [116]
    14,'S' [ 83]
-----
Reversing a stack into another
new stack (reversed):     14 elements in stack:

     1,'S' [ 83]
     2,'t' [116]
     3,'a' [ 97]
     4,'c' [ 99]
     5,'k' [107]
     6,' ' [ 32]
     7,'O' [ 79]
     8,'v' [118]
     9,'e' [101]
    10,'r' [114]
    11,'f' [102]
    12,'l' [108]
    13,'o' [111]
    14,'w' [119]
-----
original (should be now empty):     stack is empty

evaluate()怎么样

现在我们有一个堆栈可以使用。让我们将evaluate更改为

int is_wcwr(const char* str)

如果str是预期格式的字符串,则返回1的函数。

可能的实现

int is_wcwr(const char* str)
{
    if (str == NULL) return 0;
    // size must be odd
    size_t size = strlen(str);
    if (size % 2 == 0) return 0; // false
    // must have 'c' or 'C' in the center
    size_t pivot = size / 2;
    if ((str[pivot] != 'c') && (str[pivot] != 'C'))
        return 0;
    Stack* stack = create();
    // put 2nd half into stack
    str_to_stack((const char*) str+pivot+1, stack);
    // now compares 1st half of string with stack contents
    for (size_t i = 0; i < pivot; i+=1)
    {   // pair is ch_l ch_r
        char ch_l = *(str + i);
        char ch_r = pop(stack);
        if (ch_l != ch_r)
        {
            stack = destroy(stack);
            return 0;
        }
    }
    stack = destroy(stack);
    return 1;
}

逻辑很简单,实际上不需要堆栈。但我们肯定可以用一个。不变量:

  • 字符串的长度必须是奇数
  • 在中心,我们有cC

所以

  • 我们创建一个堆栈,并将字符串的另一半推入堆栈。
  • 比较堆栈中的值与字符串中的值,只要它们相等
  • 发现差异后立即返回

测试程序

int main(void)
{
  const char* res[]    = {"is not", "is"};
  const char* test[] = {
      "123456C654321",
      "23456C654321",
      "",
      "test",
      "tests",
      "tects",
      "teCts",
      "xyzCzyx"
  };

  const unsigned N = sizeof(test) / sizeof(test[0]);

  printf("Total of %d strings to test\n", N);
  for (unsigned i = 0; i < N; i += 1)
      printf(
          "\t\"%s\" %s in the wcwR format\n",
          test[i], res[is_wcwr(test[i])]);
  return 0;
}

此代码允许在顶部输入测试字符串并运行代码。一个单循环。没有读取,没有文件。

输出

Total of 8 strings to test
        "123456C654321" is in the wcwR format
        "23456C654321" is not in the wcwR format
        "" is not in the wcwR format
        "test" is not in the wcwR format
        "tests" is not in the wcwR format
        "tects" is not in the wcwR format
        "teCts" is not in the wcwR format
        "xyzCzyx" is in the wcwR format

完整代码

main.c

#include <string.h>
#include "stuff.h"

int is_wcwr(const char*);

int main(void)
{
    const char* res[]    = {"is not", "is"};
    const char* test[] = {
        "123456C654321",
        "23456C654321",
        "",
        "test",
        "tests",
        "tects",
        "teCts",
        "xyzCzyx"
    };
    const unsigned N = sizeof(test) / sizeof(test[0]);

    printf("Total of %d strings to test\n", N);
    for (unsigned i = 0; i < N; i += 1)
        printf(
            "\t\"%s\" %s in the wcwR format\n",
            test[i], res[is_wcwr(test[i])]);
    return 0;
}

int is_wcwr(const char* str)
{
    if (str == NULL) return 0;
    // size must be odd
    size_t size = strlen(str);
    if (size % 2 == 0) return 0; // false
    // must have 'c' or 'C' in the center
    size_t pivot = size / 2;
    if ((str[pivot] != 'c') && (str[pivot] != 'C'))
        return 0;
    Stack* stack = create();
    // put 2nd half into stack
    str_to_stack((const char*) str+pivot+1, stack);
    // now compares 1st half of string with stack contents
    for (size_t i = 0; i < pivot; i+=1)
    {   // pair is ch_l ch_r
        char ch_l = *(str + i);
        char ch_r = pop(stack);
        if (ch_l != ch_r)
        {
            stack = destroy(stack);
            return 0;
        }
    }
    stack = destroy(stack);
    return 1;
}
gmol1639

gmol16392#

你的异常并没有发生在你认为它发生的地方。弹出时你没有检查空栈,即。top == NULL。然后,您尝试用top->ch解引用NULL
代码还有其他问题。使用fgets()而不是gets(),后者已被弃用。if flag == 0结尾没有返回值(如果“c”或“C”后面没有字符,则可以返回)。只有return flag;没有if。你的for循环可以在字符串的末尾运行,因为你没有检查\0。您没有检查malloc()的返回值。

相关问题