【问题标题】:This code gives me undefined behaviour on Windows, but works fine on Linux这段代码在 Windows 上给了我未定义的行为,但在 Linux 上工作正常
【发布时间】:2013-09-11 16:45:06
【问题描述】:

我正在尝试用 C 语言编写一个小型双向链表程序,但由于某种原因,它给了我第一个元素的未定义行为。我希望它在开头有一个空单元格,用于链接第一个和最后一个元素。所以是这样的:... <-> Second Last <-> Last <-> Empty Cell <-> First <-> Second <->...

第一个元素是随机值,但下一个元素有效。例如,如果我的输入文件是 1 2 3 4 5,则输出将为 <undefined>2 3 4 5,其中 undefined 可以是 C 希望给我的任何数字。

奇怪的是它在调试模式下也能完美运行(使用 MinGW Developer Studio,因为我从学校习惯了它)。它在 Linux 下也很好用(使用 gcc 进行编译)。

这是代码:

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

typedef struct Nod {
struct Nod *next, *ant;
int x;
} Nod_t, *List_t, **AList_t;

void PrintList (List_t sant){
List_t lista = sant->next;
while(lista != sant){
    printf("%i ", lista->x);
    lista = lista->next;
}
}

List_t PopulateList(char* fis){

List_t lista, sant;
int nr;
FILE *f = fopen(fis, "rt");

sant = (List_t)malloc(sizeof(List_t));
sant->next = sant->ant = NULL;
lista = sant;
//First node

while(!feof(f)){
    fscanf(f, "%i", &nr);
    lista->next = (List_t)malloc(sizeof(List_t));
    lista->next->x = nr;
    lista->next->ant = lista;
    lista = lista->next;
}

sant->ant = lista;
lista->next = sant;

return sant;

}

int main (){

List_t lista1;
lista1 = PopulateList("1.txt");
PrintList(lista1);
return 0;
}

这里的缩进更好:http://pastebin.com/NVQqaYHK

【问题讨论】:

  • UB 与语言相关,而不是与操作系统相关(好吧,或多或少,无论如何)。如果它是 Windows 上的 UB,那么它也可能是 Linux 上的 UB。您可能想将您的标题改写为“此代码在 Windows 上崩溃但在 Linux 上没有崩溃,我认为它调用了 UB”。此外,格式化您的代码(空格、缩进等),因为很难按原样阅读。此外,决定您使用哪种语言编写程序。 这是 C XOR C++?它不能同时是两者,它们是非常不同且不同的语言。
  • @PirvuMihai-Cristian "Linux 上的未初始化变量设置为 0"
  • @PirvuMihai-Cristian 你只是(不)幸运。
  • @PirvuMihai-Cristian C++ 不是 C 的超集,它是具有许多不同规则的完全不同的语言。例如int *x = malloc(sizeof(int)); 是合法的 C,但在 C++ 中会导致编译时错误。
  • 我很好奇,fopen(x, "rt") 中的“t”是什么意思?

标签: c list linked-list mingw


【解决方案1】:

尝试从

更改您的 malloc
sant = (List_t)malloc(sizeof(List_t));

sant = malloc(sizeof(Nod_t));

sant = malloc(sizeof(List_t)); 返回一个指针大小的区域。

sant = malloc(sizeof(Nod_t)); 返回一个指向 Nod_t 大小的区域的指针。

顺便说一句,这是样式问题,但是您的类型别名令人困惑,并且可能导致您在 malloc 中的拼写错误。我建议只使用 Nod_t 类型并摆脱 List_t 和 AList_t。考虑以下声明:

List_t foo; // is this a pointer or a struct? Can't tell from the decl, need to know the typedef.
Nod_t *foo; // obviously a pointer

另请参阅 Zack 的建议,尤其是关于 feof() 问题。

【讨论】:

  • 这是正确的(除了转换malloc的结果,这是不应该做的)但只是问题的一部分。
  • 是的,请参阅 Zack 提出的问题。
  • 感谢您指出我明显的错误,List_t 的 sizeof 是指向 Nod_t 的指针。正所谓:int *x; x = (int*)malloc(sizeof(int*));
  • 关于malloc的强制转换,我们老师说这样比较好,因为malloc返回void*。尽管我确信我读到 C 编译器将它们转换为正确的指针类型,但我仍然保留了这一传统。
  • 另外,将 fscanf() 的返回值与 1(要读取的预期项目数)进行比较,以避免双向循环列表中出现意外节点。
【解决方案2】:

错误就在这里:

sant = (List_t)malloc(sizeof(List_t));
sant->next = sant->ant = NULL;
lista = sant;

您没有初始化sant-&gt;x,因此您在列表中有一个条目(包含文件中真实数据的所有条目之前),带有一个未初始化的x

当您稍后为列表中的所有项打印出x 时,会触发未定义的行为(在 Linux 和 Windows 上,要清楚;未定义的行为包括打印的值的可能性永远为零)。

您应该在显示的代码之后立即重新构建循环,以便在您读取文件中的第一行之前不会分配列表中的第一个条目,并且您没有这个初始列表条目具有未初始化的值。

您还需要 Charlie Burns 提到的错误修复。

【讨论】:

  • 不,正如我所说,目标是在开头有一个 NULL 值 Cell。原来的代码中“x”是一个void*,而不是int,所以它会是NULL(或者一个指向排序函数的指针),但这只是一个小代码。
  • 哦,你真的是故意的!这是一个明智的做法,但是您应该将 sant-&gt;x 显式初始化为 0 (或 -1 或其他一些具体值 - 没关系)以避免在标题单元格被打印时出现 UB。
  • 哦,我刚刚注意到,您的 while (!feof(f)) 循环是错误的。 feof 仅在您试图读取超出文件末尾 时才返回 true。如果您已阅读直到文件的末尾,它确实返回true。您应该将其更改为 for (;;) 并将 if (feof(fp)) break; 紧跟在 fscanf 之后。 (这应该会导致列表 end 出现第二个垃圾值。)
  • 我也知道这一点,就好像我在文件中的最后一个元素之后留下一个 \n 甚至一个空格一样,它只会再次读取一个空值。这个错误使得最后一个值被读取两次。我会 while(fscanf(...)) 或者你说的无限循环和中断。
  • 真的,你应该使用fgetsstrtol 而不是scanf。您必须编写更多代码,但更容易确保代码符合您的要求。
猜你喜欢
  • 1970-01-01
  • 2014-03-20
  • 1970-01-01
  • 2019-03-05
  • 2018-06-02
  • 2019-10-14
  • 2019-04-13
  • 2022-08-22
  • 1970-01-01
相关资源
最近更新 更多