C / C ++ 줄 번호


110

디버깅 목적으로 C / C ++ 컴파일러 에서 줄 번호를 얻을 수 있습니까? (특정 컴파일러에 대한 표준 방식 또는 특정 방식)

예 :

if(!Logical)
    printf("Not logical value at line number %d \n",LineNumber);
    // How to get LineNumber without writing it by my hand?(dynamic compilation)

17
@Lucas : 우리 중 일부는 디버거를 엉망으로 만드는 것을 선호하지 않습니다. 이런 종류의 "가난한 사람의 주장 진술"은 코드의 영구적 인 부분이고 계산 상태에 대해 사실이어야하는 항목에 대한 지속적인 문서화이기 때문에 때때로 더 명확합니다.
S.Lott

13
@Lucas : 디버거는 또한 장기 실행 프로그램의 간헐적 인 문제 나 클라이언트 사이트에 배포 된 소프트웨어의 문제에 대한 정보를 수집하는 데 유용하지 않습니다. 이러한 경우 유일한 옵션은 프로그램이 나중에 분석하기 위해 프로그램 상태에 대한 정보를 가능한 한 많이 기록하는 것입니다.
KeithB

1
@Lucas 및 디버거는이 정보를 얻기 위해 일부 임베디드 시스템에서 잘 작동하지 않습니다.
George Stocker

답변:


180

전 처리기 매크로 __LINE____FILE__. 미리 정의 된 매크로이며 C / C ++ 표준의 일부입니다. 전처리 중에 현재 행 번호와 현재 파일 이름을 나타내는 정수가 들어있는 상수 문자열로 각각 대체됩니다.

기타 전 처리기 변수 :

  • __func__: 함수 이름 (이것은 C99의 일부이며 모든 C ++ 컴파일러가 지원하는 것은 아닙니다)
  • __DATE__ : "Mmm dd yyyy"형식의 문자열
  • __TIME__ : "hh : mm : ss"형식의 문자열

코드는 다음과 같습니다.

if(!Logical)
  printf("Not logical value at line number %d in file %s\n", __LINE__, __FILE__);

2
C99는 AFAIK가 부분적으로 사용되지 않는 __FUNCTION__ 대신 __func__를 사용합니다. __func__는 C의 상수 문자열 연결에 사용할 수 없기 때문에 코드가 깨질 수 있습니다.
Joseph Quinsey

1
GCC 매뉴얼 참조 : "__FUNCTION__ 및 __PRETTY_FUNCTION__은 문자열 리터럴로 취급되었습니다. 문자 배열을 초기화하는 데 사용할 수 있으며 다른 문자열 리터럴과 연결할 수 있습니다. GCC 3.4 이상에서는 __func__와 같은 변수로 처리합니다. C ++에서는 __FUNCTION__ 그리고 __PRETTY_FUNCTION__은 항상 변수였습니다. "
조셉 퀸지

줄 번호를 파일 이름과 같은 문자열로 얻는 방법이 있습니까? 전처리 기가 예를 들어 정수 22 대신 문자열 리터럴 "22"를 제공하고 싶습니다.
sep332

1
@ sep332 예,하지만 cpp는 이상한 짐승이므로 매크로 인수를 사용하여 두 단계로 수행해야합니다. #define S1(N) #N #define S2(N) S1(N) #define LINESTR S2(__LINE__). c-faq.com/ansi/stringize.html
Rasmus Kaj

1
엄밀히 말하면 __func__은 매크로가 아니라 암시 적으로 선언 된 변수입니다.
HolyBlackCat

64

C ++ 표준의 일부로 사용할 수있는 사전 정의 된 매크로가 있습니다. C ++ 표준의 섹션 16.8은 무엇보다도 __LINE__매크로를 정의합니다 .

__LINE__: 현재 소스 라인의 라인 번호 (10 진수 상수).
__FILE__: 소스 파일의 추정 이름 (문자열 리터럴).
__DATE__: 소스 파일 번역 일 (문자열 리터럴 ...)
__TIME__: 소스 파일 번역 시간 (문자열 리터럴 ...)
__STDC__:__STDC__ 사전 정의 여부
__cplusplus: 이름 __cplusplus은 199711L 값으로 정의됩니다. C ++ 번역 단위 컴파일

따라서 코드는 다음과 같습니다.

if(!Logical)
  printf("Not logical value at line number %d \n",__LINE__);

19

