Я пытаюсь скомпилировать и запустить приведенную ниже программу на 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;
}
Ответы:
Ошибка сегментации возникает, когда программа пытается получить доступ к памяти за пределами области, выделенной для нее.
В этом случае опытный программист C может увидеть, что проблема происходит в строке, где
sprintf
вызывается. Но если вы не можете сказать, где происходит ошибка сегментации, или если вы не хотите читать код, чтобы попытаться выяснить это, тогда вы можете построить свою программу с помощью символов отладки (сgcc
помощью-g
флага это делает ), а затем запустите его через отладчик.Я скопировал ваш исходный код и вставил его в файл, который назвал
slope.c
. Тогда я построил это так:(Опция
-Wall
необязательна. Она просто заставляет выдавать предупреждения для большего числа ситуаций. Это также может помочь выяснить, что может быть не так.)Затем я запустил программу в отладчике
gdb
, сначала запустив ееgdb ./slope
для запускаgdb
, а затем, однажды в отладчике, далrun
команду отладчику:(Не беспокойтесь о моем
you have broken Linux kernel i386 NX
...support
сообщении; оно не мешаетgdb
эффективно использоваться для отладки этой программы.)Эта информация очень загадочная ... и если у вас не установлены отладочные символы для libc, вы получите еще более загадочное сообщение с шестнадцатеричным адресом вместо символьного имени функции
_IO_default_xsputn
. К счастью, это не имеет значения, потому что мы действительно хотим знать, где в вашей программе происходит проблема.Таким образом, решение состоит в том, чтобы оглянуться назад, чтобы увидеть, какие вызовы функций имели место, приводя к тому конкретному вызову функции в системной библиотеке, где
SIGSEGV
сигнал был, наконец, запущен.gdb
(и любой отладчик) имеет эту встроенную функцию: она называется трассировкой стека или backtrace . Я использую командуbt
отладчика для генерации обратной трассировки вgdb
:Вы можете видеть, что ваша
main
функция вызываетcalc_slope
функцию (которую вы намеревались), а затемcalc_slope
вызываетsprintf
, которая (в этой системе) реализована с помощью вызова нескольких других связанных библиотечных функций.Что вас обычно интересует, так это вызов функции в вашей программе, который вызывает функцию вне вашей программы . Если только в самих библиотеках / библиотеках, которые вы используете (есть стандартная библиотека C,
libc
предоставляемая файлом библиотекиlibc.so.6
), отсутствует ошибка, ошибка, вызывающая сбой, присутствует в вашей программе и часто будет на уровне или вблизи последний звонок в вашей программе.В этом случае это:
Вот где ваша программа вызывает
sprintf
. Мы знаем это, потому чтоsprintf
это следующий шаг. Но даже не говоря об этом, вы знаете это, потому что это то, что происходит в строке 26 , и это говорит:В вашей программе строка 26 содержит:
(Вы всегда должны использовать текстовый редактор, который автоматически показывает номера строк, по крайней мере, для той строки, в которой вы находитесь. Это очень полезно для интерпретации как ошибок во время компиляции, так и проблем во время выполнения, выявленных при использовании отладчика.)
Как обсуждалось в ответе Денниса Каарсемейкера ,
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
массива длиннее одного байта будет иметь эффект доступа к содержимому этих / этих переменных / массивов, что также может быть плохо.В заключение, если у вас есть такая ошибка, вам повезет получить ошибку типа «Ошибка сегментации» или «Ошибка общей защиты». Если у вас этого нет , вы можете не узнать, пока не станет слишком поздно, что ваша программа имеет неопределенное поведение.
источник
Привет переполнение буфера!
Вы выделяете один байт для строки в стеке, а затем приступаете к записи в нее более одного байта. И в довершение всего, вы читаете за пределами этого массива. Пожалуйста, прочтите руководство по C и особенно раздел о строках и распределении памяти для них.
источник