Программирование на С:ошибки сегментации, printf и связанные с ними особенности

StackOverflow https://stackoverflow.com/questions/975448

Вопрос

Как и многие молодые программисты, я понял полезность вставки многочисленных операторов вывода на консоль «здесь1», «здесь2» и т. д. в разные точки кода, чтобы выяснить, когда мои программы идут не так, как надо.Этот метод грубой отладки спасал меня много-много раз во время моего обучения CS.Однако, когда я начал программировать на C, я столкнулся с интересной проблемой.Если бы я попытался сбежать

void* test;

printf("hello world");
test[5] = 234;

Конечно, я получаю ошибку за то, что не выделил память для testChar.Тем не менее, вы могли бы логически подумать, что «hello world» будет напечатано до того, как произойдет ошибка сегмента, поскольку это поток кода, но, по моему опыту, всегда бывает, что ошибка сегмента происходит первой, и «hello world» " вообще никогда не выводится на консоль.(Мне не удалось протестировать именно этот пример, но я много раз сталкивался с подобной ситуацией, используя gcc в Linux.) Я предполагаю, что это связано либо с перестановкой некоторых вещей компилятором, либо с printf. используя какой-то буфер, который сбрасывается асинхронно и, следовательно, не является немедленным.Это полностью мои предположения, потому что я, честно говоря, не знаю, почему это происходит.На любом другом языке, который я использовал, независимо от того, какую проблему вызвала строка «testChar =...», «hello world» все равно будет напечатано, и таким образом я смогу определить, в чем проблема.

Мой вопрос: почему это происходит, когда я программирую C?Почему hello world не печатается первым?И во-вторых, существует ли лучший метод отладки программирования на языке C, чем этот, который выполняет ту же самую основную задачу?Например, простой/интуитивный способ найти строку кода, которая является проблемой?

Редактировать:Я случайно привел рабочий пример, ха-ха.То, что у меня есть сейчас, должно вызвать сегфолт.Забавно, как обычно, когда я не хочу segfault, я его получаю, и теперь, когда я действительно хотел его, я написал юридический код!

Это было полезно?

Решение

Размещенный вами код совершенно легален и не должен вызывать ошибку segfault - нет необходимости что-либо выделять.Ваша проблема должна заключаться где-то в другом месте - опубликуйте наименьший пример кода, вызывающего проблему.

Редактировать: Теперь вы отредактировали код, чтобы он имел совершенно другое значение.Тем не менее, причина, по которой «привет, мир» не отображается, заключается в том, что выходной буфер не был очищен.Попробуйте добавить

fflush( stdout );

после printf.

Что касается поиска источника проблемы, у вас есть несколько вариантов:

  • обильно добавьте printfs в свой код, используя __FILE__ и __LINE__ Макросы C
  • научитесь использовать отладчик — если ваша платформа поддерживает дампы ядра, вы можете использовать образ ядра, чтобы найти место ошибки.

Другие советы

printf записывает в стандартный вывод, который буферизуется.Иногда этот буфер не очищается до сбоя вашей программы, поэтому вы никогда не увидите вывод.Два способа избежать этого:

  1. использовать fprintf( stderr, "error string" ); поскольку stderr не буферизуется.
  2. добавить звонок в fflush( stdout ); после вызова printf.

Как сказали Нил и другие, написанный код в порядке.То есть до тех пор, пока вы не начнете изменять буфер, который testChar указывает на.

«Например, простой/интуитивный способ найти проблемную строку кода?»

Используйте GDB (или любой другой отладчик).

Чтобы определить, где в вашей программе есть ошибки, вы скомпилируете ее с помощью -g вариант (для включения символов отладки) запустите приложение из БДБ, он остановится из-за неисправности сегмента.

Затем вы можете посмотреть обратную трассировку с помощью bt команда, чтобы увидеть, в какой момент у вас возникла ошибка сегмента.

пример:

> gdb ./x
(gdb) r
Starting program: /proj/cpp/arr/x 
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00000000
0x000019a9 in willfail () at main.cpp:22
22          *a = 3;
(gdb) bt
#0  0x000019a9 in willfail () at main.cpp:22
#1  0x00001e32 in main () at main.cpp:49
(gdb) 

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

fprintf(stderr, "hello, world\n");

(stderr по умолчанию не буферизуется.)

Этот код не должен вызывать сбоев.Вы просто присваиваете указатель на литеральную строку переменной-указателю.Все было бы иначе, если бы вы были, например.с использованием strcpy копировать материал с неверным указателем.

Отсутствие сообщения может быть связано с буферизованным вводом-выводом.Распечатать символ новой строки \n или позвоните по телефону fflush для очистки выходного буфера.

У вас есть две проблемы.Во-первых, ваш (исходный) код не будет срабатывать.Совершенно правильно присвоить эту строковую константу указателю на символ.Но давайте пока оставим это в стороне и представим, что вы положили туда что-то, что бы сегмент.

Тогда обычно дело в буферах: в библиотеке времени выполнения C и в самой ОС.Вам нужно их промыть.

Самый простой способ сделать это (в UNIX не совсем уверен в fsync в Linux, но вы должны быть уверены, что это произойдет в конечном итоге, если только сама система не выйдет из строя):

printf ("DEBUG point 72\n"); fflush (stdout); fsync (fileno (stdout));

Я часто делал это в UNIX, и это гарантирует, что библиотеки времени выполнения C будут сброшены в UNIX (fflush), а буферы UNIX синхронизируются с диском (fsync), полезно, если стандартный вывод не является терминальным устройством или вы делаете это для другого дескриптора файла.

void* test;

printf("hello world");
test[5] = 234;

Вполне вероятно, что «привет, мир» где-то буферизуется системой и не выводится сразу на экран.Он хранится в ожидании возможности, чтобы какой-либо процесс/поток/то, что отвечает за запись на экране, мог его обработать.И пока он ожидает (и, возможно, буферизует другие данные для вывода), ваша функция завершается.Это связано с незаконным доступом и сбоями в сегментах.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top