Не могу скомпилировать с GCC на Ubuntu 12.04

Я пытаюсь скомпилировать и запустить приведенную ниже программу на C на компьютерах с Ubuntu и Windows, используя как GCC, так и VC9. Однако я сталкиваюсь со следующими проблемами:

На машине с 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;
}
9
задан 13 April 2017 в 15:24

2 ответа

Отказ сегментации происходит, когда программа пытается получить доступ к памяти за пределами области, которая была выделена для него.

В этом случае опытный программист 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, который является (в этой системе) реализован с вызовами к нескольким другим связанным библиотечным функциям.

То, чем Вы обычно интересуетесь, является вызовом функции в Вашей программе, которая вызывает функцию за пределами Вашей программы. Если нет ошибка в библиотеке/библиотеках самостоятельно, что Вы используете (в этом случае, стандартная библиотека для C libc если файлом библиотеки libc.so.6), ошибка, которая вызывает катастрофический отказ, находится в Вашей программе и часто будет в или около последней передачи Вашего со звонками в прямой эфир.

В этом случае это:

#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 для Windows)?

Люди часто говорят что, когда Вы выделяете память и затем пытаетесь получить доступ к памяти за пределами нее, которая производит ошибку. Но это не действительно верно. Согласно C и техническим стандартам C++, что это действительно продукты является неопределенным поведением.

Другими словами, что-либо может произойти!

Однако, некоторые вещи более вероятны, чем другие. Почему случается так, что небольшой массив на стеке, на некоторых реализациях, будет казаться, будет работать как больший массив над стеком?

Это сводится, как выделение стека реализовано, которому разрешают варьироваться от платформы до платформы. Ваш исполняемый файл может выделить больше памяти своему стеку, чем на самом деле предназначается, чтобы использоваться в любой момент. Иногда это может позволить Вам писать в ячейки памяти, что Вы явно не предъявили права на в Вашем коде. Вероятно, что это - то, что происходит когда Ваша сборка Ваша программа в VC9.

Однако Вы не должны полагаться на это поведение даже в VC9. Это могло потенциально зависеть от различных версий библиотек, которые могли существовать в различных системах Windows. Но еще более вероятно проблема, что дополнительное стековое пространство выделяется с намерением, что это будет на самом деле использоваться, и таким образом, это сможет на самом деле использоваться. Затем Вы испытываете полный кошмар "неопределенного поведения", где в этом случае больше чем одна переменная могла оказаться сохраненная в том же месте, где при записи в каждый перезаписывает другой..., но не всегда, потому что иногда пишет в переменные, кэшируются в регистрах и не на самом деле сразу выполняются (или читает в переменные, может кэшироваться, или переменная, как может предполагаться, совпадает с ним, был прежде, потому что память, выделенная ему, как известен компилятор, не была записана в через саму переменную).

И это приносит мне к другой вероятной возможности для того, почему программа работала при создании с VC9. Это возможно, и несколько вероятно, что некоторый массив или другая переменная были на самом деле выделены Вашей программой (который может включать быть выделенным библиотекой, которой Ваша программа пользуется) использовать пространство после однобайтового массива s. Таким образом, обработка s как массив дольше, чем один байт имел бы эффект доступа к содержанию этого переменные/массивы, которые могли также быть плохими.

В заключение когда у Вас есть ошибка как это, посчастливилось получить ошибку как "Отказ сегментации" или "Общее нарушение защиты". Когда у Вас нет этого, Вы не можете узнать, пока не слишком поздно, что Ваша программа имеет неопределенное поведение.

15
ответ дан 13 April 2017 в 15:24

Привет переполнение буфера!

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

Вы выделяете один байт для строки на стеке и затем продолжаете писать больше чем один байт в него. И завершать его, Вы читаете вне конца того массива. Прочитайте руководство C и особенно раздел по строкам и памяти выделения для них.

9
ответ дан 13 April 2017 в 15:24

Другие вопросы по тегам:

Похожие вопросы: