Номер строки C/C++

Могу ли я в целях отладки получить номер строки в компиляторах C/C++? (стандартный способ или специальные способы для определенных компиляторов)

e.g

if(!Logical)
    printf("Not logical value at line number %d \n",LineNumber);
    // How to get LineNumber without writing it by my hand?(dynamic compilation)

person Betamoo    schedule 17.05.2010    source источник
comment
@Lucas: Некоторые из нас предпочитают не связываться с отладчиками. Этот тип утверждений бедного человека иногда более понятен, потому что это постоянная часть кода и постоянная документация того, что должно быть правдой в отношении состояния вычислений.   -  person S.Lott    schedule 17.05.2010
comment
@Lucas: отладчики также менее чем полезны для периодически возникающих проблем в долго работающих программах или для сбора информации о проблемах в программном обеспечении, развернутом на клиентских сайтах. В этих случаях единственным вариантом для программы является регистрация как можно большего количества информации о состоянии программы для последующего анализа.   -  person KeithB    schedule 17.05.2010
comment
@Lucas И отладчики не так хорошо работают на некоторых встроенных системах, чтобы получить эту информацию.   -  person George Stocker    schedule 10.12.2015


Ответы (10)


Вы должны использовать макрос препроцессора __LINE__ и __FILE__. Это предопределенные макросы и часть стандарта C/C++. Во время предварительной обработки они заменяются соответственно постоянной строкой, содержащей целое число, представляющее номер текущей строки, и текущим именем файла.

Другие переменные препроцессора:

  • __func__ : имя функции (это часть C99, не все компиляторы C++ поддерживают его)
  • __DATE__ : строка вида "Ммм дд гггг"
  • __TIME__ : строка вида "чч:мм:сс"

Ваш код будет:

if(!Logical)
  printf("Not logical value at line number %d in file %s\n", __LINE__, __FILE__);
