【问题标题】:Segmentation fault when I try to printf当我尝试 printf 时出现分段错误
【发布时间】:2016-01-10 20:12:49
【问题描述】:

当它尝试打印列表中的第二个成员时,为什么我得到一个 segmentationfault

打印列表的第一个元素后,调试器打开 stdio.h 并显示:

在 C:\TDM-GCC-32\include\stdio.h:255
在 C:\TDM-GCC-32\include\stdio.h:256
在 C:\TDM-GCC-32\include\stdio.h:258
在 C:\TDM-GCC-32\include\stdio.h:259

这里是代码。

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

struct Student {
    char *Name;
    char *Adresse;
    unsigned long Mtnr;
    short Kurse;

    struct Student *next;
    struct Student *previous;
};

typedef struct Student Student;

Student *liste = NULL, *ende = NULL;

void add(char Name, char Adresse, unsigned long Mtnr, short Kurse) {
    Student *add;

    ende->next = malloc(sizeof(Student));
    add = ende->next;

    add->Name = Name;
    add->Adresse = Adresse;
    add->Mtnr = Mtnr;
    add->Kurse = Kurse;
    add->previous = ende;
    add->next = NULL;
    ende = ende->next;
}

void Ausgabe(Student *Anfang) {

    while (Anfang != NULL) {
        printf("%s %s %d %d \n", Anfang->Name, Anfang->Adresse, Anfang->Mtnr, Anfang->Kurse);
        Anfang = Anfang->next;
    }
}

int main() {
    liste = malloc(sizeof(Student));
    ende = liste;
    liste->Name = "Anna Musterfrau";
    liste->Adresse = "Am Schwarzberg-Campus 3";
    liste->Mtnr = 22222;
    liste->Kurse = 2;
    liste->next = NULL;
    liste->previous = NULL;

    add("Hans Peter", "Kasernenstrasse 4", 4444, 4);

    Ausgabe(liste);

    return 0;
}

【问题讨论】:

  • %d 不适用于unsigned long..... %lu 似乎合适

标签: c struct


【解决方案1】:

错误出现在add() 函数的声明中。字符串应该是字符指针,而不是字符。

