C / C ++ satır numarası


110

Hata ayıklama amacıyla, C / C ++ derleyicilerinde satır numarasını alabilir miyim ? (belirli derleyiciler için standart yol veya özel yollar)

Örneğin

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: Bazılarımız hata ayıklayıcılarla uğraşmamayı tercih ediyoruz. Bu tür bir "fakir kişinin iddia ifadesi" bazen daha nettir çünkü kodun kalıcı bir parçasıdır ve hesaplamanın durumu hakkında doğru olması gereken şeylerin kalıcı bir şekilde belgelenmesidir.
S.Lott

13
@Lucas: Hata ayıklayıcılar, uzun süre çalışan programlardaki kesintili problemler için veya istemci sitelerinde dağıtılan yazılımlardaki problemler hakkında bilgi toplamak için de pek yararlı değil. Bu durumlarda, tek seçenek programın daha sonra analiz etmek üzere programın durumu hakkında olabildiğince fazla bilgi kaydetmesidir.
KeithB

1
@Lucas Ve hata ayıklayıcılar bu bilgiyi almak için bazı gömülü sistemlerde o kadar iyi çalışmıyor.
George Stocker

Yanıtlar:


180

Önişlemci makrosunu __LINE__ve __FILE__. Önceden tanımlanmış makrolardır ve C / C ++ standardının bir parçasıdır. Ön işleme sırasında, bunlar sırasıyla geçerli satır numarasını temsil eden bir tamsayı tutan sabit bir dizeyle ve geçerli dosya adıyla değiştirilir.