함수 이름, 클래스 및 줄 번호와 같은 디버그 정보도 포함한다는 점을 제외하면 printf () 와 동일한 동작으로 매크로를 사용할 수 있습니다 .

#include <cstdio>  //needed for printf
#define print(a, args...) printf("%s(%s:%d) " a,  __func__,__FILE__, __LINE__, ##args)
#define println(a, args...) print(a "\n", ##args)

이러한 매크로는 java stacktrace와 같은 정보를 포함하면서 printf () 와 동일하게 작동해야합니다 . 다음은 메인의 예입니다.

void exampleMethod() {
    println("printf() syntax: string = %s, int = %d", "foobar", 42);
}

int main(int argc, char** argv) {
    print("Before exampleMethod()...\n");
    exampleMethod();
    println("Success!");
}

결과는 다음과 같습니다.

main (main.cpp : 11) 이전 exampleMethod () ...
exampleMethod (main.cpp : 7) printf () 구문 : string = foobar, int = 42
main (main.cpp : 13) 성공했습니다!


C 개발을 위해, 당신은 바꿀 것 #include<stdio.h>
phyatt

11

사용 __LINE__(이중 밑줄 LINE 이중 밑줄의), 전처리는 발생되는 행 번호로 대체됩니다.



5

C ++ 20은 std :: source_location 을 사용하여이를 달성하는 새로운 방법을 제공합니다 . 이것은 현재 gcc 및 clang std::experimental::source_location에서 #include <experimental/source_location>.

같은 매크로의 문제 __LINE__는 메시지와 함께 현재 행 번호를 출력하는 로깅 함수를 생성하려는 경우 __LINE__호출 사이트에서 확장되기 때문에 항상 함수 인수 로 전달해야한다는 것입니다. 이 같은:

void log(const std::string msg) {
    std::cout << __LINE__ << " " << msg << std::endl;
}

log실제로 호출 된 행이 아니라 항상 함수 선언 행을 출력합니다 . 반면에 std::source_location다음과 같이 작성할 수 있습니다.

#include <experimental/source_location>
using std::experimental::source_location;

void log(const std::string msg, const source_location loc = source_location::current())
{
    std::cout << loc.line() << " " << msg << std::endl;
}

여기서는 호출 된 loc위치를 가리키는 줄 번호로 초기화됩니다 log. 여기에서 온라인으로 시도 할 수 있습니다.


4

시도 __FILE__하고 __LINE__.
또한 찾을 수 __DATE__있고 __TIME__유용 할 수 있습니다 .
클라이언트 측에서 프로그램을 디버깅해야하므로 이러한 정보를 기록해야하는 경우가 아니면 일반 디버깅을 사용해야합니다.


왜 내가 여기에 투표하지 않았고 mmyers가 내 게시물을 편집 한 이유는 무엇입니까?
Sanctus2099

@ Sanctus2099 : Markdown이 FILE과 LINE을 굵은 글꼴로 표시하도록 이중 밑줄을 변환했기 때문에 편집되었습니다 (답변이 어떻게 보이는지 확인하지 않습니까?). 또 다른 요점은 (적어도 지금은 이런 식으로 보입니다) 이미 정답을받은 후 1 시간 후에 답변을했기 때문에 가치를 추가하지 않았다는 것입니다.
Felix Kling

이중 밑줄은 bold에 대한 마크 업 구문입니다 . 이중 밑줄을 제대로 표시하려면 밑줄을 이스케이프 (예 : \ _ \ _)하거나 백틱을 사용하여 표시해야합니다 raw code(예 :`__`). @mmyers는 도움을 주려고했지만 밑줄 중 하나만 이스케이프 처리했기 때문에 이탤릭체에 대한 마크 업 구문이 남았습니다 . 그러나 여기에서 반대표는 약간 가혹하지만 동의합니다.
Matt B.

좋아, 이중 밑줄이 텍스트를 굵게 바꾸는 것을 깨닫지 못했고, 내 대답이 어떻게 보이는지 볼 시간이 없었습니다. 이제 이해합니다. 내 대답이 한 시간 늦어도 좋은 대답이었습니다. 가치를 추가하지 않았지만 틀린 것도 아니기 때문에 반대 할 이유가 없습니다. 그게 당신이 도우려고 노력해서 얻는 것입니다 ...
Sanctus2099