void add(char *Name, char *Adresse, unsigned long Mtnr, short Kurse){

【讨论】:

    【解决方案2】:

    函数add的签名与Student的成员的声明和使用不一致。更改签名如下。

    void add(char* Name, char* Adresse, unsigned long Mtnr, short Kurse)
    

    从长远来看,可能还需要在add 中创建NameAdresse 的副本,因为add 的调用者可能会释放它们,可能会导致不良行为。

    【讨论】:

      【解决方案3】:

      虽然 Marc 的观察是正确的,但您可能还需要在此处解决一件事。

      当您添加一条记录时,您分配它,但您不分配它的指针的内容(特别是 = 用于名称和地址指针)。 add 函数只是将它们指向输入的地址。这是一个问题,因为提供给 add 函数的地址中的数据可能会发生变化,例如,如果它是用户输入或其他一些外部缓冲区。

      在下面的代码中,我修复了“名称”,但保留了地址。请运行它,看看会发生什么。 (assaf 的记录显示 david 的地址)

      希望对你有帮助

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      
      struct Student {
          char *Name;
          char *Adresse;
          unsigned long Mtnr;
          short Kurse;
      
          struct Student *next;
          struct Student *previous;
      };
      
      typedef struct Student Student;
      
      Student *liste = NULL, *ende = NULL;
      
      void add(char *Name, char *Adresse, unsigned long Mtnr, short Kurse) {
          Student *add;
      
          ende->next = malloc(sizeof(Student));
          add = ende->next;
      
          add->Name = malloc(strlen(Name)+1);
          strcpy(add->Name, Name);
          add->Adresse = Adresse;
          add->Mtnr = Mtnr;
          add->Kurse = Kurse;
          add->previous = ende;
          add->next = NULL;
          ende = ende->next;
      }
      
      void Ausgabe(Student *Anfang) {
      
          while (Anfang != NULL) {
              printf("%s %s %d %d \n", Anfang->Name, Anfang->Adresse, Anfang->Mtnr, Anfang->Kurse);
              Anfang = Anfang->next;
          }
      }
      
      int main() {
          char name_buf[100];
          char address_buf[100];
          liste = malloc(sizeof(Student));
          ende = liste;
          liste->Name = "Anna Musterfrau";
          liste->Adresse = "Am Schwarzberg-Campus 3";
          liste->Mtnr = 22222;
          liste->Kurse = 2;
          liste->next = NULL;
          liste->previous = NULL;
      
          add("Hans Peter", "Kasernenstrasse 4", 4444, 4);
      
          sprintf(name_buf,"assaf stoler");
          sprintf(address_buf,"maria 8");
          add(name_buf, address_buf, 8888, 8);
      
          sprintf(name_buf,"david david");
          sprintf(address_buf,"some street 9");
          add(name_buf, address_buf, 9999, 9);
      
      
          Ausgabe(liste);
      
          return 0;
      }
      

      编辑:Op问了一些问题,评论空间有限,所以我会在下面补充:

      指针只是一个指向内存中某处的对象。它的大小是固定的。它指向的内容会有所不同。

      当您在结构中包含指向字符串的指针时,需要分配/说明保存字符串的空间。它不是 sizeof(struct) 的一部分。

      在您的原始示例中,指针指向常量字符串(位于静态代码中,通常是编译器分配的数据部分),这就是您的原始代码能够访问字符串的原因。

      在更现实的情况下,输入数据不是程序数据的一部分,而是由某种输入方法接收(我的 *buf 是要模拟的)。因此,将您的姓名和地址指向它会破坏程序,因为您指向的指针可能会改变它的内容。因此需要复制数据(字符串/数组),并且由于我们复制数据,我们需要为其分配空间,并将我们的(名称/地址)指针指向它。

      替代选项是使用非指针数组作为名称和地址,如下所示:

      struct Student {
          char Name[20];
          char Adresse[60];
          unsigned long Mtnr;
          ...
      }
      

      在这种情况下, sizeof (struct Student) 实际上会包含这些字段的所有空间。您仍然需要使用 strcpy 或 memcpy,以及检查和处理太长而不适合您的预定义长度的字符串。

      希望对你有帮助

      【讨论】:

      • add-&gt;Name = malloc(strlen(Name)); strcpy(add-&gt;Name, Name); 应该是add-&gt;Name = malloc(strlen(Name)+1);
      • 我问过一次,有人告诉我如果我为结构分配内存,它也会自动为内容分配,所以 malloc(sizeof(Student)) 变成 2x malloc(sizeof(char * ))、malloc(sizeof(int)) 等,因为 sizeof(char *) == 8byte (我相信)它已经足够了,不是吗?为什么不在主函数中为列表中的第一个元素更改它?顺便说一句:为什么你使用缓冲区作为名称和地址,这在某种程度上更有用吗?如果你只能使用 '=' ,你为什么要使用 strcpy 。这对您来说更容易吗?
      • 很多问题。 1)我只修复了“名称”,因为我想向您展示在真实场景中会发生什么,并留下一些供您练习。 2)分配 sizeof(struct) 将为它分配足够的内存,但是,您的名称和地址并定义为指针,即它们指向另一个内存位置(这也使 ''=' 工作,因为它设置在它的位置指向)。所以分配的大小是指针的大小(对于名称,地址)。你不能指望在那里存储一个字符串,因为它对大多数人来说太短了。继续...
      • 如果您要使用字符数组(如在名称 [20] 中),那么单个 malloc() 就可以了,但是您会冒着用很长的时间超出数组运行的风险姓名。我添加的缓冲区只是演示了一个可能的用例,其中字符串是真实输入,而不是硬编码到程序中。在这种情况下,将指针指向它们会暴露一个错误。希望有帮助
      • 非常感谢,它实际上帮助我理解了!
      猜你喜欢
      • 1970-01-01
      • 2017-08-30
      • 1970-01-01
      • 2013-01-27
      • 1970-01-01
      • 2015-08-27
      • 2023-04-02
      • 2023-03-11
      • 1970-01-01
      相关资源
      最近更新 更多