Numer wiersza C / C ++


110

Czy ze względu na debugowanie mogę uzyskać numer wiersza w kompilatorach C / C ++? (sposób standardowy lub specyficzny dla niektórych kompilatorów)

na przykład

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: Niektórzy z nas wolą nie zadzierać z debuggerami. Ten rodzaj „stwierdzenia osoby biednej” jest czasem bardziej jasny, ponieważ jest trwałą częścią kodu i trwałą dokumentacją tego, co powinno być prawdą o stanie obliczenia.
S.Lott

13
@Lucas: Debugery są również mniej niż przydatne w przypadku sporadycznych problemów w długo działających programach lub do zbierania informacji o problemach z oprogramowaniem wdrożonym w witrynach klienckich. W takich przypadkach jedyną opcją jest zapisanie przez program jak największej ilości informacji o stanie programu w celu późniejszej analizy.
KeithB

1
@Lucas A debuggery nie działają tak dobrze w niektórych systemach wbudowanych, aby uzyskać te informacje.
George Stocker

Odpowiedzi:


180

Należy użyć makra preprocesora __LINE__i __FILE__. Są to predefiniowane makra i część standardu C / C ++. Podczas przetwarzania wstępnego są one zastępowane odpowiednio stałym łańcuchem zawierającym liczbę całkowitą reprezentującą numer bieżącego wiersza i bieżącą nazwę pliku.

Inne zmienne preprocesora:

  • __func__: nazwa funkcji (jest to część C99 , nie wszystkie kompilatory C ++ ją obsługują)
  • __DATE__ : ciąg w postaci „Mmm dd yyyy”
  • __TIME__ : ciąg w postaci „hh: mm: ss”

Twój kod będzie:

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

2
C99 używa __func__ zamiast __FUNCTION__, który AFAIK jest częściowo przestarzały. Różnica może zepsuć kod, ponieważ __func__ nie może być użyte do konkatenacji ciągów stałych w C.
Joseph Quinsey

1
Odniesienie z podręcznika GCC: „__FUNCTION__ i __PRETTY_FUNCTION__ były traktowane jako literały łańcuchowe; można je było wykorzystać do zainicjowania tablic char i można je było łączyć z innymi literałami łańcuchowymi. GCC 3.4 i później traktuje je jako zmienne, takie jak __func__. W C ++, __FUNCTION__ i __PRETTY_FUNCTION__ zawsze były zmiennymi. "
Joseph Quinsey

Czy istnieje sposób, aby uzyskać numer linii jako ciąg, taki sam jak nazwa pliku? Chciałbym, aby preprocesor podał mi np. Literał „22” zamiast liczby całkowitej 22.
wrzesień332

1
@ sep332 Tak, ale cpp to dziwna bestia, więc należy to zrobić w dwóch krokach z argumentami makr. #define S1(N) #N #define S2(N) S1(N) #define LINESTR S2(__LINE__). Zobacz c-faq.com/ansi/stringize.html
Rasmus Kaj

1
Ściśle mówiąc, __func__nie jest makrem, to niejawnie zadeklarowana zmienna.
HolyBlackCat

64

W ramach standardu C ++ istnieje kilka wstępnie zdefiniowanych makr, których można użyć. Sekcja 16.8 standardu C ++ definiuje między innymi __LINE__makro.

__LINE__: Numer wiersza bieżącego źródła (stała dziesiętna).
__FILE__: Przypuszczalna nazwa pliku źródłowego (literał ciągu znaków).
__DATE__: Data tłumaczenia pliku źródłowego (literał ciągu znaków ...)
__TIME__: Czas tłumaczenia pliku źródłowego (literał ciągu znaków ...)
__STDC__: Czy __STDC__jest wstępnie zdefiniowany
__cplusplus: Nazwa __cplusplusjest zdefiniowana na wartość 199711L, gdy kompilowanie jednostki tłumaczeniowej C ++

Twój kod wyglądałby więc tak:

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

19

Możesz użyć makra zachowującego się tak samo jak printf () , z tą różnicą, że zawiera również informacje debugowania, takie jak nazwa funkcji, klasa i numer wiersza:

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

Te makra powinny zachowywać się identycznie jak printf () , jednocześnie włączając informacje podobne do java stacktrace. Oto przykład główny:

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

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

Co daje następujący wynik:

main (main.cpp: 11) Przed exampleMethod () ...
exampleMethod (main.cpp: 7) printf () składnia: string = foobar, int = 42
main (main.cpp: 13) Sukces!


dla c rozwoju, należy zmienić #includena<stdio.h>
phyatt

11

Użyj __LINE__(to jest podwójne podkreślenie Podwójne podkreślenie LINE), preprocesor zastąpi go numerem linii, w której jest napotkany.



5

C ++ 20 oferuje nowy sposób osiągnięcia tego za pomocą std :: source_location . Jest to obecnie dostępne w gcc an clang jak w std::experimental::source_locationprzypadku #include <experimental/source_location>.

Problem z takimi makrami __LINE__polega na tym, że jeśli chcesz utworzyć na przykład funkcję rejestrującą, która wyświetla bieżący numer linii wraz z komunikatem, zawsze musisz przekazać __LINE__jako argument funkcji, ponieważ jest on rozwijany w miejscu wywołania. Coś takiego:

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

Zawsze wyświetla wiersz deklaracji funkcji, a nie wiersz, z którego logzostała wywołana. Z drugiej strony std::source_locationmożesz napisać coś takiego:

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

