2016-11-13 16 views
-2

Я пытаюсь открыть все возможности Printf и я попытался это:Почему printf не может правильно обрабатывать флаги, ширину поля и точность?

printf("Test:%+*0d", 10, 20); 

, который печатает

Тест:% + 100d

я должен использовать первый флаг +, затем ширина * и повторное использование флага 0.

Почему это делает этот выход? Я специально использовал printf() в плохой, но мне интересно, почему он показывает мне номер 100?

+4

«Я намеренно использовать Printf в плохом состоянии» и, таким образом, вы получаете UB, все может случиться. – Deduplicator

+0

@Deduplicator Правильно, но мне нужно перекодировать функцию, которая воспроизводит поведение printf – Sadek

+0

Вам нужно реализовать неопределенное поведение printf или просто определенное поведение? Надеюсь, только определенное поведение. (Если нет, у вас есть невыполнимая задача, потому что различные реализации printf реализуют неопределенное поведение по-разному, что вы будете имитировать?) В этом случае проблема (которую я надеюсь, вам не нужно беспокоиться) что флаг '0' следует спецификатору ширины' * '. –

ответ

2

Это потому, что вы поставляете синтаксическую бессмыслицу, чтобы она могла делать все, что захочет. Связанные чтения, undefined behavior.

компиляции кода с предупреждениями поддержкой и он расскажет вам, что-то вроде

предупреждение: неизвестный тип преобразования символов '0' в формате [-Wformat =]
printf("Test:%+*0d", 10, 20);
^

Верификация должна быть либо

  • printf("Test:%+*.0d", 10, 20); // note the '.'

    , где, то 0 используется в качестве точности

    связанной со ссылкой на C11, глава §7.21.6.1, (курсив мой )

    необязательная точность, которая дает минимальное количество цифр для d, i , o, u, x и X преобразования, количество цифр после десятичной точки символа для a, A, e, E, f и F преобразования, максимальное количество значащих цифр для g и G конверсий или максимальное количество байтов, которое должно быть записано для преобразований s. Точность принимает вид периода (.), за которым следует либо звездочка * (описанная ниже), либо необязательное десятичное целое;, если указан только период , точность принимается за нуль. Если появляется точность с любым другим спецификатором преобразования , поведение не определено.

  • printf("Test:%+0*d", 10, 20);

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

    Опять же, процитировать (и курсив мой)

    Каждая спецификация преобразования вводится символ %. После %, следующий появляются в последовательности:

    • ноль или более флагов (в любом порядке) [...]
    • Необязательное минимальная ширина поля [...]
    • опциональный точность [...]
    • Дополнительный модификатор длины [...]
    • спецификатор преобразования [....]
+0

Отлично. Я бы предложил * явное * упоминание о [неопределенном поведении] (https://en.wikipedia.org/wiki/Undefined_behavior), потому что многие новички не понимают этого понятия. –

+1

Хм, нет, он не доставляет ерунда компилятору, и он не свободен делать что-то произвольное. Однако реализация функции printf() может делать все, что захочет, с этой строкой. –

+0

@ HansPassant: вы вроде как правы. Но соответствующий компилятор может (и часто, например, по крайней мере [GCC] (http://gcc.gnu.org/)) обрабатывать 'printf' специально. –

1

В дополнении Sourav Ghosh's answer; важным является понятие undefined behavior, что сложно. Обязательно прочитайте блог Lattner: What Every C Programmer Should Know About Undefined Behavior. См. Также this.

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

Имейте в виду, что если на самом деле printf реализуется с помощью стандартной библиотеки C, это может быть (и часто является), специально обрабатывается компилятором (с GCC и GNU LIBC, что магия мощь происходит с помощью внутренне __builtin_printf)

C99 & Стандарты C11: частично, определяющие поведение printf, но оставляют некоторые неопределенные случаи поведения для облегчения реализации. Вы вряд ли полностью поймете или сможете имитировать эти случаи. А сама реализация может измениться (например, на моем Debian Linux, модернизация libc может изменить неопределенное поведение из printf)

Если вы хотите, чтобы понять больше printf изучить исходные некоторую стандартную библиотеку реализации C (например,musl-libc, код которого вполне читаем) и реализации GCC (при условии операционной системы Linux).

Но Сопровождающие ГНУ LIBC и НКА (& даже ядро ​​Linux, через системные вызовы) оставаться свободными, чтобы изменить неопределенной поведения (в printf и что-нибудь еще)

На практике всегда компилировать с gcc -Wall (и, вероятно, также -g) при использовании GCC. Не принимайте никаких предупреждений (так что улучшите свой собственный код, пока не получите его).

1

Ваш формат printf неверен: флаги должны предшествовать спецификатору ширины.

После того, как он обрабатывает * как ширина спецификатора, printf ожидает, либо . или длину модификатора или спецификатор преобразования, будучи 0 ни один из них, поведение не определено.

Выполнение вашей библиотеки printf делает что-то странное, кажется, обрабатывает *, заменив его фактическим аргументом ширины ... Побочным эффектом реализации. Другие могут делать что-то еще, в том числе прерывать программу. Такая ошибка формата была бы особенно рискованной, если бы последовало преобразование %s.

Изменение кода printf("Test:%+0*d", 10, 20); должен произвести ожидаемый результат:

Test:+000000020 

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

  • Нет связанных вопросов^_^