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: Деякі з нас вважають за краще не возитися з налагоджувачами. Такий вид "твердження твердження бідної людини" іноді є більш зрозумілим, оскільки це постійна частина коду та міцна документація речей, які повинні відповідати стану обчислень.
С.Лотт

13
@Lucas: Налагоджувачі також є менш корисними для непостійних проблем у тривалих програмах або для збору інформації про проблеми програмного забезпечення, розгорнутого на клієнтських сайтах. У цих випадках єдиний варіант - програма записувати якомога більше інформації про стан програми для подальшого аналізу.
KeithB

1
@Lucas І налагоджувачі не так добре працюють у деяких вбудованих системах, щоб отримати цю інформацію.
Джордж Стокер

Відповіді:


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 використовує __func__, а не __FUNCTION__, яка AFAIK частково застаріла. Різниця може порушити ваш код, оскільки __func__ не може бути використаний для константації постійних рядків C.
Джозеф Квінсі

1
Посилання з посібника GCC: "__FUNCTION__ та __PRETTY_FUNCTION__ розглядалися як рядкові літерали; їх можна використовувати для ініціалізації масивів char, і вони можуть бути об'єднані з іншими рядковими літералами. 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 ++ існує кілька заздалегідь визначених макросів, які ви можете використовувати. Розділ 16.8 стандарту C ++ визначає, серед іншого, __LINE__макрос.

__LINE__: Номер рядка поточного рядка джерела (десяткова константа).
__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)

Ці макроси повинні поводитись однаково з printf () , включаючи інформацію, схожу на java stacktrace. Ось головний приклад:

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>
фіат

11

Використовуючи __LINE__(це подвійне підкреслення LINE з подвійним підкресленням), препроцесор замінить його номером рядка, на якому він зустрічається.



5

C ++ 20 пропонує новий спосіб досягти цього, використовуючи std :: source_location . Наразі це доступно в gcc a 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: Це було відредаговано, тому що Маркдаун перетворив ваші подвійні підкреслення, щоб відобразити FILE та LINE жирним шрифтом (чи не ви перевіряєте, як виглядає ваша відповідь?). Іншим моментом може бути (принаймні, на мене це виглядає зараз), що ви дали відповідь через 1 годину після того, як вже була надана правильна відповідь, тож ви не додали ніякої цінності.
Фелікс Клінг

Подвійне підкреслення - це синтаксис розмітки для жирного . Щоб правильно відобразити подвійні підкреслення, потрібно уникати їх (як-от: \ _ \ _) або використовувати задні посилання, щоб позначити їх як raw code(наприклад: `__`). @mmyers намагалися допомогти, але він уникнув лише однієї з підкреслень, і, таким чином, вам залишився синтаксис розмітки для курсиву . Тут низовики трохи суворі, я згоден.
Метт Б.

Гаразд, я не усвідомлював того, що подвійні підкреслення перетворюють текст жирним шрифтом, і я повинен був піти і не встиг подивитися, як виглядає моя відповідь. Я зараз розумію. Навіть якщо моя відповідь запізнилася на годину, це все-таки хороша відповідь. Це не додало жодної цінності, але не було помилковим, тому немає жодної причини. Ось що ви отримуєте, намагаючись допомогти ...
Sanctus2099

2
@ Sanctus2099 Деякі люди швидко проголосують, тому важливо переконатися, що ваша відповідь правильна. У цьому випадку ви написали неправильну відповідь і залишили її без редагування протягом 4 годин. Ви нікого не звинувачуєте, окрім себе.
meagar

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 char *, але їх неможливо використовувати з буквами на зразок __FILE__або __func__як зазначено тут .

Зрештою, якщо ви використовуєте C ++ або C ++ 11, це може бути дуже цікавою альтернативою, ніж використання макросів для отримання лінії виклику.


1

Використання __LINE__, але який його тип?

LINE Передбачуваний номер рядка (у поточному файлі джерела) поточного рядка джерела (ціла константа).

Як ціла константа , код часто може вважати, що значення є __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()

Щоб уникнути всіх цілих обмежень: строфікуйте . Код можна надрукувати безпосередньо без 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().


1

Для тих, хто може знадобитися, макрос "FILE_LINE", щоб легко надрукувати файл і рядок:

#define STRINGIZING(x) #x
#define STR(x) STRINGIZING(x)
#define FILE_LINE __FILE__ ":" STR(__LINE__)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.