Это неопределенное поведение в стандарте. Это означает, что «все честно», потому что вы делаете неправильные вещи.
Хуже всего то, что любой компилятор наверняка предупредит вас, но вы проигнорировали предупреждение. Установка чего-то другого, кроме компилятора, повлечет за собой затраты, которые все заплатят, чтобы вы могли поступить неправильно.
Это противоположно тому, что обозначают C и C ++: вы платите за то, что используете. Если вы хотите оплатить стоимость, вы должны выполнить проверку.
Что на самом деле происходит, зависит от ABI, компилятора и архитектуры. Это неопределенное поведение, потому что язык дает разработчику свободу делать то, что лучше на каждой машине (то есть, иногда более быстрый код, иногда более короткий код).
Например, когда вы вызываете функцию на машине, это просто означает, что вы указываете микропроцессору перейти в определенное место кода.
В некоторых готовых сборках и ABI printf("%.*f", 5, 1);
будет выглядеть как
mov A, STR_F ; // load into register A the 32 bit address of the string "%.*f"
mov B, 5 ; // load second 32 bit parameter into B
mov F0, 1.0 ; // load first floating point parameter into register F0
call printf ; // call the function
Теперь, если вы пропустите какой-либо параметр, в данном случае B, он примет любое значение, которое было там раньше.
С такими функциями, как printf
, дело в том, что они допускают что угодно в своем списке параметров (это printf(const char*, ...)
, поэтому все допустимо). Вот почему вы не должны использовать printf
на C ++: у вас есть лучшие альтернативы, такие как потоки. printf
избегает проверок компилятора. потоки лучше осведомлены о типах и могут быть расширены для ваших собственных типов. Кроме того, именно поэтому ваш код должен компилироваться без предупреждений.