【问题标题】:Segmentation fault in stack in push Function C language推送函数C语言中堆栈中的分段错误
【发布时间】:2017-11-20 19:25:52
【问题描述】:

我在启动程序时遇到了问题,它在函数 Full 处崩溃并推送第 70 和 78 行 (Linux)。我尝试修复它,但我总是在同一个地方崩溃。

   #include <stdio.h>
   #define SIZE 64

   struct stack{
      int TowerTop;
      int Elem[SIZE];
   };

   /* Forward Declarations */                    
   void push(int Elem, struct stack *x);
   int  create (struct stack *x);
   int  full(const struct stack *x);
   void pop(struct stack *x);
   int lolol(struct stack *x,struct stack *x2,struct stack *x3,int a);

   void main() {
      int j;
      int a = 5; 

      //scanf("%d",&a);

      struct stack Tower1;
      struct stack Tower2;
      struct stack Tower3;

      create(&Tower1);
      create(&Tower2);
      create(&Tower3);

      for(int i=0; i<=SIZE-1; i++) {
          Tower1.Elem[i]=0;
          Tower2.Elem[i]=0;
          Tower3.Elem[i]=0; 
      }

      for(int i=0; i<=a; i++){
          Tower1.Elem[i]=i+1;
      }

      // Display initial tower setup
      for(int i=0; i<a; i++){
         printf("%d %7d %7d\n",Tower1.Elem[i],Tower2.Elem[i],Tower3.Elem[i]);
      }

      lolol(&Tower1,&Tower2,&Tower3,a);

      // Display Tower after move made by lolol
      printf("%d\n", j);
      for(int i=0; i<a; i++) {
         printf("%d %7d %7d\n",Tower1.Elem[i],Tower2.Elem[i],Tower3.Elem[i]);
      }
    }

    int lolol(struct stack *x,struct stack *x2,struct stack *x3,int a) {
       if(a == 1) {
          printf("\n Disk 1 move to");
          push(x->Elem[x->TowerTop], x2->Elem[x2->TowerTop]); j++;
          pop(x->Elem[x->TowerTop]);
          return 0; 
       }

       lolol(x, x3, x2, a-1);
       push(x->Elem[x->TowerTop], x2->Elem[x2->TowerTop]); j++;
       pop(x->Elem[x->TowerTop]);
       lolol(x3, x2, x, a-1);
      }

      int create(struct stack *x) {
         x->TowerTop = -1;
      }

      int full(const struct stack *x) {
          if (x->TowerTop == SIZE-1) {
             return 1;
          } else {
             return 0;
          } 
       }

       void push(int Elem, struct stack *x){
          if(full(x)) {
             printf("Stack is full"); 
          } else {
             x->TowerTop++;
             x->Elem[x->TowerTop]=Elem;
          }
       }

       void pop( struct stack *x) {
          if(x->TowerTop== -1) {
             printf("Empty");
          } else {
             x->TowerTop--;
          }
       }

【问题讨论】:

  • 你做了什么调试?您查过导致 Segfault 的原因吗?
  • 漂亮的缩进。
  • 了解How to Ask 并提供minimal reproducible example。注意:家庭作业问题有附加要求才能成为主题。你的老师希望你自己解决。编程包括调试。习惯就好。
  • @tkausl:请记住,初学者会认真对待这样的评论(我希望你在开玩笑,是吗?)。 OP:了解编码风格并使用通用的!不要在格式方面有创意,这是有原因的
  • 您的程序的函数参数与调用参数不匹配,并且函数没有返回值。请启用所有编译器警告(它给了我 10 个)并跟进。

标签: c linux stack segmentation-fault towers-of-hanoi


【解决方案1】:

我们将一起调试您的代码。但首先,我会告诉你更多关于如何在论坛上提问(因此在 StackOverflow 上)。

首先没有“尽可能快”,因为我们都是来帮忙的。我们会尽力而为,它可能会也可能不会准时。然后请用您的代码再次阅读您的消息并问自己,这是否可读?我真的希望你的代码在你的文件中看起来不一样,而你只是以某种方式复制/粘贴失败了,因为这段代码非常不可读。

第一步:更改代码使其可读。

我将跳过这一步的冗长,但基本上它是缩进,在需要时添加空格等。

#include <stdio.h>

