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

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

我正在尝试使用 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;
}

答案1

A分段故障当程序尝试访问为其分配的区域之外的内存时,就会发生这种情况。

在这种情况下,经验丰富的 C 程序员可以看到问题发生在sprintf调用的那一行。但是如果你不知道分段错误发生在哪里,或者你不想费心阅读代码来尝试要弄清楚,那么您可以使用调试符号(使用gcc-g标志可以执行此操作)构建您的程序,然后通过调试器运行它。

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

gcc -Wall -g -o slope slope.c

-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(以及任何调试器)都内置有此功能:它被称为堆栈跟踪或者回溯。我使用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)

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

你通常感兴趣的是函数调用在你的程序中调用一个函数在你的程序之外除非你使用的库本身有 bug(在本例中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一个单字节数组。(不为零,因为您为其分配的值""是一个字节长,也就是说,它等于{ '\0' },就像"Hello, world!\n"等于 一样{ 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n', '\0' }。)

所以为什么可以这在某些平台上仍然可以工作(显然在使用 VC9 for Windows 编译时可以工作)?

人们经常说,当你分配内存然后尝试访问内存之外的内存时,会产生错误。但事实并非如此。根据 C 和 C++ 技术标准,这实际上会产生未定义的行为。

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

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

这归结于堆栈分配的实现方式,这在不同平台之间是允许的。可执行文件可能为其堆栈分配比实际预期使用的内存更多的内存。有时,这可能允许您写入您尚未写入的内存位置明确地在您的代码中声明。当您在 VC9 中构建程序时,很可能会发生这种情况。

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

这让我想到了为什么这个程序在使用 VC9 构建时能够正常工作的另一种可能。有可能,而且很有可能,一些数组或其他变量实际上是由您的程序分配的(可能包括由您的程序正在使用的库分配的)以使用单字节数组后的空间s。 因此,将其视为s长度超过一个字节的数组会产生访问该/那些变量/数组的内容的效果,这也可能是坏事。

总之,当你犯这样的错误时,幸运的收到“分段错误”或“常规保护错误”等错误。当您有这个,你可能直到太晚才发现你的程序有未定义的行为。

答案2

你好!缓冲区溢出!

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

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

相关内容