Tutaj locjest inicjalizowany numerem linii wskazującym na lokalizację, w której logzostał wywołany. Możesz spróbować online tutaj.


4

Spróbuj __FILE__i __LINE__.
Możesz również znaleźć __DATE__i __TIME__przydatne.
Chociaż jeśli nie musisz debugować programu po stronie klienta, a tym samym musisz rejestrować te informacje, powinieneś użyć normalnego debugowania.


Dlaczego zostałem odrzucony w głosowaniu w tej sprawie i dlaczego mmyers zmienili mój post?
Sanctus2099

@ Sanctus2099: Został zmieniony, ponieważ Markdown przekształcił Twoje podwójne podkreślenia, aby wyświetlić plik i LINIĘ pogrubioną czcionką (nie sprawdzasz, jak wygląda twoja odpowiedź?). Inną kwestią może być (przynajmniej tak mi się teraz wydaje), że udzieliłeś odpowiedzi 1 godzinę po udzieleniu już poprawnej odpowiedzi, więc nie dodałeś żadnej wartości.
Felix Kling

Podwójne podkreślenie to składnia znaczników dla pogrubienia . Aby poprawnie wyświetlać podwójne podkreślenia, musisz zmienić ich znaczenie (w ten sposób: \ _ \ _) lub użyć odwrotnych znaków, aby oznaczyć je jako raw code(w ten sposób: `__`). @mmyers próbował pomóc, ale uniknął tylko jednego z podkreśleń, więc pozostała ci składnia znaczników dla kursywy . Jednak głosy przeciwne są tutaj nieco szorstkie, zgadzam się.
Matt B.

OK, nie zdawałem sobie sprawy z tego, że podwójne podkreślenia zmieniają tekst w pogrubiony i musiałem iść i nie miałem czasu, aby spojrzeć, jak wygląda moja odpowiedź. Teraz rozumiem. Nawet jeśli moja odpowiedź była spóźniona o godzinę, to i tak była dobra. Nie dodało to żadnej wartości, ale też nie było złe, więc nie ma powodu do głosowania przeciw. To właśnie dostajesz, próbując pomóc ...
Sanctus2099,

2
@ Sanctus2099 Niektórzy ludzie szybko głosują przeciw, dlatego ważne jest, aby upewnić się, że Twoja odpowiedź jest prawidłowa. W takim przypadku zamieściłeś złą odpowiedź i pozostawiłeś ją bez edycji przez 4 godziny. Nie możesz winić nikogo oprócz siebie.
meagar

2

Dla tych, którzy mogą tego potrzebować, makro „FILE_LINE” do łatwego drukowania pliku i wiersza:

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

1

Ponieważ teraz stawiam czoła temu problemowi i nie mogę dodać odpowiedzi na inne, ale również ważne pytanie zadane tutaj , podam przykładowe rozwiązanie problemu: uzyskanie tylko numeru wiersza, w którym funkcja została wywołana C ++ przy użyciu szablonów.

Tło: w C ++ można użyć wartości całkowitych innych niż typ jako argumentu szablonu. Różni się to od typowego użycia typów danych jako argumentów szablonu. Chodzi więc o to, aby użyć takich wartości całkowitych dla wywołania funkcji.

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

Wynik:

funkcja została wywołana pod numerem linii: 0

funkcja została wywołana pod numerem linii: 16

Należy tu wspomnieć, że w C ++ 11 Standard można podać domyślne wartości szablonu dla funkcji korzystających z szablonu. W pre C ++ 11 domyślne wartości argumentów innych niż typowe wydają się działać tylko dla argumentów szablonu klasy. Zatem w C ++ 11 nie byłoby potrzeby stosowania zduplikowanych definicji funkcji, jak powyżej. W C ++ 11 można również mieć argumenty szablonu const char *, ale nie można ich używać z literałami takimi jak __FILE__lub __func__jak wspomniano tutaj .

Więc w końcu, jeśli używasz C ++ lub C ++ 11, może to być bardzo interesująca alternatywa niż używanie makr do uzyskania linii wywołującej.


1

Użyj __LINE__, ale jaki jest jego typ?

LINIA Przypuszczalny numer wiersza (w bieżącym pliku źródłowym) bieżącego wiersza źródłowego (stała całkowita).

Jako stała liczba całkowita kod często przyjmuje wartość, __LINE__ <= INT_MAXa więc typ jest int.

Aby drukować w C, printf()potrzebuje specyfikator dopasowanie: "%d". Jest to znacznie mniejszy problem w C ++ z cout.

Pedantyczna obawa: jeśli numer linii przekroczy INT_MAX1 (nieco wyobrażalne w przypadku wersji 16-bitowej int), miejmy nadzieję, że kompilator wyświetli ostrzeżenie. Przykład:

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

Alternatywnie, kod mógłby zmusić szersze typy do uprzedzania takich ostrzeżeń.

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

Uniknąć printf()

Aby uniknąć wszystkich ograniczeń liczb całkowitych: stringify . Kod może być drukowany bezpośrednio bez printf()wywołania: miła rzecz, której należy unikać w obsłudze błędów 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 Z pewnością kiepska praktyka programistyczna przy tak dużym pliku, ale być może kod generowany maszynowo może być wysoki.

2 Podczas debugowania czasami kod po prostu nie działa zgodnie z oczekiwaniami. Wywoływanie złożonych funkcji, takich jak *printf()samo w sobie, może powodować problemy, a nie proste fputs().

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.