2014-11-16 3 views
7

This blog post утверждает, что прохождение va_list другой функции, как в следующем коде небезопасно, и что va_list должны сначала быть скопированы с помощью va_copy:Можно ли передать va_list другой функции без использования va_copy?

void 
foo_ap(const char *fmt, va_list ap) 
{ 
    char buf[128]; 

    vsnprintf(buf, sizeof(buf), fmt, ap); 

    // now, do something with buf ... 
} 

Как один из комментариев к сообщению в блоге упоминает, я могу Посмотрите, как это будет небезопасно, так как состояние спецификации C99 (7.15):

Объект ap может быть передан в качестве аргумента другой функции; если эта функция вызывает макрос va_arg с параметром ap, значение ap в вызывающей функции является неопределенным и должно быть передано в макрос va_end до любой дополнительной ссылки на ap.

(Пока foo_ap не ссылается ap после прохождения его, то вторая часть предложения, кажется, не применяются в любом случае.) Автор блога говорит в другом комментарии, что он может быть неправильно, но, возможно, кто-то может добавить некоторые разъяснения. Должен ли я действительно использовать va_copy, чтобы быть в безопасности? Или существуют какие-либо платформы, которые не соответствуют спецификации и требуют использования va_copy?

+3

Сообщение в блоге, кажется, прямо противоречит предложению, указанному в стандарте C99. Автор блога говорит «Давным-давно» в начале этого, поэтому я предполагаю, что он имел дело с какой-то старой, не соответствующей стандартам системой. –

+4

Вам нужно только 'va_copy', если вы все еще планируете использовать' ap' после 'vsnprintf'. Например, вы можете дважды называть 'vsnprintf' - один раз, чтобы определить необходимый размер буфера, и снова форматировать для реального. Если вам не нужно использовать 'ap' снова, за исключением передачи его в' va_end', вам не нужно 'va_copy' его. –

+0

@IgorTandetnik Если вы ответите на свой вопрос, я приму его. – nwellnhof

ответ

6

Вам нужно только va_copy, если вы все еще планируете использовать ap после vsnprintf. Например, вы можете дважды позвонить vsnprintf - один раз, чтобы определить размер требуемого буфера, и снова для форматирования для реального. Если вам не нужно снова использовать ap (за исключением, возможно, для передачи его va_end, если вы были его создателем с va_start), то вам не нужно va_copy.

+0

Я наблюдал реализации, когда va_list ведет себя так, как если бы он передавался по значению [поэтому аргументы, потребляемые функцией, которая его получает, возвращаются к существованию, когда эта функция возвращает], и другие, где она ведет себя, как если бы она передавалась по ссылке [аргументы остаются потребленными ]. Таким образом, проблемы с использованием va_list после вызова метода, который также использует его, являются не просто «теоретическими». – supercat

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