Diğer önişlemci değişkenleri:

  • __func__: işlev adı (bu, C99'un bir parçasıdır, tüm C ++ derleyicileri bunu desteklemez)
  • __DATE__ : "Aaa gg yyyy" biçiminde bir dizi
  • __TIME__ : "hh: mm: ss" biçiminde bir dizi

Kodunuz şöyle olacaktır:

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

2
C99, AFAIK'in kısmen kullanımdan kaldırıldığı __FUNCTION__ yerine __func__ kullanır. __Func__ C'nin sabit dize birleştirmesi için kullanılamadığından, fark kodunuzu bozabilir.
Joseph Quinsey

1
GCC kılavuzundan referans: "__FUNCTION__ ve __PRETTY_FUNCTION__ dize değişmezleri olarak kabul edildi; char dizilerini başlatmak için kullanılabilirler ve diğer dize değişmezleri ile birleştirilebilirler. GCC 3.4 ve daha sonra bunları __func__ gibi değişkenler olarak ele alın. C ++ içinde __FUNCTION__ ve __PRETTY_FUNCTION__ her zaman değişken olmuştur. "
Joseph Quinsey

Dosya adıyla aynı satır numarasını bir dizge olarak almanın bir yolu var mı? Önişlemcinin bana 22 tamsayısı yerine değişmez dizesi "22" vermesini istiyorum.
sep332

1
@ sep332 Evet, ancak cpp tuhaf bir canavar, bu yüzden makro argümanlarıyla iki adımda yapılmalıdır. #define S1(N) #N #define S2(N) S1(N) #define LINESTR S2(__LINE__). Bkz. C-faq.com/ansi/stringize.html
Rasmus Kaj

1
Kesin olarak söylemek gerekirse, __func__makro değil, dolaylı olarak bildirilmiş bir değişkendir.
HolyBlackCat

64

C ++ standardının bir parçası olarak, kullanabileceğiniz bazı önceden tanımlanmış makrolar vardır. C ++ standardının 16.8 Bölümü, diğer şeylerin yanı sıra __LINE__makroyu tanımlar .

__LINE__: Geçerli kaynak satırının satır numarası (ondalık sabit).
__FILE__: Kaynak dosyanın varsayılan adı (bir karakter dizesi değişmezi).
__DATE__: Kaynak dosyanın çeviri tarihi (bir karakter dizesi değişmezi ...)
__TIME__: Kaynak dosyanın çevrilme zamanı (bir karakter dizesi değişmezi ...)
__STDC__:__STDC__ Önceden tanımlı olup olmadığı
__cplusplus: Ad __cplusplus, ne zaman 199711L değerine tanımlanır? C ++ çeviri birimini derleme

Yani kodunuz şöyle olacaktır:

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

19

Fonksiyon adı, sınıf ve satır numarası gibi hata ayıklama bilgilerini de içermesi dışında, printf () ile aynı davranışa sahip bir makro kullanabilirsiniz :

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

Bu makrolar , java stacktrace benzeri bilgiler dahil edilirken printf () ile aynı şekilde davranmalıdır . İşte bir örnek main:

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

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

Hangi aşağıdaki çıktıyla sonuçlanır:

main (main.cpp: 11) exampleMethod () ...
exampleMethod (main.cpp: 7) printf () sözdizimi: string = foobar, int = 42
main (main.cpp: 13) Başarı!


c gelişimi için, değiştirmek istiyorum #includeiçin<stdio.h>
phyatt

11

Kullanın __LINE__(bu çift alt çizgi LINE çift alt çizgi), önişlemci onu karşılaşıldığı satır numarasıyla değiştirecektir.



5

C ++ 20, std :: source_location kullanarak bunu başarmanın yeni bir yolunu sunar . Bu şekilde gcc bir clang şu anda erişilebilir std::experimental::source_locationolan #include <experimental/source_location>.

Makrolar gibi makrolarla ilgili sorun, __LINE__örneğin mevcut satır numarasını bir mesajla birlikte çıkaran bir günlüğe kaydetme işlevi oluşturmak istiyorsanız, her zaman __LINE__bir işlev bağımsız değişkeni olarak iletmeniz gerekir, çünkü arama sitesinde genişletilir. Bunun gibi bir şey:

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

logGerçekte çağrılan satırı değil, her zaman işlev bildiriminin satırını çıkarır . Öte yandan, std::source_locationbunun gibi bir şey yazabilirsiniz:

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

Burada, aranan locyeri gösteren satır numarası ile başlatılır log. Burada çevrimiçi deneyebilirsiniz.


4

Deneyin __FILE__ve __LINE__.
Ayrıca bulabilir __DATE__ve __TIME__yararlı olabilirsiniz .
Ancak, istemci tarafında bir programda hata ayıklamanız gerekmedikçe ve bu nedenle bu bilgileri günlüğe kaydetmeniz gerekmedikçe, normal hata ayıklamayı kullanmalısınız.


Neden buna karşı oy verdim ve mmyers neden gönderimi düzenledi?
Sanctus2099

@ Sanctus2099: Düzenlendi, çünkü Markdown çift alt çizginizi FILE ve LINE'ı kalın yazı tipinde gösterecek şekilde dönüştürdü (cevabınızın nasıl göründüğünü kontrol etmiyor musunuz?). Başka bir nokta da (en azından şimdi bana öyle görünüyor), zaten doğru bir cevap verildikten 1 saat sonra bir cevap vermiş olmanız, yani hiçbir değer katmamış olmanız olabilir.
Felix Kling

Çift alt çizgi, kalın için işaretleme sözdizimidir . Çift alt raw codeçizgileri düzgün bir şekilde görüntülemek için, onlardan kaçınmanız gerekir (bu gibi: \ _ \ _) veya işaretlemek için ters işaretler kullanmanız gerekir (şu şekilde: `__`). @mmyers yardım etmeye çalıştı, ancak o yalnızca altçizgilerden birinden kurtuldu ve bu nedenle italik için işaretleme sözdizimi kaldınız . Olumsuz oylar burada biraz sert olsa da, katılıyorum.
Matt B.

Tamam, çift alt çizginin metni kalın hale getirmesiyle ilgili şeyi fark etmedim ve gitmem gerekti ve cevabımın nasıl göründüğüne bakacak zamanım olmadı. Şimdi anlıyorum Cevabım bir saat geç olsa bile yine de iyi bir cevaptı. Herhangi bir değer katmadı, ancak yanlış da değildi, bu yüzden olumsuz oylama için bir neden yok. Yardım etmeye çalıştığınız için aldığınız şey bu ...
Sanctus2099

2
@ Sanctus2099 Bazı insanlar hızlı bir şekilde olumsuz oy kullanıyor, bu yüzden cevabınızın doğru olduğundan emin olmak önemlidir. Bu durumda, yanlış bir cevap gönderdiniz ve 4 saat boyunca değiştirmeden bıraktınız. Kendinden başka suçlayacak kimse yok.
meagar

2

İhtiyaç duyanlar için, dosya ve satırı kolayca yazdırmak için bir "FILE_LINE" makrosu:

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

1

Şu anda da bu sorunla karşı karşıya olduğum ve burada sorulan farklı ama aynı zamanda geçerli bir soruya yanıt ekleyemediğim için, şu sorun için örnek bir çözüm sunacağım: sadece işlevin çağrıldığı yerin satır numarasını alma Şablonları kullanarak C ++.

Arka plan: C ++ 'da tip dışı tamsayı değerleri şablon bağımsız değişkeni olarak kullanılabilir. Bu, veri türlerinin şablon bağımsız değişkenleri olarak tipik kullanımından farklıdır. Dolayısıyla fikir, bir işlev çağrısı için bu tür tamsayı değerleri kullanmaktır.

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

Çıktı:

işlev satır numarasından çağrıldı: 0

işlev satır numarası: 16'da çağrılmıştır

Burada belirtilmesi gereken bir şey, C ++ 11 Standardında şablon kullanan işlevler için varsayılan şablon değerleri vermenin mümkün olmasıdır. C ++ 11 öncesi tipte olmayan argümanlar için varsayılan değerler yalnızca sınıf şablonu argümanları için işe yarıyor gibi görünüyor. Böylece, C ++ 11'de, yukarıdaki gibi yinelenen işlev tanımlarına sahip olmaya gerek kalmayacaktır. C ++ 11 yılında onun gibi değişmezleri ile bunları kullanmak için const char * şablon argümanları ama onun mümkün değildir olması da geçerli __FILE__ya da __func__bahsedildiği gibi burada .

Sonuç olarak, C ++ veya C ++ 11 kullanıyorsanız, bu, arama hattını elde etmek için makroları kullanmaktan çok ilginç bir alternatif olabilir.


1

Kullanın __LINE__, ama türü nedir?

HAT Geçerli kaynak satırının (bir tam sayı sabiti) varsayılan satır numarası (geçerli kaynak dosyası içinde).

Bir tamsayı sabiti olarak , kod genellikle değerin __LINE__ <= INT_MAXve türünün olduğunu varsayabilir int.

C baskı için, printf()eşleştirme belirtici gerekir: "%d". Bu, C ++ ile çok daha az endişe kaynağıdır cout.

Bilgiçlik endişe: satır numarası aşarsa INT_MAX1 (16 bit ile biraz düşünülebilir int), umutla derleyici, bir uyarı üretecektir. Misal:

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

Alternatif olarak kod, daha geniş türleri bu tür uyarıları önlemeye zorlayabilir.

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

Önlemek printf()

Tüm tamsayı sınırlamalarını önlemek için: stringify . Kod, bir printf()arama olmadan doğrudan yazdırılabilir : hata işlemede kaçınılması gereken güzel bir şey 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 Bu kadar büyük bir dosyaya sahip olmak için kesinlikle zayıf programlama uygulaması, ancak belki de makine tarafından üretilen kod yüksek olabilir.

2 Hata ayıklamada, bazen kod umulduğu gibi çalışmaz. Gibi karmaşık işlevleri çağırmak *printf(), basit yerine sorunlara neden olabilir fputs().

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.