提问人:Nishu Patel 提问时间:9/6/2023 最后编辑:chqrlieNishu Patel 更新时间:9/9/2023 访问量:87
发生异常,在 c 中使用 free() 时出现分段错误;
Exception occurred, segmentation fault while using free() in c;
问:
我的代码以某种方式运行,但它没有给出预期的结果,所以为了理解它,我开始调试,它显示发生了异常,我使用的行出现了分段错误。
这是我的代码: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;
}
请帮我解决这个问题,任何人都可以建议任何合适的技术来自己解决这样的问题吗?谢谢。
答:
您的异常不会发生在您认为发生的地方。弹出时您不会检查空堆栈,即 .然后,您正在尝试使用 取消引用。top == NULL
NULL
top->ch
代码还存在其他问题。代替 使用,后者已弃用。末尾没有返回值 if(如果“c”或“C”后面没有字符,则可以返回)。只是没有.您的循环可以从字符串的末尾运行,因为您没有检查 .您没有检查 的返回值。fgets()
gets()
flag == 0
return flag;
if
for
\0
malloc()
我将向你展示一种解决此类问题的通用方法。我什至不会试图说它是一个好方法,但它总体上对我有用,并且在第一次运行中就在这里工作,至少在我运行的几次测试中是这样。
(这是一篇很长的文章,因为我正在使用从代码中复制和粘贴以及注释来构建示例)。
关于发布的代码
我不清楚你想做什么。一般来说,最好在代码之前发布任务的描述。阅读代码,我相信:
- 你的目标是函数
evaluate()
- 这个想法是检查格式 wCwR,一个像“1234C4321”这样的字符串
- 中心有一个 OR
C
c
- 如果 前面的字符串在 之后反转,则返回
C
C
evaluate
true
- 中心有一个 OR
- A 的使用是根据要求。
stack
笔记:
- 不要使用固定值,如 .封装事物和使用 jus 指针更容易
arr[100]
- 不要编写交互式代码。它太慢了,太不方便了。使用文件、常量、工厂函数。
- 永远不要使用.几十年来,它已被弃用
gets
- 返回
0
false
- 永远不要使用全局性的东西
- 将代码写入(并测试它)就像将代码写入内存一样。
free
malloc
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 个堆栈变得微不足道。就像在 RAII 概念中一样,我们将首先编写 和 .C++
create
destroy
和 的可能代码create
destroy
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 语言中有一个邪教,说不要在 中使用强制转换。我会的,永远。作为对程序员和读者的提醒。即使是像这样非常简单的代码,也会为OR分配内存,如你所见。编译器不需要这种强制转换,但通过编写和重复它,您可以确定什么是什么,并防止许多错误并为您省去很多麻烦。那种耗费金钱、工作和时间的麻烦。malloc()
Node
Stack
(这个邪教是基于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”
- 显示堆栈内容
- 推送字符串
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)
返回 if 的函数是预期格式的字符串。1
str
可能的实现
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;
}
逻辑很简单,实际上不需要堆栈。但我们肯定可以使用一个。 不变量:
- 字符串的长度必须为奇数
- 在中心,我们有或
c
C
所以
- 我们创建一个堆栈,并将字符串的后半部分推入堆栈。
- 将堆栈中的值与字符串中的值进行比较,只要它们相等即可
- 发现差异后立即返回
测试程序
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;
}
评论
char
评论
gets()
for
pop()
不检查堆栈是否为空。evaluate
int
flag != 1