2
@ Sanctus2099 어떤 사람들은 투표가 빠르기 때문에 답이 올바른지 확인하는 것이 중요합니다. 이 경우 오답을 게시하고 4 시간 동안 수정하지 않은 상태로 두 셨습니다. 탓할 사람은 자신 외에는 없습니다.
meagar

2

필요한 경우 파일과 행을 쉽게 인쇄 할 수있는 "FILE_LINE"매크로 :

#define STRINGIZING(x) #x
#define STR(x) STRINGIZING(x)
#define FILE_LINE __FILE__ ":" STR(__LINE__)

1

나는 또한 지금이 문제에 직면하고 있고 여기 에서 묻는 다른 유효한 질문에 대한 답변을 추가 할 수 없기 때문에 문제에 대한 예제 솔루션을 제공 할 것입니다 : 함수가 호출 된 줄 번호 만 가져 오기 템플릿을 사용하는 C ++.

배경 : C ++에서는 형식이 아닌 정수 값을 템플릿 인수로 사용할 수 있습니다. 이것은 템플릿 인수로 데이터 유형의 일반적인 사용과 다릅니다. 따라서 아이디어는 함수 호출에 이러한 정수 값을 사용하는 것입니다.

#include <iostream>

class Test{
    public:
        template<unsigned int L>
        int test(){
            std::cout << "the function has been called at line number: " << L << std::endl;
            return 0;
        }
        int test(){ return this->test<0>(); }
};

int main(int argc, char **argv){
    Test t;
    t.test();
    t.test<__LINE__>();
    return 0;
}

산출:

이 함수는 줄 번호 : 0에서 호출되었습니다.

이 함수는 줄 번호 : 16에서 호출되었습니다.

여기서 언급해야 할 한 가지는 C ++ 11 Standard에서는 템플릿을 사용하여 함수에 대한 기본 템플릿 값을 제공 할 수 있다는 것입니다. C ++ 11 이전 버전에서는 형식이 아닌 인수에 대한 기본값이 클래스 템플릿 인수에 대해서만 작동하는 것 같습니다. 따라서 C ++ 11에서는 위와 같이 중복 된 함수 정의를 가질 필요가 없습니다. C ++ 11에서 그와 같은 리터럴 함께 사용하는 const를 문자 * 템플릿 인수하지만 가능하지 않은 것이 또한 유효 __FILE__또는 __func__언급 한 바와 같이 여기를 .

따라서 결국 C ++ 또는 C ++ 11을 사용하는 경우 매크로를 사용하여 호출 라인을 얻는 것보다 매우 흥미로운 대안이 될 수 있습니다.


1

을 사용 __LINE__하지만 그 유형은 무엇입니까?

LINE 현재 소스 행 (정수 상수)의 추정 행 번호 (현재 소스 파일 내).

AS를 정수 상수 , 코드는 종종 값이 가정 할 수 __LINE__ <= INT_MAX와 종류가 그래서 int.

C로 인쇄하려면 printf()일치하는 지정자가 필요합니다 : "%d". 이것은 C ++에서 cout.

현명한 우려 : 줄 번호가 INT_MAX1을 초과하면 (16 비트로 다소 생각할 수 있음 int) 컴파일러가 경고를 생성하기를 바랍니다. 예:

format '%d' expects argument of type 'int', but argument 2 has type 'long int' [-Wformat=]

또는 코드는 이러한 경고를 방지하기 위해 더 넓은 유형을 강제 할 수 있습니다.

printf("Not logical value at line number %ld\n", (long) __LINE__);
//or
#include <stdint.h>
printf("Not logical value at line number %jd\n", INTMAX_C(__LINE__));

기피 printf()

모든 정수 제한을 피하려면 : stringify . 코드는 printf()호출 없이 직접 인쇄 할 수 있습니다 . 오류 처리 2 에서 피하는 것이 좋습니다 .

#define xstr(a) str(a)
#define str(a) #a

fprintf(stderr, "Not logical value at line number %s\n", xstr(__LINE__));
fputs("Not logical value at line number " xstr(__LINE__) "\n", stderr);

1 그렇게 큰 파일을 갖는 것은 확실히 좋지 않은 프로그래밍 관행이지만 기계 생성 코드는 높을 수 있습니다.

2 디버깅에서 때때로 코드가 원하는대로 작동하지 않는 경우가 있습니다. 다음과 같은 복잡한 함수를 호출 *printf()하면 간단한 fputs().

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.