Nomor baris C / C ++


110

Demi tujuan debugging, bisakah saya mendapatkan nomor baris di kompiler C / C ++? (cara standar atau cara khusus untuk kompiler tertentu)

misalnya

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: Beberapa dari kita memilih untuk tidak main-main dengan debugger. "Pernyataan pernyataan orang miskin" semacam ini terkadang lebih jelas karena ini adalah bagian permanen dari kode, dan dokumentasi abadi tentang hal-hal yang seharusnya benar tentang status komputasi.
S. Lotot

13
@Lucas: Debugger juga kurang berguna untuk masalah intermiten dalam program yang berjalan lama, atau untuk mengumpulkan informasi tentang masalah dalam perangkat lunak yang disebarkan di situs klien. Dalam kasus ini, satu-satunya pilihan adalah program untuk mencatat sebanyak mungkin informasi tentang status program, untuk analisis nanti.
KeithB

1
@Lucas Dan debugger tidak bekerja dengan baik pada beberapa sistem tertanam untuk mendapatkan informasi ini.
George Stocker

Jawaban:


180

Anda harus menggunakan makro preprocessor __LINE__dan __FILE__. Mereka adalah makro yang telah ditentukan sebelumnya dan bagian dari standar C / C ++. Selama preprocessing, mereka diganti masing-masing dengan string konstan yang memegang integer yang mewakili nomor baris saat ini dan dengan nama file saat ini.

Variabel preprocessor lainnya:

  • __func__: nama fungsi (ini adalah bagian dari C99 , tidak semua kompiler C ++ mendukungnya)
  • __DATE__ : string dengan format "Mmm dd yyyy"
  • __TIME__ : string dalam bentuk "hh: mm: ss"

Kode Anda akan menjadi:

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

2
C99 menggunakan __func__ daripada __FUNCTION__, yang AFAIK sudah tidak digunakan lagi. Perbedaannya dapat merusak kode Anda, karena __func__ tidak dapat digunakan untuk penggabungan string konstan C.
Joseph Quinsey

1
Referensi dari manual GCC: "__FUNCTION__ dan __PRETTY_FUNCTION__ diperlakukan sebagai literal string; keduanya dapat digunakan untuk menginisialisasi larik karakter, dan dapat digabungkan dengan literal string lainnya. GCC 3.4 dan kemudian memperlakukannya sebagai variabel, seperti __func__. Dalam C ++, __FUNCTION__ dan __PRETTY_FUNCTION__ selalu menjadi variabel. "
Joseph Quinsey

Apakah ada cara untuk mendapatkan nomor baris sebagai string, sama dengan nama file? Saya ingin preprocessor memberi saya misalnya string literal "22" bukan integer 22.
sep332

1
@ sep332 Ya, tetapi cpp adalah binatang yang aneh, jadi ini harus dilakukan dalam dua langkah dengan argumen makro. #define S1(N) #N #define S2(N) S1(N) #define LINESTR S2(__LINE__). Lihat c-faq.com/ansi/stringize.html
Rasmus Kaj

1
Tegasnya, __func__ini bukan makro, ini adalah variabel yang dideklarasikan secara implisit.
HolyBlackCat

64

Sebagai bagian dari standar C ++, terdapat beberapa makro yang telah ditentukan sebelumnya yang dapat Anda gunakan. Bagian 16.8 dari standar C ++ mendefinisikan antara lain __LINE__makro.

__LINE__: Nomor baris dari baris sumber saat ini (konstanta desimal).
__FILE__: Nama yang diperkirakan dari file sumber (literal string karakter).
__DATE__: Tanggal terjemahan file sumber (karakter string literal ...)
__TIME__: Waktu terjemahan file sumber (karakter string literal ...)
__STDC__: Apakah __STDC__sudah ditentukan sebelumnya
__cplusplus: Nama __cplusplusditentukan ke nilai 199711L saat menyusun unit terjemahan C ++

Jadi kode Anda adalah:

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

19

Anda bisa menggunakan makro dengan perilaku yang sama seperti printf () , kecuali makro juga menyertakan informasi debug seperti nama fungsi, kelas, dan nomor baris:

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

Makro ini harus berperilaku identik dengan printf () , sementara menyertakan informasi mirip java stacktrace. Berikut contoh mainnya:

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

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

Yang menghasilkan keluaran sebagai berikut:

main (main.cpp: 11) Sebelum exampleMethod () ...
exampleMethod (main.cpp: 7) printf () sintaks: string = foobar, int = 42
main (main.cpp: 13) Berhasil!


untuk pengembangan c, Anda harus mengubah #includeto<stdio.h>
phyatt

11

Gunakan __LINE__(itu garis bawah ganda LINE garis bawah ganda), preprocessor akan menggantinya dengan nomor baris yang ditemukan.



5

C ++ 20 menawarkan cara baru untuk melakukannya dengan menggunakan std :: source_location . Ini saat ini dapat diakses di gcc dan clang as std::experimental::source_locationwith#include <experimental/source_location> .

Masalah dengan makro seperti itu __LINE__adalah jika Anda ingin membuat, misalnya fungsi logging yang mengeluarkan nomor baris saat ini bersama dengan pesan, Anda harus selalu meneruskan __LINE__sebagai argumen fungsi, karena itu diperluas di situs panggilan. Sesuatu seperti ini:

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

Akan selalu menampilkan baris deklarasi fungsi dan bukan baris tempat logsebenarnya dipanggil. Di sisi lain, dengan std::source_locationAnda dapat menulis sesuatu seperti ini:

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

