Joonas' Note

Joonas' Note

Visual Studio에서는 되는데, 채점하면 컴파일에러인가요? 본문

알고리즘/문제 풀이

Visual Studio에서는 되는데, 채점하면 컴파일에러인가요?

2017. 10. 29. 04:43 joonas

    [이전 블로그로부터 글 옮김]


    이 내용은 Beakjoon Online JudgeAlgospot 등의 온라인 저지를 사용하시는 분들에게 도움이 될 내용입니다.


    최근들어 컴파일에러로 오답을 받고 질문을 올리시는 분이 자주 보이는 것 같습니다.

    주로 그 내용은 "비주얼 스튜디오에서는 되는 데 백준에서는 오류가 나네요" 등이더군요.

    컴파일 에러를 해결하시는 데에 도움이 될 만한 글을 남기고자 합니다.

    비쥬얼 스튜디오에서는 되는 데..

    비쥬얼 스튜디오는 생각 이상으로 강력한 통합 개발 환경(IDE)을 제공합니다. 아래 코드는 컴파일 에러일까요?

    #include <iostream>
    int main() {
        printf("%d", strlen("123"));
        return 0;
    }

    위 코드는 컴파일 에러가 맞습니다. strlen 함수는 cstring 또는 string.h 헤더에 포함되어있기 때문이죠.

    하지만 놀랍게도 비쥬얼 스튜디오 2017에서는 정상적으로 실행됩니다. (다른 버전은 테스트 후 추가하겠습니다)

    자세한 이유는 여기를 참고하시면 좋을 것 같습니다.

    요약하자면 "헤더의 의존성 때문"이지만, 이는 약속된것도 표준으로 정의된것도 아니기때문에 컴파일러마다 다르게 동작합니다. 비쥬얼 스튜디오는 VS C++ compiler를, 채점은 GCC compiler를 사용합니다.

    (정확히는 컴파일러때문이 아니라, 비쥬얼 스튜디오에서는 의존성을 보고 헤더를 자동으로 추가해주기 때문이라고 생각하고 있습니다.)

    그럼 제출해야만 에러를 알 수 있나요?

    아닙니다. 채점 도움말을 보시면 채점 환경과 컴파일 옵션을 확인할 수 있습니다. 이와 비슷한 환경에서 실행하시면 채점할 때 어떤 오류가 나올 지 미리 확인할 수 있습니다.

    C++의 경우 Ubuntu 16.04.1 LTS 64-bit에서 g++ Main.cc -o Main -O2 -Wall -lm로 컴파일 하는군요. O2 최적화, 경고 무시, 수학라이브러리를 포함하고 있음을 알 수 있습니다.

    에러를 해결하는 방법

    0. GCC Compiler를 사용한다.

    여기에는 여러 방법이 있습니다만, 크게 2가지가 있습니다. 직접 설치하거나, 설치된 환경에서 실행하는 것입니다.

    Windows 환경에서 완전히 동일하진 못해도 컴파일 채점환경과 비슷하게 실행하는 방법이 있습니다.

    1. MinGW(http://www.mingw.org/) 프로그램 설치하기

    Windows에서 사용하기 적당한 IDE로 Dev-C++가 있습니다. 이 IDE의 경우에는 MinGW에서 제공하는 GCC compiler를 통해 컴파일하기 때문에, 채점 환경과 어느정도 비슷한 결과를 보실 수 있을겁니다. Dev-C++을 설치하시면 컴파일러 설치부터 환경변수 설정까지 자동으로 됩니다.

    1. Online IDE에서 실행하기

    저는 이 방법을 추천합니다. 백준 온라인 저지와 비슷하게 Ubuntu + GCC 로 실행할 수 있기 때문에 가장 비슷한 환경에서 확인할 수 있기 때문입니다.

    엄청나게 많은 온라인 IDE가 있지만, 개인적으로 쓸만한 사이트를 추천하자면 아래 3개정도입니다.

    저는 이 중에서도 ideone.com이 가장 편하고 좋다고 생각합니다.

    각자 장단점이 있지만 글의 주제에 벗어나므로 간단하게만 적자면, IDEone.com은 간단하게 코딩하기에 정말 좋지만, 가끔 사이트가 엄청 느리고 컴파일 옵션(O2 등)을 지정하지 못합니다.

    c9.io는 Bash 환경을 제공해서 진짜 좋습니다. 하지만 가입도 해야하고 가상환경도 생성하는 등 문제풀이를 하기엔 첫 세팅이 오래걸립니다.

    1. ANSI 표준 확인하기

    "표준"이라는 것은 중요합니다. 서로 다른 환경과 플랫폼에서 동일한 실행 결과를 보장해줄 수 있기 때문입니다. 이는 모든 컴파일러에도 해당됩니다.

    대표적으로 itoa 함수는 표준이 아니기 때문에, 사용을 권장하지 않습니다. 표준이 아니라는 의미는 컴파일러에 따라 존재하지 않는 함수로 취급될 수 있다는 의미입니다. (즉, 컴파일 에러가 나타날 수 있습니다.)

    그럼 지금 사용하려는 함수가 표준인지 아닌 지는 어떻게 알 수 있을까요? 바로 다음에서 설명하겠습니다.

    *2. 헤더 확인하기

    사실 다 필요없고 이 부분만 잘 지켜주어도 컴파일 오류의 90%를 예방할 수 있다고 생각합니다.

    C++의 표준 함수들은 각각 그것이 정의되어 있는 헤더파일이 C++ 표준 스펙에 정의되어 있습니다.

    예를 들어 memset 의 경우는 또는 에 정의되어 있고,

    어떤 컴파일러를 써도 이 헤더를 include 하면 memset이 잘 정의됨을 보장받을 수 있습니다. (이 글 by @wookayin)

    C++의 표준 함수들은 레퍼런스를 확인하는 것이 가장 좋습니다.

    사용하려는 함수를 아래 레퍼런스들 중 아무 곳에서나 검색하셔서 헤더를 확인하시면 됩니다.

    이 외에도 많지만, 개인적으로 Cplusplus.com과 cppreference를 가장 많이 사용합니다. (역시 닉값...)

    3. 에러 메시지 천천히 읽어보기

    컴파일러가 출력하는 에러 메시지에는 생각보다 많은 정보가 담겨있습니다. 당연한 말이지만, 이 메시지에서 출력된 오류들을 하나씩 수정하면 컴파일 오류가 해결됩니다.

    백준 온라인 저지에서는 채점 결과에서 보이는 컴파일 에러를 클릭하시면 메시지를 확인하실 수 있습니다!

    소스코드와 함께 경고(warning)와 오류(error)가 표시됩니다. 경고는 말그대로 경고일뿐이니, 오류(error)만 확인하시면 됩니다.

    // 컴파일 에러 코드
    // 에러가 어디서 나는 지 모르겠어요
    #include <stdio.h>
    int main(){
        char s[1001];
        scanf("%s", s);
        printf("%d", strlen(s));
        return 0;
    }

    위 소스코드에 대한 오류 메시지는 아래와 같습니다.

    Main.cc: In function ‘int main()’:
    Main.cc:3:31: error: ‘strlen’ was not declared in this scope
         int a, b, c = strlen("123");
                                   ^
    Main.cc:3:15: warning: unused variable ‘c’ [-Wunused-variable]
         int a, b, c = strlen("123");
                   ^
    Main.cc:4:27: warning: ignoring return value of ‘int scanf(const char*, ...)’, declared with attribute warn_unused_result [-Wunused-result]
         scanf("%d %d", &a, &b);
                               ^

    여기에는 에러가 하나밖에 없네요. 하지만 가장 많이 보실 메시지라고 생각합니다.

    error: ‘strlen’ was not declared in this scope 과 같이 ~ not declared in this scope 는 선언되지 않은 변수나 함수에 나타나는 메시지입니다.

    표준이 아닌 함수를 사용하는 경우에도 함수가 없는 것으로 간주하기 때문에 이 경우에 해당됩니다.

    이 오류는 해당 함수(또는 변수)를 정의하거나 올바른 헤더를 추가하시면 해결됩니다.

    이 외에도 여러 메시지가 있지만, 구글에 error: ‘strlen’ was not declared in this scope 처럼 문장을 통채로 검색하셔도 해결되는 경우가 있으니, 갓구글에 의지하셔도 좋습니다.

    4. VS에서 제공하는 함수들

    비쥬얼 스튜디오는 어떤 함수가 deprecated 되었다던가 등의 이유로 사용을 못하게 하기도 하더군요. 대표적으로 gets인데, 이 부분이 가끔 화날때가 있습니다.

    scanf_s 나 gets_s 등 보안을 이유로 제공되는 함수들이 있습니다. 하지만 채점되는 컴파일러 GCC에 이러한 함수는 없습니다.

    비쥬얼 스튜디오 프로젝트를 만들 때(혹은 속성에서) SDL를 체크하지 않으시면 됩니다. 자세한 내용은 여기에서 스크린샷과 함께 잘 설명해주셨습니다.

    위에서 잠깐 언급했는데, gets의 경우는 사용하기가 참 난감합니다. 비쥬얼 스튜디오에서는 컴파일이 안되고, gets_s를 gets로 제출할 때마다 수정하기에는 번거롭기 때문입니다. 그래서 저는 아래처럼 매크로를 사용합니다.

    #define gets(s) gets_s(s)

    그리고 제출할 때는 위 매크로를 제거하고 제출하시면 됩니다. fgets(~)도 써봤는데, 이건 Linux와 Windows의 개행방식(\n, \r\n) 때문인지 조금씩 다르게 동작하더군요.

    이렇듯 컴파일러가 다르기 때문에 발생하는 컴파일 에러가 은근히 많습니다.

    5. 그 외

    • g++에서 main함수는 int형이어야 합니다. void main을 사용하면 컴파일 에러를 받게 됩니다.
    • for (int i=0; i&lt;n; i++) 와 같이 for문 안에 변수를 선언 했을 때, 변수 i는 해당 for문 블럭을 벗어나면 사라지게 됩니다.
    • itoa 함수는 ANSI에서 규정한 표준 함수가 아닙니다.
    • __int64는 ANSI 표준이 아닙니다. 하지만, 64비트 정수를 사용하고 싶은 경우에는 long long을 사용하면 됩니다.

    부족한 지식과 글솜씨지만, 앞으로 답변하실 분들과 질문하실 분들의 수고로움을 대신할 수 있다면 좋겠습니다.

    중간에 잘못된 내용이 있다면 지적해주세요!

    글을 어떻게 마쳐야할 지 모르겠네요.

    그럼 20000


    https://www.acmicpc.net/blog/view/52 에서 글 옮김


    Comments