Безопасно ли отключать буферизацию с помощью stdout и stderr?

Иногда мы помещаем некоторые отладочные отпечатки в наш код таким образом

printf("successfully reached at debug-point 1\n"); 

some code is here

printf("successfully reached at debug-point 2"); 

После последнего printf возникает ошибка сегментации.

Теперь в этом состоянии только debug-point1 будет напечатан на stdio. Debug-point 2 print был записан в буфер stdio, но не очищен, потому что не получил \n, поэтому мы думаем, что сбой произойдет после debug-point1.

Чтобы избавиться от этого, если я отключу опцию буферизации с потоками stdio и stderr следующим образом

setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);

Тогда безопасно ли это делать?

Почему все потоки по умолчанию буферизуются в строке?

Изменить:

Каков размер выделенного по умолчанию буфера для любого файлового потока? Я думаю, что это зависит от ОС. Я хотел бы знать о Linux.

Ответ 1

почему весь поток по умолчанию буферизируется по строке

Они буферизуются по соображениям производительности. Библиотека пытается избежать системного вызова, потому что требуется много времени. И не все из них буферизованы по умолчанию. Например, stderr обычно небуферизируется, а stdout - буферизируется только в том случае, если он ссылается на tty.

то безопасно ли это сделать?

Безопасно отключить буферизацию, но я должен сказать, что это не лучшая технология отладки.

Ответ 2

Возможным способом может быть глобальный флаг bool dodebug и определить макрос, например,

#ifdef NDEBUG
#define debugprintf(Fmt,...) do{} while(0)
#else
#define debugprintf(Fmt,...) do {if (dodebug) {                 \
   printf("%s:%d " Fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \
   fflush(stdout); }} while(0)
#endif

Затем внутри вашего кода используйте

debugprintf("here i=%d", i);

Конечно, вы могли бы в макроэлементе сделать fprintf вместо этого... Обратите внимание на fflush и добавленную новую строку в формате.

Отказ от буферизации, вероятно, следует избегать по соображениям производительности.

Ответ 3

Ну, хорошо. Вы не правы. Именно по этой причине stderr по умолчанию не буферизируется.

EDIT: Кроме того, в качестве общего предложения попробуйте использовать контрольные точки отладчика вместо printf s. Делает жизнь намного легче.

Ответ 4

Он "безопасен" в некотором смысле и небезопасен в другом. Небезопасно добавлять debug printfs и по той же причине небезопасно добавлять код для модификации буферизации stdio в том смысле, что это кошмар обслуживания. То, что вы делаете, НЕ является хорошей технологией отладки. Если ваша программа получает segfault, вы должны просто изучить дамп ядра, чтобы узнать, что произошло. Если это не подходит, запустите программу в отладчике и пройдите через нее, чтобы выполнить действие. Это звучит сложно, но это очень просто и очень важно. Здесь образец:

$ gcc -o segfault -g segfault.c   # compile with -g to get debugging symbols
$ ulimit -c unlimited             # allow core dumps to be written
$ ./segfault                      # run the program
Segmentation fault (core dumped)
$ gdb -q segfault /cores/core.3632  # On linux, the core dump will exist in
                                    # whatever directory was current for the
                                    # process at the time it crashed.  Usually
                                    # this is the directory from which you ran
                                    # the program.
Reading symbols for shared libraries .. done
Reading symbols for shared libraries . done
Reading symbols for shared libraries .. done
#0  0x0000000100000f3c in main () at segfault.c:5
5               return *x;          <--- Oh, my, the segfault occured at line 5
(gdb) print x                       <--- And it because the program dereferenced
$1 = (int *) 0x0                     ... a NULL pointer.

Ответ 5

Если ваша программа много пишет, отключение буферизации, вероятно, сделает ее где-то между 10 и 1000 раз медленнее. Обычно это нежелательно. Если ваша цель - это просто согласованность вывода при отладке, попробуйте добавить явные вызовы fflush, где вы хотите вывести поток, а не отключать буферизацию по всему миру. И желательно не писать сбой кода...