Di sini, locdiinisialisasi dengan nomor baris yang menunjuk ke lokasi tempat logdipanggil. Anda dapat mencobanya secara online di sini.


4

Coba __FILE__dan __LINE__.
Anda mungkin juga menemukan __DATE__dan __TIME__berguna.
Meskipun kecuali Anda harus men-debug program di sisi klien dan dengan demikian perlu mencatat informasi ini, Anda harus menggunakan debugging normal.


Mengapa saya menolak ini dan mengapa mmyers mengedit posting saya?
Sanctus2099

@ Sanctus2099: Itu telah diedit, karena Markdown mengubah garis bawah ganda Anda menjadi FILE dan LINE dalam huruf tebal (tidakkah Anda memeriksa bagaimana jawaban Anda terlihat?). Poin lain mungkin (setidaknya menurut saya seperti ini sekarang) bahwa Anda memberikan jawaban 1 jam setelah jawaban yang sudah benar diberikan, jadi Anda tidak menambahkan nilai.
Felix Kling

Garis bawah ganda adalah sintaks markup untuk cetak tebal . Untuk menampilkan garis bawah ganda dengan benar, Anda harus menghindarinya (seperti ini: \ _ \ _) atau gunakan backticks untuk menandainya sebagai raw code(seperti ini: `__`). @mmyers berusaha membantu, tetapi dia hanya lolos dari salah satu garis bawah, dan karenanya Anda ditinggalkan dengan sintaks markup untuk cetak miring . Suara negatif agak keras di sini, saya setuju.
Matt B.

Oke, saya tidak menyadari tentang garis bawah ganda yang mengubah teks menjadi tebal dan saya harus pergi dan tidak punya waktu untuk melihat tampilan jawaban saya. Saya mengerti sekarang. Bahkan jika jawaban saya terlambat satu jam, itu tetap jawaban yang bagus. Itu tidak menambah nilai apa pun tetapi itu juga tidak salah jadi tidak ada alasan untuk downvote. Itulah yang Anda dapatkan untuk mencoba membantu ...
Sanctus2099

2
@ Sanctus2099 Beberapa orang cepat menolak, itulah mengapa penting untuk memastikan jawaban Anda benar. Dalam hal ini, Anda memposting jawaban yang salah dan membiarkannya tidak diedit selama 4 jam. Anda tidak bisa menyalahkan siapa pun kecuali diri Anda sendiri.
meagar

2

Bagi mereka yang mungkin membutuhkannya, makro "FILE_LINE" untuk dengan mudah mencetak file dan baris:

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

1

Karena saya juga menghadapi masalah ini sekarang dan saya tidak dapat menambahkan jawaban untuk pertanyaan berbeda tetapi juga valid yang ditanyakan di sini , saya akan memberikan contoh solusi untuk masalah: hanya mendapatkan nomor baris tempat fungsi tersebut dipanggil C ++ menggunakan template.

Latar belakang: di C ++ seseorang dapat menggunakan nilai integer bukan tipe sebagai argumen template. Ini berbeda dari penggunaan tipikal tipe data sebagai argumen templat. Jadi idenya adalah menggunakan nilai integer tersebut untuk pemanggilan fungsi.

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

Keluaran:

fungsi telah dipanggil pada nomor baris: 0

fungsi telah dipanggil pada nomor baris: 16

Satu hal yang perlu disebutkan di sini adalah bahwa dalam C ++ 11 Standard dimungkinkan untuk memberikan nilai template default untuk fungsi yang menggunakan template. Dalam pra C ++ 11 nilai default untuk argumen non-tipe tampaknya hanya berfungsi untuk argumen template kelas. Jadi, di C ++ 11, tidak perlu memiliki definisi fungsi duplikat seperti di atas. Di C ++ 11 juga valid untuk memiliki argumen template char * const tetapi tidak memungkinkan untuk menggunakannya dengan literal seperti __FILE__atau __func__seperti yang disebutkan di sini .

Jadi pada akhirnya jika Anda menggunakan C ++ atau C ++ 11, ini mungkin alternatif yang sangat menarik daripada menggunakan makro untuk mendapatkan jalur panggilan.


1

Gunakan __LINE__, tapi apa tipenya?

LINE Jumlah baris yang diperkirakan (dalam file sumber saat ini) dari baris sumber saat ini (konstanta integer).

Sebagai konstanta integer , kode sering kali dapat mengasumsikan nilainya adalah __LINE__ <= INT_MAXdan jenisnya adalah int.

Untuk cetak di C, printf()membutuhkan specifier yang cocok: "%d". Ini adalah perhatian yang jauh lebih kecil di C ++ dengan cout.

Pedantic concern: Jika nomor baris melebihi INT_MAX1 (bisa dibayangkan dengan 16-bit int), mudah-mudahan kompilator akan menghasilkan peringatan. Contoh:

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

Alternatifnya, kode dapat memaksa tipe yang lebih luas untuk mencegah peringatan tersebut.

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

Menghindari printf()

Untuk menghindari semua batasan integer: stringify . Kode dapat langsung dicetak tanpa printf()panggilan: hal yang baik untuk dihindari dalam penanganan kesalahan 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 Tentu saja praktik pemrograman yang buruk untuk memiliki file sebesar itu, namun mungkin kode yang dihasilkan mesin dapat menjadi tinggi.

2 Dalam debugging, terkadang kode tidak berfungsi seperti yang diharapkan. Memanggil fungsi kompleks seperti *printf()itu sendiri bisa menimbulkan masalah vs. sederhana fputs().

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.