2015-05-14 2 views
3

Определение функции PRINTF в C Язык является:Почему printf требует обязательный параметр?

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

То же самое для scanf и много подобных функций, где управляют переменное число аргументов.

Почему существует обязательный параметр _Format?

+2

Потому что в противном случае звонок был бы не-оператором? – Amit

+1

Вопрос меня озадачивает; как в противном случае будет задан желаемый формат функции? Вы предпочитаете перегрузки для форматированного вывода каждого элементарного типа данных? – Codor

+0

Потому что нет смысла вызывать функцию, если нет переданного ей параметра. –

ответ

7

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

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

Например, это действует как printf, но печатает на обеих stdout и другой файл по вашему выбору:

void tee(FILE *f, char const *fmt, ...) { 
    va_list ap; 
    va_start(ap, fmt); 
    vprintf(fmt, ap); 
    va_end(ap); 
    va_start(ap, fmt); 
    vfprintf(f, fmt, ap); 
    va_end(ap); 
} 

Это использует vprintf и vfprintf, поэтому он не (непосредственно) использовать va_arg себя, только va_start и va_end, но этого достаточно, чтобы показать, как fmt участвует в использовании va_start.

В свое время это фактически не требовалось. Когда C был блестящим и новым, у вас могла бы быть функция, эквивалентная функции: int f(...);.

Во время первой попытки стандартизации C, однако, это было устранено в пользу макросов, отмеченных выше (va_start, va_arg, va_end), которые требуют, по меньшей мере, один параметр с именем.Старшие макросы устанавливали ряд требований в вызывающем соглашении:

  1. Параметры всегда передаются одинаково, независимо от типа или номера.
  2. Всегда можно найти первый аргумент, который был передан.

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

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

Способ, с которым они столкнулись, заключался в том, чтобы передать непосредственно предыдущий (названный) аргумент va_start (и va_start - это макрос, который обычно использует адрес этого аргумента). Если вы нажмете справа налево, это даст вам адрес, независимо от расстояния, необходимого в стеке, тогда va_arg может вернуться обратно до стек, чтобы получить другие переменные.

Это, очевидно, рассматривалось как приемлемый компромисс, тем более что функции, которые принимают переменные аргументы, почти всегда принимают по крайней мере один именованный параметр.

+0

Я бы лучше объяснил, как всплывают аргументы из стека, вместо использования подпрограмм varargs, чтобы лучше понять и объяснить значение требуемого параметра. В любом случае я одобряю ваш ответ. Спасибо – gaetanoM

+1

@gaemaf: Аргументы не обязательно поступают из стека ;-), но я согласен, что более простой пример был бы полезен. – chqrlie

+0

Несмотря на то, что этот ответ пропустил более фундаментальный момент и вникает в то, что по сути является деталями реализации - будучи стандартной функцией, 'printf' не нужно реализовывать в терминах' va_start' и т. Д. –

4

Потому что он не хочет, чтобы догадаться, что для печати

1

Это обязательное, поскольку printf используется для печати данных. Представьте, что произойдет, если вы ничего не напечатаете. Ничего. Итак, зачем удалять этот параметр?

Это то же самое о scanf: вам нужно читать данные каким-то образом и как вы собираетесь это сделать, если вы не знаете, формат этих данных?

Некоторые функции не имеют параметров, потому что они не нужны, например,

void Hello(void) { puts("Hello"); } 

Таким образом, они могут «выжить» без параметров. О printf:

int printf(void) { //imaginary function, don't use it! 
    // WTF? What to print? 
    // Absolutely nothing! What's the purpose then? 
    return smth; 
} 

Тогда это printf является абсолютно бесполезны, когда нет аргументов передаются.

0

Без описания формата printf не понимает, что печатать. В C все просто байты, поэтому printf не имеет представления о том, какие данные передаются ему, и поэтому не имеет представления о том, как его представлять.

Когда вы новичок в C, вы еще не знаете, насколько это верно, особенно если вы изучили язык, на котором print() понимает тип данных, которые он видит.

1

В общем случае функции, которые имеют неизвестное количество аргументов, полагаются на va_start, va_arg и va_end для обработки аргументов, которые явно не указаны в списке параметров функции.

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

Для параметра printf параметр/аргумент, определяющий спецификацию формата, является лучшим выбором в качестве требуемого параметра/аргумента.

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