2014-01-15 6 views
2

Я знаю, что, чтобы правильно использовать printf(), нам нужно дополнительно передать такое же количество значений в соответствии с тем, что определено в const char* format, то есть %s/%f/%d....Различные проверки времени выполнения printf() для% s?

int printf(const char* format, ...); 

Я просто заметил, что, хотя и не рекомендуется, он будет работать без каких-либо ошибок во время выполнения, если мы не будем проходить в любых значениях, как следующее (конечно, мы получим не-ожидаемые результаты) :

printf("%a"); // ok 
printf("%c"); // ok 
printf("%d"); // ok 
printf("%e"); // ok 
printf("%f"); // ok 
printf("%g"); // ok 
printf("%i"); // ok 
printf("%o"); // ok 
printf("%p"); // ok 
printf("%u"); // ok 
printf("%x"); // ok 

Если это будет справедливо для всех форматов, я не буду чувствовать себя не удивлен, думая, что printf() не делает никаких проверок во время выполнения. Странно то, что это даст ошибку во время выполнения для %s (также кажется единственной).

printf("%s"); // run-time error: Access violation reading location 

Еще более интересным является то, что, кажется, что это только во время выполнения проверок для первого или подряда %s. Проверьте следующие примеры:

printf("%s%s", "xxx"); // run-time error: Access violation reading location 
printf("%s%d%s", "xxx"); // ok 

Итак, мой вопрос заключается в том, что:

  • ли printf() проверки во время выполнения по-разному для %s по сравнению с другими форматами? И почему?
  • Почему printf() только проверка времени на первый или следующий из %s?

ps: Я тестировал его под VS2010, ОС: Win7x64.

+0

Это действительно вопрос * C *, в этом вопросе нет ничего * C++ *. –

+2

Читайте на [variadic functions] (https://en.wikipedia.org/wiki/Variadic_function). 'printf' - одна из таких функций. С переменными функциями вы не можете проверять время на наличие аргументов, поэтому вы должны полагаться на другой известный аргумент, например строку формата в случае 'printf'. Если вы, как программист, не можете дать ему правильный аргумент, он будет читать все, что есть в стеке. '% d' и другие печатать эти значения мусора. '% s' принимает это значение мусора в качестве указателя и разыгрывает его, что вызывает ошибку сегментации (нарушение прав доступа). – Shahbaz

+1

Мне также интересно, не распечатывает ли fffs здесь? –

ответ

5

В этом случае C++ стандартное падение назад на C99 draft standard, который говорит, что это неопределенное поведение в разделе 7.19.6.1fprintf функции которая охватывает printf с спецификаторов формата уважения, говорит:

[.. .] Если для формата недостаточно аргументов, поведение равно undefined.

Так как стандарт говорит, что в определении для неопределенного поведения:

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

все может произойти.

С printf является variadic function, он должен полагаться на вызывающего, чтобы правильно представлять данные, переданные в. Таким образом, проверка времени выполнения невозможна. Хотя некоторые компиляторы предоставляют проверку времени компиляции, например, gcc даже предоставляет эту проверку как атрибут format (archetype, string-index, first-to-check), который может использоваться с вашими собственными функциями.

+4

+1 для цитирования стандартов –

2

[ED: Обратите внимание на этот вопрос изначально был помечен C++, и этот ответ предполагает ОП компиляцию в C++]

хотя и не рекомендуется, он будет работать без каких-либо ошибок во время выполнения, если мы этого не делаем передать любые значения,

Нет, это дает неопределенное поведение. Все может случиться, в том числе , что вы ожидаете. Это не значит, что ОК.

printfno Проверка времени выполнения любого нежелательного товара, который вы отправляете, если вы его вообще отправляете. Бремя на вы, чтобы вы могли написать правильный код.

Кстати, ваш более поздний случай, когда вы получали ошибки «необработанного исключения» - это не было результатом sprintf, выполняющим проверку времени. Это было еще одно проявление Неопределенного Поведения.

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

Урок жизни: не использовать sprintf в C++.Вместо этого используйте что-то современное и безопасное, как потоки.

2

Нет проверок времени выполнения. printf будет просто слепо читать любую тарабарщину в стеке (или какой-либо механизм var-arg используется). В случае %s он интерпретирует тарабарщину как указатель, а затем пытается прочитать материал с этого адреса, что обычно приводит к нарушению доступа. *

В сущности, вы видите не определено поведение.


* Хорошо, я предполагаю, что это своего рода-времени выполнения проверки, на OS/уровень HW.

0

Это неопределенное поведение, поэтому все может случиться. На практике разница, которую вы видите, может быть вызвана тем фактом, что все другие спецификаторы, которые вы использовали, ссылаются на значения непосредственно в стеке, поэтому отображается мусор, но он не падает. С% s это другое - случайное значение обрабатывается как указатель, и ваша программа обращается к случайной области памяти, поэтому, скорее всего, она сработает. Сказав это, он по-прежнему не определен и не должен использоваться.

1

Нет проверок времени выполнения, технически трудно сделать это. Но есть доступные проверки компиляции.

С GCC это -Wall.
С Visual Studio вам нужно запустить инструмент статического анализа (доступный в более дорогих версиях).

+1

+1 для введения * статического анализа *. – herohuyongtao

+1

+1 для написания _comment_. –

0

printf("%d"): Попытка прочитать 4 байта из возможного незаконного адреса памяти.

printf("%s"): Попытка прочитать неизвестное количество байтов из возможного незаконного адреса памяти до тех пор, пока не будет найден байт со значением 0.

В обоих случаях это «вопрос удачи» независимо от того, произойдет ли нарушение доступа к памяти.

Но, как вы можете понять, эти шансы намного выше в случае "%s".

Смежные вопросы