Numero di riga C / C ++


110

Per motivi di debug, posso ottenere il numero di riga nei compilatori C / C ++? (modalità standard o modalità specifiche per alcuni compilatori)

per esempio

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: alcuni di noi preferiscono non fare confusione con i debugger. Questo tipo di "dichiarazione di asserzione della persona povera" è talvolta più chiara perché è una parte permanente del codice e una documentazione duratura di cose che dovrebbero essere vere riguardo allo stato del calcolo.
S.Lott

13
@ Lucas: i debugger sono anche meno che utili per problemi intermittenti in programmi a lunga esecuzione o per raccogliere informazioni sui problemi nel software distribuito nei siti client. In questi casi, l'unica opzione è che il programma registri quante più informazioni possibili sullo stato del programma, per un'analisi successiva.
KeithB

1
@ Lucas E i debugger non funzionano così bene su alcuni sistemi embedded per ottenere queste informazioni.
George Stocker

Risposte:


180

È necessario utilizzare la macro del preprocessore __LINE__e __FILE__. Sono macro predefinite e fanno parte dello standard C / C ++. Durante la preelaborazione, vengono sostituiti rispettivamente da una stringa costante contenente un numero intero che rappresenta il numero di riga corrente e dal nome del file corrente.

Altre variabili del preprocessore:

  • __func__: nome della funzione (questo fa parte di C99 , non tutti i compilatori C ++ lo supportano)
  • __DATE__ : una stringa di formato "Mmm gg aaaa"
  • __TIME__ : una stringa di forma "hh: mm: ss"

Il tuo codice sarà:

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

2
C99 utilizza __func__ invece di __FUNCTION__, che AFAIK è parzialmente deprecato. La differenza può rompere il codice, perché __func__ non può essere utilizzato per la concatenazione di stringhe costanti di C.
Joseph Quinsey

1
Riferimento dal manuale di GCC: "__FUNCTION__ e __PRETTY_FUNCTION__ sono stati trattati come stringhe letterali; potrebbero essere usati per inizializzare array di caratteri e potrebbero essere concatenati con altri valori letterali di stringa. GCC 3.4 e successivamente trattarli come variabili, come __func__. In C ++, __FUNCTION__ e __PRETTY_FUNCTION__ sono sempre state variabili. "
Joseph Quinsey

C'è un modo per ottenere il numero di riga come una stringa, uguale al nome del file? Vorrei che il preprocessore mi fornisse ad esempio la stringa letterale "22" invece dell'intero 22.
sep332

1
@ sep332 Sì, ma il cpp è una strana bestia, quindi deve essere fatto in due passaggi con argomenti macro. #define S1(N) #N #define S2(N) S1(N) #define LINESTR S2(__LINE__). Vedi c-faq.com/ansi/stringize.html
Rasmus Kaj

1
A rigor di termini, __func__non è una macro, è una variabile dichiarata implicitamente.
HolyBlackCat

64

Come parte dello standard C ++ esistono alcune macro predefinite che puoi usare. La sezione 16.8 dello standard C ++ definisce, tra le altre cose, la __LINE__macro.

__LINE__: Il numero di riga della riga di origine corrente (una costante decimale).
__FILE__: Il presunto nome del file di origine (una stringa di caratteri letterale).
__DATE__: La data di traduzione del file di origine (una stringa di caratteri letterale ...)
__TIME__: L'ora di traduzione del file di origine (una stringa di caratteri letterale ...)
__STDC__: Se __STDC__è predefinita
__cplusplus: Il nome __cplusplusè definito al valore 199711L quando compilare un'unità di traduzione C ++

Quindi il tuo codice sarebbe:

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

19

È possibile utilizzare una macro con lo stesso comportamento di printf () , tranne per il fatto che include anche informazioni di debug come nome della funzione, classe e numero di riga:

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

Queste macro dovrebbero comportarsi in modo identico a printf () , includendo allo stesso tempo informazioni simili a java stacktrace. Ecco un esempio principale:

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

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

Che si traduce nel seguente output:

main (main.cpp: 11) Prima di exampleMethod () ...
exampleMethod (main.cpp: 7) printf () sintassi: string = foobar, int = 42
main (main.cpp: 13) Successo!


per lo sviluppo c, cambieresti il #includea<stdio.h>
phyatt

11

Usa __LINE__(che è doppio trattino basso LINE doppio trattino basso), il preprocessore lo sostituirà con il numero di riga in cui si trova.



5

C ++ 20 offre un nuovo modo per ottenere ciò utilizzando std :: source_location . Questo è attualmente accessibile in gcc un clang come std::experimental::source_locationcon #include <experimental/source_location>.

Il problema con le macro come __LINE__è che se si desidera creare, ad esempio, una funzione di registrazione che emetta il numero di riga corrente insieme a un messaggio, è sempre necessario passare __LINE__come argomento della funzione, poiché viene espansa nel sito della chiamata. Qualcosa come questo:

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

Restituirà sempre la riga della dichiarazione della funzione e non la riga da cui è logstata effettivamente chiamata. D'altra parte, con std::source_locationte puoi scrivere qualcosa del genere:

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

