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

9

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

На машине Ubuntu:

GCC компилируется отлично, но при запуске мне отображается следующее приглашение:

Segmentation Fault (Core Dump).

На компьютере с Windows:

VC9 Compiles & amp; работает нормально. 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)!='
#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)!='%pre%');
        }
        curr++;
    }

    return sum;
}

int main()
{
    printf("%d",calc_slope(1,150));
    return 0;
}
'); } curr++; } return sum; } int main() { printf("%d",calc_slope(1,150)); return 0; }

Обновление:

Кредит отправляется Eliah за то, что он не только помогает мне отслеживать эту ошибку, но и представляет мне gdb и инструмент обратного трассировки ( bt ), которые так полезны при отладке gcc-скомпилированной программы. Вот модифицированная версия, после обработки и ошибки я работал:

%pre%     
задан Prahlad Yeri 28.03.2013 в 18:01
источник

2 ответа

15

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

В этом случае опытный программист на C может видеть, что проблема происходит в строке, где вызывается sprintf . Но если вы не можете определить, где произошла ошибка сегментации, или если вы не хотите читать код через try , чтобы понять это, вы можете создать свою программу с помощью отладочных символов (с gcc , флаг -g делает это), а затем запустите его через отладчик.

Я скопировал исходный код и вложил его в файл, который я назвал slope.c . Затем я построил его следующим образом:

gcc -Wall -g -o slope slope.c

( -Wall является необязательным. Это просто для того, чтобы он выдавал предупреждения для большего количества ситуаций. Это может помочь в определении того, что может быть и неправильным).

Затем я запустил программу в отладчике gdb , сначала запустив gdb ./slope , чтобы запустить gdb с программой, а затем один раз в отладчике передал команду run отладчику:

[email protected]:~/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 (и любой отладчик) имеет встроенную функцию: она называется трассировкой трассировки или backtrace . Я использую команду bt debugger для генерации обратной линии в 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 ), ошибка, которая вызывает сбой, находится в вашей программе и < em> часто будет находиться рядом с последним вызовом в вашей программе.

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

#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);

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

Как обсуждалось в ответе Денниса Каарсемкера , s - однобайтовый массив. (Не равен нулю, так как значение, присвоенное вами, "" , равно одному байту, то есть оно равно { '"Hello, world!\n"' } , таким же образом, что { 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n', 's' } равно s .)

Итак, почему может это все еще работать на какой-то платформе (и, видимо, при компиляции с VC9 для Windows)?

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

Другими словами, все может случиться!

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

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

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

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

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

    
ответ дан Eliah Kagan 28.03.2013 в 18:58
источник
9

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

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

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

    
ответ дан Dennis Kaarsemaker 28.03.2013 в 18:05