我正在尝试使用 GCC 和 VC9 在我的 Ubuntu 和 Windows 机器上编译和运行以下 C 程序。但是,我面临以下问题:
GCC 编译得很好,但是在运行时,我会看到这个提示:
Segmentation Fault (Core Dump).
Run Code Online (Sandbox Code Playgroud)
VC9 编译运行良好。GCC 编译正常,但程序运行时进程终止。
在这里需要您的专家帮助。这是我的代码:
Segmentation Fault (Core Dump).
Run Code Online (Sandbox Code Playgroud)
幸得利雅不仅帮助我跟踪误差,而且还向我介绍了gdb
其回追踪工具(bt
),它是在调试GCC编译的程序,以便帮助。这是修改后的版本,经过反复试验,我完成了:
#include <string.h>
#include <stdio.h>
int calc_slope(int input1,int input2)
{
int sum=0;
int start=input1;
int end=input2;
int curr=start;
//some validation:
if (input1>input2)
return -1;
while(curr<=end)
{
if (curr>100)
{
char *s="";
int length;
int left;
int right;
int cent;
sprintf(s,"%d",curr);
length=strlen(s);
s++;
do
{
//printf("curr=%d char=%c pointer=%d length=%d \n",curr,*s,s,length);
left = *(s-1) - '0';
cent = *s - '0';
right = *(s+1) - '0';
//printf("curr=%d l=%d c=%d r=%d\n",curr,left,cent,right);
if ( (cent>left && cent>right) || (cent<left && cent<right) )
{
sum+=1; //we have either a maxima or a minima.
}
s++;
} while (*(s+1)!='\0');
}
curr++;
}
return sum;
}
int main()
{
printf("%d",calc_slope(1,150));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
Eli*_*gan 15
一个分段错误当程序试图已分配给它的区域的存取存储器以外发生。
在这种情况下,有经验的 C 程序员可以看到问题发生在sprintf
被调用的行中。但是,如果您无法判断分段错误发生在哪里,或者您不想费心通读代码以试图弄清楚它,那么您可以使用调试符号构建您的程序(使用gcc
,-g
标志执行此操作) 然后通过调试器运行它。
我复制了您的源代码并将其粘贴到我命名为slope.c
. 然后我像这样构建它:
gcc -Wall -g -o slope slope.c
Run Code Online (Sandbox Code Playgroud)
(这-Wall
是可选的。它只是为了让它在更多情况下产生警告。这也有助于找出可能出错的地方。)
然后我在调试器中gdb
运行程序,首先运行gdb ./slope
以启动gdb
程序,然后在调试器中向调试器发出run
命令:
ek@Kip:~/source$ gdb ./slope
GNU gdb (GDB) 7.5-ubuntu
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/ek/source/slope...done.
(gdb) run
Starting program: /home/ek/source/slope
warning: Cannot call inferior functions, you have broken Linux kernel i386 NX (non-executable pages) support!
Program received signal SIGSEGV, Segmentation fault.
0x001a64cc in _IO_default_xsputn () from /lib/i386-linux-gnu/libc.so.6
Run Code Online (Sandbox Code Playgroud)
(不要担心我的you have broken Linux kernel i386 NX
...support
消息;它不会阻止gdb
有效地用于调试此程序。)
该信息非常神秘……如果您没有为 libc 安装调试符号,那么您将收到一条更神秘的消息,该消息具有十六进制地址而不是符号函数名称_IO_default_xsputn
。幸运的是,这并不重要,因为我们真正想知道的是问题发生在程序中的哪个位置。
因此,解决方案是向后看,SIGSEGV
在最终触发信号的系统库中查看导致该特定函数调用发生的函数调用。
gdb
(和任何调试器)都内置了此功能:它称为堆栈跟踪或回溯。我使用bt
调试器命令在以下位置生成回溯gdb
:
(gdb) bt
#0 0x001a64cc in _IO_default_xsputn () from /lib/i386-linux-gnu/libc.so.6
#1 0x00178e04 in vfprintf () from /lib/i386-linux-gnu/libc.so.6
#2 0x0019b234 in vsprintf () from /lib/i386-linux-gnu/libc.so.6
#3 0x0017ff7b in sprintf () from /lib/i386-linux-gnu/libc.so.6
#4 0x080484cc in calc_slope (input1=1, input2=150) at slope.c:26
#5 0x08048578 in main () at slope.c:52
(gdb)
Run Code Online (Sandbox Code Playgroud)
你可以看到你的main
函数调用了calc_slope
函数(你想要的),然后calc_slope
调用sprintf
,它是(在这个系统上)通过调用其他几个相关的库函数来实现的。
您通常感兴趣的是程序中调用程序之外的函数的函数调用。除非您使用的库/库本身存在错误(在这种情况下,libc
是库文件提供的标准 C 库libc.so.6
),否则导致崩溃的错误在您的程序中,并且通常位于或接近程序中的最后一次调用。
在这种情况下,那就是:
#4 0x080484cc in calc_slope (input1=1, input2=150) at slope.c:26
Run Code Online (Sandbox Code Playgroud)
这就是您的程序调用的地方sprintf
。我们知道这一点,因为这sprintf
是下一步。但即使没有说明,你也知道这一点,因为这就是第 26 行发生的事情,它说:
... at slope.c:26
Run Code Online (Sandbox Code Playgroud)
在您的程序中,第 26 行包含:
sprintf(s,"%d",curr);
Run Code Online (Sandbox Code Playgroud)
(您应该始终使用自动显示行号的文本编辑器,至少对于您当前所在的行。这对于解释使用调试器时发现的编译时错误和运行时问题非常有帮助。)
正如Dennis Kaarsemaker 的回答中所讨论的,s
是一个一字节数组。(不是零,因为您分配给它的值""
, 是一个字节长,也就是说,它等于{ '\0' }
,与"Hello, world!\n"
等于 的方式相同{ 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n', '\0' }
。)
那么,为什么会在一些平台上,这仍然工作(显然在与VC9编译并适用于Windows)?
人们常说,当您分配内存然后尝试访问它之外的内存时,会产生错误。但事实并非如此。根据 C 和 C++ 技术标准,这真正产生的是未定义的行为。
换句话说,任何事情都可能发生!
尽管如此,有些事情比其他事情更有可能。为什么堆栈上的小数组在某些实现中看起来像堆栈上的大数组?
这归结为堆栈分配的实现方式,这允许因平台而异。您的可执行文件为其堆栈分配的内存可能比任何时候实际打算使用的内存多。有时,这可能允许您写入未在代码中明确声明的内存位置。这很可能就是您在 VC9 中构建程序时发生的情况。
但是,即使在 VC9 中,您也不应该依赖此行为。它可能依赖于可能存在于不同 Windows 系统上的不同版本的库。但更可能的问题是分配额外的堆栈空间是为了实际使用它,因此它可能会被实际使用。然后你会经历“未定义行为”的完整噩梦,在这种情况下,多个变量可能最终存储在同一个地方,写入一个会覆盖另一个......但并非总是如此,因为有时会写入变量缓存在寄存器中并且实际上并没有立即执行(或者对变量的读取可能会被缓存,或者一个变量可能被假定为与之前相同,因为编译器知道分配给它的内存没有被写入通过变量本身)。
这让我想到了另一种可能的可能性,即为什么该程序在使用 VC9 构建时可以工作。有可能,并且有些可能,某些数组或其他变量实际上是由您的程序分配的(可能包括由您的程序正在使用的库分配)以使用一字节 array 之后的空间s
。因此,将s
数组视为超过一个字节的数组会影响访问该/那些变量/数组的内容,这也可能很糟糕。
总而言之,当您遇到这样的错误时,很幸运会出现“Segmentation fault”或“General protection fault”之类的错误。如果您没有它,您可能不会发现您的程序有未定义的行为,直到为时已晚。
你好缓冲区溢出!
char *s="";
sprintf(s,"%d",curr);
length=strlen(s);
Run Code Online (Sandbox Code Playgroud)
您为堆栈上的字符串分配一个字节,然后继续向其中写入一个以上的字节。最重要的是,您阅读了该数组的末尾。请阅读 C 手册,尤其是有关字符串和为它们分配内存的部分。
归档时间: |
|
查看次数: |
2006 次 |
最近记录: |