对于我的Programming 102类,我们被要求提供在Linux下编译和运行的C代码.我的硬盘驱动器上没有足够的空间来安装Linux和Windows,因此我使用cygwin编译我的程序.
我必须提供的最新程序编译并在cygwin下正常运行.它在Linux下编译很好,但执行中途会产生分段错误.我向给我们上课的研究生解释了这一点,他说cygwin的GCC版本允许编写和执行更粗糙的代码.
我通过谷歌找到的少数参考文献尚无定论.我找到的一个帖子说Linux下的seg故障原因是内存泄漏.为什么这不会影响cygwin版本?
我会使用大学的计算机,但我不能在他们身上使用Subversion,这会严重阻碍我的努力.(我是编码的新手,通常需要能够恢复到X修订版).
cygwin的GCC版本是否真的对它编译的代码更"松懈"?如果是这样,编码时是否有任何明显的问题需要注意?是否有任何替代方法可以编写将在Linux下运行的代码?
编辑
谢谢你的回复.我在原帖中没有明确表示:我的代码中有一个错误对我来说非常重要(我对编程很陌生,毕竟C语言真的很绿).我的TA暗示cygwin的GCC是一个不太可靠的编译器 - 比起在GNU/Linux下发现的代码要运行得更多.我发现这很奇怪,所以在互联网上搜索,但实际上找不到任何对这个事实的引用.
不仅仅是责怪编译器和我的代码,我想知道程序在Windows下运行并在Linux下崩溃的原因是什么.回复:在这方面说明了Windows/Linux下的不同内存管理器和堆/堆栈布局.
结论是cygwin的GCC和GNU/Linux一样"好",它是底层操作系统/纯粹的运气,我的错误程序运行在一个而不是另一个是非常正确的吗?
关于发布源代码,这是一个家庭作业,所以我更愿意自己找到问题,如果可能的话:)
编辑2
我接受了jalf的答案,因为它讨论了什么使程序在Windows下而不是在Linux下运行,这是我真正想知道的.感谢所有贡献的人,他们都是非常有趣和内容丰富的回复.
当我发现问题并修复它时,我会上传一个包含这个非工作版本的所有源代码的zip文件,万一有人好奇地看到我到底做了什么:)
编辑3
对于那些有兴趣看到代码的人,我发现了问题,这确实是由于指针.我试图从函数返回一个指针.我试图返回的指针正在函数内部声明,因此在函数执行后被销毁.问题代码在第22-24行注释掉.
随意嘲笑我的代码.
/** * Returns array of valid searches based on current coordinate */ void determine_searches(int row, int col, int last_row, int last_col, int *active_search){ // define coordinate categories and related valid search directions int Library0[] = {2, 3, 4, -1}; int Library1[] = {4, 5, 6, -1}; int Library2[] = {2, 3, 4, 5, 6, -1}; int Library3[] = {0, 1, 2, 3, 4, 5, 6, 7, -1}; int Library4[] = {0, 1, 2, -1}; int Library5[] = {0, 6, 7, -1}; int Library6[] = {0, 1, 2, 6, 7, -1}; int Library7[] = {0, 1, 2, 3, 4, -1}; int Library8[] = {0, 4, 5, 6, 7, -1}; int * Library[] = { Library0, Library1, Library2, Library3, Library4, Library5, Library6, Library7, Library8, }; // declare (and assign memory to) the array of valid search directions that will be returned //int *active_search; //active_search = (int *) malloc(SEARCH_DIRECTIONS * sizeof(int)); // determine which is the correct array of search directions based on the current coordinate // top left corner int i = 0; if(row == 0 && col == 0){ while(Library[0][i] != -1){ active_search[i] = Library[0][i]; i++; } } // top right corner else if(row == 0 && col == last_col){ while(Library[1][i] != -1){ active_search[i] = Library[1][i]; i++; } } // non-edge columns of first row else if(row == 0 && (col != 0 || col != last_col)){ while(Library[2][i] != -1){ active_search[i] = Library[2][i]; i++; } } // non-edge coordinates (no edge columns nor rows) else if(row != 0 && row != last_row && col != 0 && col != last_col){ while(Library[3][i] != -1){ active_search[i] = Library[3][i]; i++; } } // bottom left corner else if(row == last_row && col == 0){ while(Library[4][i] != -1){ active_search[i] = Library[4][i]; i++; } } // bottom right corner else if(row == last_row && col == last_col){ while(Library[5][i] != -1){ active_search[i] = Library[5][i]; i++; } } // non-edge columns of last row else if(row == last_row && (col != 0 || col != last_col)){ while(Library[6][i] != -1){ active_search[i] = Library[6][i]; i++; } } // non-edge rows of first column else if((row != 0 || row != last_row) && col == 0){ while(Library[7][i] != -1){ active_search[i] = Library[7][i]; i++; } } // non-edge rows of last column else if((row != 0 || row != last_row) && col == last_col){ while(Library[8][i] != -1){ active_search[i] = Library[8][i]; i++; } } active_search[i] = -1; }
Nik Reiman.. 14
我并不是说听起来很粗鲁,但可能你的代码很糟糕,而不是编译器.;)像这样的问题实际上比你想象的更常见,因为不同的操作系统和编译器将有不同的方式在堆栈和堆中组织应用程序的数据.前者可能特别成问题,特别是如果您最终覆盖堆栈上的内存,或引用系统已决定用于其他内容的释放内存.所以基本上,你有时会侥幸逃脱,但有时你的应用会窒息而死.无论哪种方式,如果它是段错误的,那是因为你试图引用你不被允许的内存,所以它更像是一个"快乐的巧合",它没有在另一个系统/编译器下崩溃.
但实际上,段错误是一个段错误,所以你应该调试你的代码来寻找内存损坏而不是调整编译器的配置来找出出错的地方.
编辑:好的,我明白你现在的意思了......我以为你是在解决这个问题,"X很糟糕,但是Y工作得很好!" 态度,但似乎是你的TA有这个.;)
无论如何,这里有一些调试问题的更多提示:
寻找指针算术,引用/解除引用可能的"doh!" 错误.你添加/减去一个(也就是fencepost错误)的任何地方都特别可疑.
注释掉问题区域周围的malloc/free调用,以及使用这些指针的任何相关区域.如果代码停止崩溃,那么你就是朝着正确的方向前进.
假设您至少已经确定了代码崩溃的一般区域,请在其中插入早期的return语句,并找到代码不会崩溃的点.这有助于找到该点与代码实际崩溃之间某处的区域.请记住,像这样的段错误可能不一定直接发生在你的bug所在的代码行上.
使用系统上提供的内存调试工具.
在Unix上,查看本指南在unix上调试内存,以及valgrind profiler(@Sol,thx,提醒我这个)
在Visual Studio/Windows上,您的好朋友CrtCheckMemory()非常方便.另外,请阅读CRT内存调试模式,因为它们是在VS中工作的更好的功能之一.通常情况下,在VS中打开一个内存选项卡就足以在记忆各种模式后诊断出这样的错误.
在Mac OSX中,您可以在malloc_error_break(来自gdb或Xcode)上设置断点,这会导致调试器在malloc检测到内存损坏时中断.我不确定这是否可用于其他unix版本,但快速谷歌搜索似乎表明它只是mac.此外,OSX似乎存在一个相当"实验性"的valgrind版本.
jalf.. 8
就像其他人所说的那样,你可能想在这里发布一些代码,即使这不是你问题的真正意义.让每个人在这里查看代码,看看他们是否能找到导致段错误的原因,这可能仍然是一个很好的学习经历.
但是,问题是,有很多平台依赖,以及基本随机的因素影响C程序.虚拟内存有时意味着访问未分配的内存似乎可以正常工作,因为您点击了某个页面的未使用部分,该部分已在某个早期点分配.其他时候,它会发生段错误,因为您点击了一个尚未分配给您的进程的页面.这真的无法预测.这取决于你的内存分配位置,它是在页面的边缘,还是在中间?这取决于操作系统和内存管理器,到目前为止已经分配了哪些页面,......你明白了.系统上安装了不同的编译器,相同编译器的不同版本,不同的操作系统,不同的软件,驱动程序或硬件,当您访问未分配的内存时,无论是否出现段错误,任何事情都可能发生变化.
至于TA的声称cygwin更"松懈",这是垃圾,原因很简单.两个编译器都没有发现错误!如果"本机"GCC编译器真的不那么松懈,它会在编译时给你一个错误.Segfaults不是由编译器生成的.编译器无法确保您获得段错误而不是看似有效的程序.
我并不是说听起来很粗鲁,但可能你的代码很糟糕,而不是编译器.;)像这样的问题实际上比你想象的更常见,因为不同的操作系统和编译器将有不同的方式在堆栈和堆中组织应用程序的数据.前者可能特别成问题,特别是如果您最终覆盖堆栈上的内存,或引用系统已决定用于其他内容的释放内存.所以基本上,你有时会侥幸逃脱,但有时你的应用会窒息而死.无论哪种方式,如果它是段错误的,那是因为你试图引用你不被允许的内存,所以它更像是一个"快乐的巧合",它没有在另一个系统/编译器下崩溃.
但实际上,段错误是一个段错误,所以你应该调试你的代码来寻找内存损坏而不是调整编译器的配置来找出出错的地方.
编辑:好的,我明白你现在的意思了......我以为你是在解决这个问题,"X很糟糕,但是Y工作得很好!" 态度,但似乎是你的TA有这个.;)
无论如何,这里有一些调试问题的更多提示:
寻找指针算术,引用/解除引用可能的"doh!" 错误.你添加/减去一个(也就是fencepost错误)的任何地方都特别可疑.
注释掉问题区域周围的malloc/free调用,以及使用这些指针的任何相关区域.如果代码停止崩溃,那么你就是朝着正确的方向前进.
假设您至少已经确定了代码崩溃的一般区域,请在其中插入早期的return语句,并找到代码不会崩溃的点.这有助于找到该点与代码实际崩溃之间某处的区域.请记住,像这样的段错误可能不一定直接发生在你的bug所在的代码行上.
使用系统上提供的内存调试工具.
在Unix上,查看本指南在unix上调试内存,以及valgrind profiler(@Sol,thx,提醒我这个)
在Visual Studio/Windows上,您的好朋友CrtCheckMemory()非常方便.另外,请阅读CRT内存调试模式,因为它们是在VS中工作的更好的功能之一.通常情况下,在VS中打开一个内存选项卡就足以在记忆各种模式后诊断出这样的错误.
在Mac OSX中,您可以在malloc_error_break(来自gdb或Xcode)上设置断点,这会导致调试器在malloc检测到内存损坏时中断.我不确定这是否可用于其他unix版本,但快速谷歌搜索似乎表明它只是mac.此外,OSX似乎存在一个相当"实验性"的valgrind版本.
就像其他人所说的那样,你可能想在这里发布一些代码,即使这不是你问题的真正意义.让每个人在这里查看代码,看看他们是否能找到导致段错误的原因,这可能仍然是一个很好的学习经历.
但是,问题是,有很多平台依赖,以及基本随机的因素影响C程序.虚拟内存有时意味着访问未分配的内存似乎可以正常工作,因为您点击了某个页面的未使用部分,该部分已在某个早期点分配.其他时候,它会发生段错误,因为您点击了一个尚未分配给您的进程的页面.这真的无法预测.这取决于你的内存分配位置,它是在页面的边缘,还是在中间?这取决于操作系统和内存管理器,到目前为止已经分配了哪些页面,......你明白了.系统上安装了不同的编译器,相同编译器的不同版本,不同的操作系统,不同的软件,驱动程序或硬件,当您访问未分配的内存时,无论是否出现段错误,任何事情都可能发生变化.
至于TA的声称cygwin更"松懈",这是垃圾,原因很简单.两个编译器都没有发现错误!如果"本机"GCC编译器真的不那么松懈,它会在编译时给你一个错误.Segfaults不是由编译器生成的.编译器无法确保您获得段错误而不是看似有效的程序.