#define SIZE 64

struct stack
{
  int TowerTop;
  int Elem[SIZE];
};

void push(int Elem, struct stack *x);
void pop(struct stack *x);
int create (struct stack *x);
int full(const struct stack *x);
int lolol(struct stack *x, struct stack *x2, struct stack *x3, int a);

int a;
int j;

void main()
{

  //scanf("%d", &a);
  a = 5;
  struct stack Tower1;
  struct stack Tower2;
  struct stack Tower3;
  create(&Tower1);
  create(&Tower2);
  create(&Tower3);

  for (int i = 0; i <= SIZE - 1; i++)
    {
      Tower1.Elem[i] = 0;
      Tower2.Elem[i] = 0;
      Tower3.Elem[i] = 0;
    }

  for (int i = 0; i <= a; i++)
    {
      Tower1.Elem[i] = i + 1;
    }

  for (int i = 0; i < a; i++)
    {
      printf("%d %7d %7d\n", Tower1.Elem[i], Tower2.Elem[i], Tower3.Elem[i]);
    }

   lolol(&Tower1, &Tower2, &Tower3, a);

   printf("%d\n", j);

   for (int i = 0; i < a; i++)
    {
      printf("%d %7d %7d\n", Tower1.Elem[i], Tower2.Elem[i], Tower3.Elem[i]);
    }
}

int lolol(struct stack *x, struct stack *x2, struct stack *x3, int a)
{
  if (a == 1)
    {
      printf("\n Disk 1 move to");
      push(x->Elem[x->TowerTop], x2->Elem[x2->TowerTop]);
      j++;
      pop(x->Elem[x->TowerTop]);
      return 0;
    }
  lolol(x, x3, x2, a - 1);
  push(x->Elem[x->TowerTop], x2->Elem[x2->TowerTop]);
  j++;
  pop(x->Elem[x->TowerTop]);
  lolol(x3, x2, x, a - 1);
}

int create(struct stack *x)
{
  x->TowerTop = -1;
}

int full(const struct stack *x)
{
  if (x->TowerTop == SIZE-1)
    {
      return 1;
    }
  else
    {
      return 0;
    }
}

void push(int Elem, struct stack *x)
{
  if (full(x))
    {
      printf("Stack is full");
    }
  else
    {
      x->TowerTop++;
      x->Elem[x->TowerTop] = Elem;
    }
}

void pop(struct stack *x)
{
  if (x->TowerTop == -1)
    {
      printf("Empty");
    }
  else
    {
      x->TowerTop--;
    }
}

请不要说我没有更改任何逻辑或类型或其他任何东西。

第二步:读取代码。

实际上,我稍后可能会回到这个问题,但我快速阅读了一下,有一些基本的东西需要说一下。 main 函数应始终返回 int。根据C标准,main函数只能通过两种方式声明:int main(void)(不带参数)或int main(int argc, char *argv[]);(带命令行参数)。

那么,你必须知道aj 这两个变量是文件的全局变量。这意味着您可以在同一个源文件中的任何地方访问它们。好听,不行你永远不应该做全局变量,除非你别无选择(例如使用sigaction)。在你的情况下,它们真的不需要。

最后,评论。请推荐您的代码。虽然这里的大部分内容都很容易理解,但函数 lolol(顺便说一句,这个名字根本没有意义)正在做一些我在快速查看代码时无法理解的事情。

第三步:编译

gcc file.c => 4 个警告。除非您知道自己在做什么并且是一位经验丰富的 C 程序员——您还不是(还不是!)您在编译时不应该有任何警告。此外,您应该使用-Wall -Wextra 开关进行编译。这将引发更多警告,但允许您在错误存在之前发现它们。

