C语言push Function中栈中的段错误

Segmentation fault in stack in push Function C language

我在启动程序时遇到了问题,它在函数 Full 和 push 行 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--;
          }
       }

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

首先没有 'as fast as possible' 因为我们都在这里提供帮助。我们会尽力而为,它可能准时也可能不准时。然后请用您的代码再次阅读您的消息并问问自己,这是可读的吗?我真的希望你的代码在你的文件中看起来不一样,你只是以某种方式失败了 copy/paste 因为这段代码很难读。

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

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

#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 函数应始终 returns 和 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->Elem[x->TowerTop], x2->Elem[x2->TowerTop]); 变为 push(x->Elem[x->TowerTop], x2);,因为您将 x->Elem[x->TowerTop] 推到 x2 堆栈的顶部。
  • 接下来的三个警告相同,即 pop 第 66 行、push 第 70 行和 pop 第 72 行。
  • 接下来的两个警告是相同的:file.c:74:1: warning: control reaches end of non-void function [-Wreturn-type] => 我仍然不确定 lolol 是什么意思,所以我将在下面添加一个 return 0它结束,并将 create 函数切换为 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 的情况下编译和 运行ning。

最后一步:更进一步

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

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

我建议也研究 gdb GNU Linux 调试器。很给力。

函数full可以改成简单的return x->TowerTop == SIZE - 1。它避免了分支,因此 运行 更快。

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

谢谢。