无法在Ubuntu 12.04上使用GCC进行编译


9

我正在尝试使用GCC和VC9在我的Ubuntu和Windows计算机上编译并运行以下C程序。但是,我面临以下问题:

在Ubuntu机器上:

GCC可以正常编译,但是运行时会显示以下提示:

Segmentation Fault (Core Dump).

在Windows计算机上:

VC9编译并运行良好。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;
}

更新:

幸得利雅不仅帮助我跟踪误差,而且还向我介绍了gdb其回追踪工具(bt),它是在调试GCC编译的程序,以便帮助。这是修改后的版本,经过反复试验,我努力了:

#include <string.h>
#include <stdio.h>
#include <stdlib.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)
        {
            int size=10;
            char *s=(char*)malloc((size+1) * sizeof(char));
            int left;
            int right;
            int cent;

            sprintf(s,"%d",curr);
            s++;
            do
            {
                left = *(s-1) - '0';
                cent = *s - '0';
                right = *(s+1) - '0';
                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;
}

3
我认为这不是编译问题,而是更多的运行时问题。您将从StackOverflow获得更多帮助。
oaskamay

您确定使用VC9编译后确实可以正常运行吗?
伊利亚·卡根

是的,100%。但不适用于gcc。
Prahlad Yeri 2013年

@PrahladYeri酷!我已经在回答中解释了这样做的原因。(这也意味着我们可能应该考虑这个问题,因为它与特定于Ubuntu *的*行为有关。Windows中的GCC表现出可比的行为,但是没有错误消息,而且很难确切知道那里发生了什么-此外,何时Ubuntu上的GCC和Microsoft Visual C ++的工作原理有所不同,我认为Ask Ubuntu是一个合理的地方,询问Ubuntu上的GCC为何能正常工作。话虽如此,有关如何正确使用GCC的其他问题也应
归因

在C中修改字符串文字是未定义的行为。请记住这一点。
jn1kk

Answers:


15

一个分段错误当程序试图已分配给它的区域的存取存储器以外发生。

在这种情况下,经验丰富的C程序员可以看到问题在sprintf调用的行中发生。但是,如果您无法分辨出分段错误发生在哪里,或者您不想打扰阅读代码来尝试找出问题所在,则可以使用调试符号来构建程序(使用gcc-g标志可以完成此操作),然后通过调试器运行它。

我复制了源代码并将其粘贴到名为的文件中slope.c。然后我像这样构建它:

gcc -Wall -g -o slope slope.c

(The -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

(不必担心我的you have broken Linux kernel i386 NX... support消息;它不会阻止gdb有效地调试该程序。)

该信息是高度隐秘的...如果您没有为libc安装调试符号,那么您将获得更加隐秘的消息,该消息具有十六进制地址而不是符号函数名_IO_default_xsputn。幸运的是,这无关紧要,因为我们真正想知道的是问题在程序中的什么地方发生。

因此,解决方案是向后看,看看发生了什么函数调用SIGSEGV,最终导致系统中信号最终触发的那个特定函数调用。

gdb(以及任何调试器)都内置了此功能:称为堆栈跟踪回溯跟踪。我使用btdebugger命令在中生成回溯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)

您可以看到您的main函数先调用了该calc_slope函数(该函数是您想要的),然后calc_slope调用sprintf,这是在此系统上通过对其他几个相关库函数的调用而实现的。

通常,您最感兴趣的是程序中的函数调用该函数调用程序外部的函数。除非您正在使用的库中存在错误(在这种情况下,libc是库文件提供的标准C库libc.so.6),否则导致崩溃的Bug会出现在您的程序中,并且通常会位于程序中的最后一次调用

在这种情况下,那就是:

#4  0x080484cc in calc_slope (input1=1, input2=150) at slope.c:26

那是您的程序调用的地方sprintf。我们知道这一点,因为这sprintf是下一步。但即使没有说明,您也知道这一点,因为这就是第26行发生的事情,它说:

... at slope.c:26

在您的程序中,第26行包含:

            sprintf(s,"%d",curr);

(您应该始终使用至少自动显示行号的文本编辑器,至少对于您当前所在的行。这对于解释编译时错误和使用调试器时发现的运行时问题很有帮助。)

正如Dennis Kaarsemaker的答案所述,它s是一个1字节的数组。(不为零,因为该值您分配它,""是一个字节长,也就是说,它是等于{ '\0' },在以同样的方式"Hello, world!\n"等于{ 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n', '\0' })。

那么,为什么在一些平台上,这仍然工作(显然在与VC9编译并适用于Windows)?

人们经常说,当您分配内存,然后尝试访问其外部的内存时,会产生错误。但这不是真的。根据C和C ++技术标准,这真正产生的是未定义的行为。

换句话说,任何事情都可能发生!

不过,有些事情比其他事情更有可能。为什么在某些实现中,堆栈上的小数组看起来像堆栈上的大数组一样起作用?

这取决于堆栈分配的实现方式,允许堆栈分配因平台而异。您的可执行文件可能分配给其堆栈的内存多于任何一次实际打算使用的内存。有时,这可能使您可以写入未在代码中明确声明的内存位置。在VC9中构建程序时,很可能就是这种情况。

但是,即使在VC9中也不应依赖此行为。它可能依赖于不同Windows系统上可能存在的不同版本的库。但是,更可能的问题是分配了额外的堆栈空间,目的是要实际使用,因此实际上可以使用它。然后,您将经历“未定义行为”的整个噩梦,在这种情况下,多个变量最终可能存储在同一个地方,写一个变量会覆盖另一个变量……但并非总是如此,因为有时写变量它们被缓存在寄存器中而不是立即执行(或者对变量的读取可能被缓存,或者可以假定变量与以前相同,因为编译器知道分配给它的内存没有通过变量本身)。

这使我想到了使用VC9构建该程序时为何起作用的另一种可能性。有可能,而且在某种程度上,您的程序实际上已经分配了一些数组或其他变量(其中可能包括您的程序正在使用的库所分配的)来使用一字节数组后的空间s。因此,将s数组视为长于一个字节的数组将具有访问该变量/数组/变量的内容的效果,这也可能是不好的。

总之,当您遇到这样的错误时,很幸运收到诸如“分段错误”或“常规保护错误”之类的错误。如果您没有该程序,则可能要等到程序具有未定义的行为为时已晚,才能找出答案。


1
感谢您这样清晰的解释。这正是我所需要的.. !!
Prahlad Yeri 2013年

9

你好缓冲区溢出!

char *s="";
sprintf(s,"%d",curr);
length=strlen(s);

您为堆栈上的字符串分配一个字节,然后继续向其写入一个以上的字节。最重要的是,您阅读的内容超出了该数组的末尾。请阅读C手册,尤其是有关字符串和为其分配内存的部分。


是的,我后来才知道。但是当我写这篇文章时,VC9编译器不仅允许,而且还向我正确显示了结果。我打印了strlen(s),结果显示为4,而不是1!
Prahlad Yeri

您还可以建议我如何解决这个问题吗?由于您一定从代码中推测得出来,因此我无法提前为* s分配固定大小。它的长度是curr变量中的位数,直到将其转换为字符串后才能知道!?
Prahlad Yeri

我可以,但是您真的应该去Stack Overflow寻求编程方面的建议,因为在这里这很不合理。
丹尼斯·考斯玛克

1
@DennisKaarsemaker此处的原始问题可能并没有引起关注,因为它显然涉及到行为在Ubuntu和另一个平台之间是不同的(并且我已经在回答中解释了这样做的最可能原因)。我确实同意有关如何在C中正确分配字符串的问题属于堆栈溢出问题,而不是此处的问题。
伊利亚·卡根
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.