우분투 12.04에서 GCC로 컴파일 할 수 없습니다


9

GCC와 VC9가 모두있는 Ubuntu 및 Windows 컴퓨터에서 아래 C 프로그램을 컴파일하고 실행하려고합니다. 그러나 아래 문제에 직면하고 있습니다.

우분투 머신에서 :

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;
}

최신 정보:

크레딧은 오류를 추적하는 데 도움이 될뿐만 아니라 gcc 컴파일 프로그램을 디버깅하는 데 도움이되는 백 트레이싱 도구 ( )를 소개 하여 Eliah 에게 갑니다 . 다음은 수정 된 버전입니다. 시험과 오류가 발생했습니다.gdbbt

#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로 컴파일 한 후에 이것이 실제로 잘 실행 됩니까?
Eliah Kagan

예, 100 %입니다. 그러나 gcc로는 그렇지 않습니다.
Prahlad Yeri

@PrahladYeri 쿨! 나는 이것 대한 이유를 내 대답에 설명했다 . (이것은 우분투 * 특정 * 동작에 관한 것이므로이 주제에 대해이 주제를 고려해야한다는 것을 의미합니다. Windows의 GCC는 비슷한 동작을 나타내지 만 오류 메시지는 없으며 어떤 일이 일어나고 있는지 정확히 알기가 어렵습니다. 우분투 및 Microsoft Visual C ++ 작업에 GCC는 다르게, 우분투는 우분투에 GCC가이처럼 작동하는 이유에 대해 물어 적당한 장소 물어 생각을하는 방법에 대한 문의 사항, 말한다면. 바로 그것을 스택 오버플로에 속해가).
엘리야를 Kagan

C에서 문자열 리터럴을 수정하는 것은 정의되지 않은 동작입니다. 기억하십시오.
jn1kk

답변:


15

세그멘테이션 결함은 프로그램이이를 위해 할당 된 영역의 액세스 메모리 외부하려고 할 때 발생한다.

이 경우 숙련 된 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에 디버그 기호가 설치되어 있지 않으면 기호 함수 이름 대신 16 진수 주소를 가진 훨씬 더 암호 메시지가 나타납니다 _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);

(최소한 현재 줄에 대해서는 줄 번호를 자동으로 표시하는 텍스트 편집기를 사용해야합니다. 이는 디버거를 사용하는 동안 나타나는 컴파일 타임 오류와 런타임 문제를 해석하는 데 매우 유용합니다.)

논의 된 바와 같이 데니스 Kaarsemaker의 대답 , s1 바이트의 어레이이다. (0이 아닌 값은 당신이 그것을 할당했기 때문에, ""1 바이트는 그 긴, 그것은 동일에 { '\0' }, 같은 방법으로 "Hello, world!\n"동일합니다 { 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n', '\0' }.)

그렇다면 왜 여전히 일부 플랫폼에서 작동 할 수 있습니까?

사람들은 종종 메모리를 할당 한 다음 외부 메모리에 액세스하려고하면 오류가 발생한다고 말합니다. 그러나 그것은 사실이 아닙니다. C 및 C ++ 기술 표준에 따르면 이것이 실제로 생성하는 것은 정의되지 않은 동작입니다.

다시 말해, 어떤 일이든 일어날 수 있습니다!

그러나 어떤 것들은 다른 것보다 더 가능성이 높습니다. 스택의 작은 배열이 일부 구현에서 스택의 큰 배열처럼 작동하는 이유는 무엇입니까?

이는 스택 할당이 구현되는 방식에 따라 달라지며 플랫폼마다 다를 수 있습니다. 실행 파일은 실제로 한 번에 사용하려는 것보다 더 많은 메모리를 스택에 할당 할 수 있습니다. 때로는 코드에서 명시 적으로 주장 하지 않은 메모리 위치에 쓸 수 있습니다. VC9에서 프로그램을 빌드 할 때 이런 일이 일어나고있을 가능성이 큽니다.

그러나 VC9에서도이 동작에 의존해서는 안됩니다. 다른 Windows 시스템에 존재할 수있는 다른 버전의 라이브러리에 의존 할 수 있습니다. 그러나 여분의 스택 공간이 실제로 사용될 것이라는 의도로 여분의 스택 공간이 할당되어 실제로 사용될 수 있다는 문제가 더 가능성높습니다 .그런 다음 "정의되지 않은 동작"이라는 악몽을 경험할 수 있습니다.이 경우 하나 이상의 변수가 같은 곳에 저장 될 수 있습니다. 하나에 쓰면 다른 하나를 덮어 씁니다. 그러나 때로는 변수에 쓰기 때문에 항상 그런 것은 아닙니다 레지스터에 캐시되고 실제로 즉시 수행되지 않습니다 (또는 변수에 대한 읽기가 캐시되거나 변수가 할당 된 메모리가 쓰지 않은 것으로 알려지기 때문에 변수가 이전과 동일하다고 가정 될 수 있음) 변수 자체).

그리고 그것은 VC9로 빌드 할 때 프로그램이 왜 작동했는지에 대한 다른 가능성을 제공합니다. 그것은 것을 어느 정도 예상 가능하고, 어떤 배열 또는 다른 변수가 실제로 1 바이트 배열 한 후 공간을 사용하기 위해 (프로그램이 사용하는 라이브러리에 의해 할당되는 포함 할 수있다) 프로그램에 의해 할당되었다 s. 따라서 s1 바이트보다 긴 배열로 처리 하면 해당 / 해당 변수 / 배열의 내용에 액세스하는 효과가 있으며 이는 좋지 않을 수도 있습니다.

이 같은 실수가있을 때 결론적으로, 그건 운이 "분할 오류"또는 같은 오류 얻을 "일반 보호 오류가." 이 때 하지 않는 것을 가지고 너무 늦기 프로그램이 가지고 때까지, 당신은 찾을 수 없습니다 정의되지 않은 동작을.


1
자명 한 설명 감사합니다. 이것은 내가 필요한 것입니다 .. !!
Prahlad Yeri

9

안녕하세요 버퍼 오버플로!

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

스택에서 문자열에 대해 1 바이트를 할당 한 다음 2 바이트 이상을 작성합니다. 그리고 그것을 끝내기 위해, 당신은 그 배열의 끝을 넘어 읽습니다. C 매뉴얼, 특히 문자열 및 메모리 할당 섹션을 읽으십시오.


예, 나중에 알게되었습니다. 그러나이 글을 쓸 때 VC9 컴파일러는 허용 할뿐만 아니라 결과를 올바르게 보여주었습니다. 나는 strlen (s)를 printf-ed했고 그것은 1이 아니라 4를 보여 주었다!!
Prahlad Yeri

이 문제를 해결하는 방법에 대해 조언 해 주시겠습니까? 코드에서 추측 했으므로 미리 고정 크기를 * s에 할당 할 방법이 없습니다. 길이는 문자열로 변환 할 때까지 알 수없는 curr 변수의 자릿수입니다! ?
Prahlad Yeri

할 수는 있지만 프로그래밍 어드바이스를 위해 스택 오버플로로 향해야합니다.
Dennis Kaarsemaker

1
그것은 분명히 우분투와 다른 플랫폼 사이 다릅니다 (그리고 나는 이것에 대한 가장 가능성있는 이유를 설명했다고 동작을 포함하기 때문에 여기에 원래 질문은 주제 해제하지 않을 수도 있습니다 @DennisKaarsemaker 내 대답을 ). C에서 문자열을 올바르게 할당하는 방법에 대한 질문은 여기가 아니라 스택 오버플로에 속한다 는 데 동의합니다 .
Eliah Kagan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.