Номер строки 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: Некоторые из нас предпочитают не связываться с отладчиками. Этот вид «утверждения утверждения для бедняков» иногда более ясен, потому что это постоянная часть кода и постоянная документация о том, что должно быть верным в отношении состояния вычислений.
S.Lott

13
@Lucas: Отладчики также менее чем полезны для периодически возникающих проблем в долго работающих программах или для сбора информации о проблемах в программном обеспечении, развернутом на сайтах клиентов. В этих случаях единственный вариант для программы - это записать как можно больше информации о состоянии программы для последующего анализа.
KeithB

1
@Lucas И отладчики не так хорошо работают на некоторых встроенных системах, чтобы получить эту информацию.
Джордж Стокер

Ответы:


180

Вы должны использовать макрос препроцессора __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__);

2
C99 использует __func__, а не __FUNCTION__, который AFAIK частично устарел. Разница может сломать ваш код, потому что __func__ не может использоваться для конкатенации константных строк C.
Джозеф Куинси

1
Ссылка из руководства GCC: «__FUNCTION__ и __PRETTY_FUNCTION__ обрабатывались как строковые литералы; их можно было использовать для инициализации массивов символов, и они могли быть объединены с другими строковыми литералами. 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
Расмус Кадж

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>
phyatt

11

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



5

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был вызван. Вы можете попробовать это онлайн здесь.


4

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


Почему меня проголосовали против этого и почему mmyers отредактировали мой пост?
Sanctus2099

@ Sanctus2099: он был отредактирован, потому что Markdown преобразовал ваши двойные подчеркивания, чтобы отображать FILE и LINE жирным шрифтом (вы не проверяете, как выглядит ваш ответ?). Другой момент может заключаться в том (по крайней мере, мне сейчас так кажется), что вы дали ответ через 1 час после того, как был дан уже правильный ответ, поэтому вы не добавили никакой ценности.
Феликс Клинг,

Двойное подчеркивание - это синтаксис разметки жирного шрифта . Чтобы правильно отображать двойные подчеркивания, вы должны экранировать их (например: \ _ \ _) или использовать обратные кавычки, чтобы пометить их как raw code(например: `__`). @mmyers попытался помочь, но он избежал только одного из подчеркиваний, и поэтому у вас остался синтаксис разметки для курсива . Впрочем, я согласен с тем, что голоса против здесь немного резкие.
Мэтт Б.

Хорошо, я не понимал, что двойное подчеркивание превращает текст в полужирный, и мне пришлось уйти, и у меня не было времени посмотреть, как выглядел мой ответ. Теперь я понимаю. Даже если я опоздал на час, это был хороший ответ. Это не добавило никакой ценности, но и не было неправильным, поэтому нет причин для отрицательного голосования. Вот что вы получаете, пытаясь помочь ...
Sanctus2099,

2
@ Sanctus2099 Некоторые люди сразу же голосуют против, поэтому важно убедиться, что ваш ответ правильный. В этом случае вы отправили неверный ответ и оставили его без редактирования в течение 4 часов. Вам некого винить, кроме себя.
meagar

2

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

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.