所以我们将修复警告:

  • warning: return type of ‘main’ is not ‘int’ [-Wmain]。好吧,让我们切换它。 void main() 变成 int main(void),正如我之前所说的。
  • file.c:64:34: warning: passing argument 2 of ‘push’ makes pointer from integer without a cast [-Wint-conversion] => 好吧,你在这里使用push 函数,我假设你写的。但是,此函数需要 int 作为它的第一个参数和 struct stack * 作为它的第一个参数 第二个参数,当你传递一个int 时。让我们解决这个问题:push(x-&gt;Elem[x-&gt;TowerTop], x2-&gt;Elem[x2-&gt;TowerTop]); 变为 push(x-&gt;Elem[x-&gt;TowerTop], x2);,因为您将 x-&gt;Elem[x-&gt;TowerTop] 推到了 x2 堆栈的顶部。
  • 接下来的三个警告也是如此,分别是第 66 行 pop、第 70 行 push 和第 72 行 pop
  • 接下来的两个警告是相同的:file.c:74:1: warning: control reaches end of non-void function [-Wreturn-type] => 我仍然不确定lolol 是什么意思,所以我将在它的末尾添加一个return 0,然后切换@ 987654351@ 函数为void 类型。

这是更正后的代码:

#include <stdio.h>

#define SIZE 64

struct stack
{
  int TowerTop;
  int Elem[SIZE];
};

void push(int Elem, struct stack *x);
void pop(struct stack *x);
void create (struct stack *x);
int full(const struct stack *x);
int lolol(struct stack *x, struct stack *x2, struct stack *x3, int a);

int a;
int j;

int main(void)
{

  //scanf("%d", &a);
  a = 5;
  struct stack Tower1;
  struct stack Tower2;
  struct stack Tower3;
  create(&Tower1);
  create(&Tower2);
  create(&Tower3);

  for (int i = 0; i <= SIZE - 1; i++)
    {
      Tower1.Elem[i] = 0;
      Tower2.Elem[i] = 0;
      Tower3.Elem[i] = 0;
    }

  for (int i = 0; i <= a; i++)
    {
      Tower1.Elem[i] = i + 1;
    }

  for (int i = 0; i < a; i++)
    {
      printf("%d %7d %7d\n", Tower1.Elem[i], Tower2.Elem[i], Tower3.Elem[i]);
    }

   lolol(&Tower1, &Tower2, &Tower3, a);

   printf("%d\n", j);

   for (int i = 0; i < a; i++)
    {
      printf("%d %7d %7d\n", Tower1.Elem[i], Tower2.Elem[i], Tower3.Elem[i]);
    }
}

int lolol(struct stack *x, struct stack *x2, struct stack *x3, int a)
{
  if (a == 1)
    {
      printf("\n Disk 1 move to");
      push(x->Elem[x->TowerTop], x2);
      j++;
      pop(x);
      return 0;
    }
  lolol(x, x3, x2, a - 1);
  push(x->Elem[x->TowerTop], x2);
  j++;
  pop(x);
  lolol(x3, x2, x, a - 1);
  return 0;
}

void create(struct stack *x)
{
  x->TowerTop = -1;
}

int full(const struct stack *x)
{
  if (x->TowerTop == SIZE-1)
    {
      return 1;
    }
  else
    {
      return 0;
    }
}

void push(int Elem, struct stack *x)
{
  if (full(x))
    {
      printf("Stack is full");
    }
  else
    {
      x->TowerTop++;
      x->Elem[x->TowerTop] = Elem;
    }
}

void pop(struct stack *x)
{
  if (x->TowerTop == -1)
    {
      printf("Empty");
    }
  else
    {
      x->TowerTop--;
    }
}

在没有 SIGSEGV 的情况下编译和运行。

最后一步:走得更远

我将讨论更高级的主题,但它可能对您有用。首先,获取有关堆栈及其工作原理的更多信息,因为我不确定您是否完全理解它。

另外,要调试您的程序,您可以使用valgrind,它是一个非常有用的内存检查工具,而且总是派上用场。要使用它,请在编译时添加标志-g

我建议也研究一下gdb,GNU Linux 调试器。非常强大。

函数full 可以更改为简单的return x-&gt;TowerTop == SIZE - 1。它避免了分支,因此运行速度更快。

这就是所有人。仍然不确定您的代码应该做什么,但您只询问了如何修复您的段错误。现在请记住。 编译时不要出现任何警告。

谢谢。

【讨论】:

  • 非常感谢。我很抱歉我的话。英语不是我想要习惯的第一语言
  • 别担心,没关系。我只是希望你能理解你的错误并成为一个更好的程序员。 :)
  • @MatthewDarens 如果能解决您的问题,请接受 Doe 的回答
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-28
  • 2020-07-23
  • 2017-02-08
  • 2021-12-02
  • 2021-10-15
相关资源
最近更新 更多