Qui, locviene inizializzato con il numero di riga che punta alla posizione in cui è logstato chiamato. Puoi provarlo online qui.


4

Prova __FILE__e __LINE__.
Potresti anche trovare __DATE__e __TIME__utile.
Tuttavia, a meno che non sia necessario eseguire il debug di un programma sul lato client e quindi registrare queste informazioni, è necessario utilizzare il normale debug.


Perché sono stato rifiutato e perché mmyers ha modificato il mio post?
Sanctus2099

@ Sanctus2099: È stato modificato, perché Markdown ha trasformato i tuoi doppi trattini bassi per visualizzare FILE e LINE in grassetto (non controlli come appare la tua risposta?). Un altro punto potrebbe essere (almeno mi sembra in questo modo ora) che tu abbia dato una risposta 1 ora dopo che è stata data una risposta già corretta, quindi non hai aggiunto alcun valore.
Felix Kling

Il doppio trattino basso è la sintassi del markup per il grassetto . Per visualizzare correttamente i doppi trattini bassi, è necessario eseguire l'escape (in questo modo: \ _ \ _) o utilizzare i backtick per contrassegnarli come raw code(in questo modo: `__`). @mmyers ha tentato di aiutare, ma è sfuggito solo a uno dei trattini bassi e quindi ti è rimasta la sintassi del markup per il corsivo . I voti negativi sono un po 'duri qui, però, sono d'accordo.
Matt B.

Ok, non mi ero reso conto del fatto che i doppi trattini bassi trasformano il testo in grassetto e sono dovuto andare e non ho avuto il tempo di guardare come appariva la mia risposta. Adesso capisco. Anche se la mia risposta era in ritardo di un'ora, era comunque una buona risposta. Non aggiungeva alcun valore, ma non era nemmeno sbagliato, quindi nessun motivo per votare negativo. Questo è quello che ottieni cercando di aiutare ...
Sanctus2099

2
@ Sanctus2099 Alcune persone votano rapidamente per difetto, ecco perché è importante assicurarsi che la risposta sia corretta. In questo caso, hai pubblicato una risposta sbagliata e l'hai lasciata inedita per 4 ore. Non hai nessuno da incolpare tranne te stesso.
meagar

2

Per coloro che potrebbero averne bisogno, una macro "FILE_LINE" per stampare facilmente file e riga:

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

1

Dal momento che sto affrontando anche questo problema ora e non posso aggiungere una risposta a una domanda diversa ma valida anche qui , fornirò una soluzione di esempio per il problema di: ottenere solo il numero di riga in cui è stata chiamata la funzione C ++ utilizzando modelli.

Background: in C ++ si possono usare valori interi non di tipo come argomento del modello. Questo è diverso dall'utilizzo tipico dei tipi di dati come argomenti del modello. Quindi l'idea è di utilizzare tali valori interi per una chiamata di funzione.

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

Produzione:

la funzione è stata chiamata al numero di riga: 0

la funzione è stata chiamata al numero di riga: 16

Una cosa da menzionare qui è che in C ++ 11 Standard è possibile fornire valori di template predefiniti per le funzioni che utilizzano template. Nelle versioni precedenti a C ++ 11 i valori predefiniti per argomenti non di tipo sembrano funzionare solo per argomenti del modello di classe. Pertanto, in C ++ 11, non sarebbe necessario avere definizioni di funzioni duplicate come sopra. In C ++ 11 è anche valido avere argomenti del modello const char * ma non è possibile usarli con letterali come __FILE__o __func__come menzionato qui .

Quindi, alla fine, se stai usando C ++ o C ++ 11 questa potrebbe essere un'alternativa molto interessante rispetto all'utilizzo di macro per ottenere la linea chiamante.


1

Usa __LINE__, ma qual è il suo tipo?

LINE Il numero di riga presunto (all'interno del file sorgente corrente) della riga sorgente corrente (una costante intera).

Come costante intera , il codice può spesso assumere che il valore sia __LINE__ <= INT_MAXe quindi il tipo lo è int.

Per la stampa in C, printf()ha bisogno l'identificatore di corrispondenza: "%d". Questa è una preoccupazione molto minore in C ++ con cout.

Preoccupazione pedante: se il numero di riga supera INT_MAX1 (in qualche modo concepibile con 16 bit int), si spera che il compilatore produca un avviso. Esempio:

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

In alternativa, il codice potrebbe forzare tipi più ampi a prevenire tali avvisi.

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

Evitare printf()

Per evitare tutte le limitazioni relative ai numeri interi: stringify . Il codice potrebbe essere stampato direttamente senza una printf()chiamata: una cosa carina da evitare nella gestione degli errori 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 Sicuramente una cattiva pratica di programmazione per avere un file così grande, ma forse il codice generato dalla macchina può andare alto.

2 Nel debug, a volte il codice semplicemente non funziona come sperato. Chiamare funzioni complesse come *printf()può incorrere in problemi rispetto a un semplice file fputs().

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.