person Julien Hoarau    schedule 17.05.2010
comment
C99 использует __func__, а не __FUNCTION__, который, насколько мне известно, частично устарел. Разница может сломать ваш код, потому что __func__ нельзя использовать для конкатенации константных строк C. - person Joseph Quinsey; 17.05.2010
comment
Ссылка из руководства GCC: __FUNCTION__ и __PRETTY_FUNCTION__ обрабатывались как строковые литералы; их можно использовать для инициализации массивов символов, и их можно объединять с другими строковыми литералами. GCC 3.4 и более поздние версии обрабатывают их как переменные, например __func__. В C++ __FUNCTION__ и __PRETTY_FUNCTION__ всегда были переменными. - person Joseph Quinsey; 17.05.2010
comment
Есть ли способ получить номер строки в виде строки, такой же, как имя файла? Я хотел бы, чтобы препроцессор дал мне, например. строковый литерал 22 вместо целого числа 22. - person sep332; 28.11.2012
comment
Что касается текста первого аргумента для printf (), должен ли макрос, используемый во втором аргументе, на самом деле быть __LINE__-1, в примере в этом ответе? - person Toby; 02.03.2015
comment
@ sep332 Попробуйте это (непроверено!): char *convert_line_number(unsigned long line) { char line_string[15]; sprintf(&line_string, "%ul", line); return (&line_string); Вызовите так: int main(int argc, char **argv) { printf("%s", convert_line_number(__LINE__); return (0); } - person Jeremy; 04.04.2015
comment
@ sep332 Да, но cpp - странный зверь, поэтому его нужно делать в два этапа с аргументами макроса. #define S1(N) #N #define S2(N) S1(N) #define LINESTR S2(__LINE__) . См. c-faq.com/ansi/stringize.html. - person Rasmus Kaj; 16.04.2015
comment
Строго говоря, __func__ — это не макрос, это неявно объявленная переменная. - person HolyBlackCat; 23.01.2017
comment
@JosephQuinsey, это не правда (по крайней мере, уже нет): These identifiers are variables, not preprocessor macros, and may not be used to initialize char arrays or be concatenated with string literals. (см. gcc.gnu.org/onlinedocs/gcc/Function-Names.html) - person garlix; 18.07.2018

В рамках стандарта C++ существует несколько предопределенных макросов, которые вы можете использовать. Раздел 16.8 стандарта C++ определяет, среди прочего, макрос __LINE__.

__LINE__: номер текущей строки исходного кода (десятичная константа).
__FILE__: предполагаемое имя исходного файла (строковый литерал).< br> __DATE__: Дата перевода исходного файла (строковый литерал...)
__TIME__: Время перевода исходного файла (a литерал символьной строки...)
__STDC__: Является ли __STDC__ предопределенным
__cplusplus: Имя __cplusplus определено как значение 199711L при компиляции перевода C++ единица измерения

Таким образом, ваш код будет:

if(!Logical)
  printf("Not logical value at line number %d \n",__LINE__);
person Brian R. Bondy    schedule 17.05.2010

Вы можете использовать макрос с тем же поведением, что и 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. Вот пример основного:

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) Успех !

person Some Java Programmer    schedule 07.11.2014
comment
для разработки c вы бы изменили #include на <stdio.h> - person phyatt; 12.04.2016
comment
printf не является async-signal-safe. Поэтому этот макрос нельзя использовать в обработчиках сигналов. - person ceving; 28.06.2021

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

person meagar    schedule 17.05.2010

Макросы Checkout __FILE__ и __LINE__

person Anton    schedule 17.05.2010

C++20 предлагает новый способ добиться этого с помощью std::source_location. . В настоящее время это доступно в gcc как 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. Вы можете попробовать это онлайн здесь.

person sirain    schedule 25.10.2019

Попробуйте __FILE__ и __LINE__.
Вам также могут пригодиться __DATE__ и __TIME__.
Однако, если вам не нужно отлаживать программу на стороне клиента и, следовательно, необходимо регистрировать эту информацию, вам следует использовать обычную отладку.

person Sanctus2099    schedule 17.05.2010
comment
Почему меня проголосовали против этого и почему mmyers отредактировал мой пост? - person Sanctus2099; 17.05.2010
comment
@ Sanctus2099: Он был отредактирован, потому что Markdown преобразовал ваши двойные символы подчеркивания, чтобы отображать FILE и LINE жирным шрифтом (вы не проверяете, как выглядит ваш ответ?). Другим моментом может быть (по крайней мере, сейчас мне так кажется), что вы дали ответ через 1 час после того, как был дан уже правильный ответ, поэтому вы не добавили никакой ценности. - person Felix Kling; 17.05.2010
comment
Двойное подчеркивание — это синтаксис разметки для жирного шрифта. Чтобы правильно отображать двойные символы подчеркивания, вы должны экранировать их (например, \_\_) или использовать обратные кавычки, чтобы пометить их как raw code (например, `__`). @mmyers пытался помочь, но он избегал только одного символа подчеркивания, и поэтому у вас остался синтаксис разметки для курсивного начертания. Тем не менее, отрицательные голоса здесь немного суровы, я согласен. - person mbauman; 17.05.2010
comment
Хорошо, я не понял, что двойные подчеркивания превращают текст в жирный шрифт, и мне пришлось идти, и у меня не было времени посмотреть, как выглядит мой ответ. Теперь я понимаю. Даже если мой ответ опоздал на час, это все равно был хороший ответ. Это не добавило никакой ценности, но и не было ошибкой, поэтому нет причин для отрицательного голосования. Вот что ты получаешь за попытку помочь... - person Sanctus2099; 17.05.2010
comment
@ Sanctus2099 Некоторые люди быстро голосуют против, поэтому важно убедиться, что ваш ответ правильный. В этом случае вы разместили неправильный ответ и оставили его без редактирования в течение 4 часов. Вам некого винить, кроме себя. - person meagar; 17.05.2010
comment
Извините, в превью выглядело нормально. Мои извинения. - person Michael Myers; 18.05.2010

Для тех, кому это может понадобиться, макрос "FILE_LINE" для простой печати файла и строки:

#define STRINGIZING(x) #x
#define STR(x) STRINGIZING(x)
#define FILE_LINE __FILE__ ":" STR(__LINE__)
person clarkttfu    schedule 14.05.2020

Поскольку я также столкнулся с этой проблемой сейчас и не могу добавить ответ на другой, но также правильный вопрос, заданный здесь, я приведу пример решения проблемы: получение только номера строки, в которой функция была вызвана в С++ с использованием шаблонов.

Предыстория: в 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 можно указать значения шаблона по умолчанию для функций, использующих шаблон. В пред-С++ 11 значения по умолчанию для аргументов, не являющихся типами, похоже, работают только для аргументов шаблона класса. Таким образом, в C++11 не было бы необходимости иметь повторяющиеся определения функций, как указано выше. В С++ 11 также допустимо иметь аргументы шаблона const char *, но невозможно использовать их с литералами, такими как __FILE__ или __func__, как упоминалось здесь.

Итак, в конце концов, если вы используете C++ или C++11, это может быть очень интересной альтернативой использованию макроса для получения строки вызова.

person John Doe    schedule 09.02.2016

Используйте __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()

Чтобы избежать всех ограничений на целочисленные значения: strify. Код может печатать напрямую без вызова 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().

person chux - Reinstate Monica    schedule 19.02.2020