С++ для начинающих. Функции с переменным числом параметров

Иногда у начинающих возникает вопрос про использование многоточия в языке С++.
  • Многоточие можно использовать тогда, когда по каким-то причинам предполагается создать функцию с неизвестным заранее числом идущих в функцию аргументов. В объявлении и определении такой функции переменное число аргументов задается многоточием, обязательно в конце списка формальных параметров.
  • Многоточие иногда используют, но в зависимости от компиляторов, один из старых способов может совсем не давать желаемого эффекта. До С++11 рекомендуется использовать только вариант с stdarg , а варианты, показанные внизу страницы, рекомендуется не использовать вообще.
На заметку:

  • Для того, чтобы получить доступ ко всем параметрам, принимающих значения аргументов, заходящих в функцию, обязательно знать имя и тип хотя бы одного параметра.
В функцию можно отдавать произвольное число аргументов и не зависеть от типов аргументов. Чтобы функция могла обработать подобный подарок, имеет смысл использовать специальный набор макроопределений, которые становятся доступными при включении в программу заголовочного файла stdarg.h. Эти макроопределения обеспечивают стандартный (независящий от реализации) способ доступа к спискам параметров переменной длины.
Код С++ функции с произвольным числом параметров переменной длины (C++ 3.1)
До С++11 рекомендуется использовать stdarg

  • Cпециальный тип va_list используется для представления списков параметров неизвестной длины и состава.
  • va_start вызывается непосредственно перед началом работы с неименованными параметрами.
  • ар инициализируется указателем на последний именованный параметр в списке с переменным числом параметров — параметр lastarg.

    Здесь подпараметр является условным обозначением и относится к макрокомандам, а не функциям.
  • После вызова макроопределения va_start, каждый вызов va_arg возвращает значение заказанного типа type себе. Надо заранее указать тип желаемого параметра. В некоторых реализациях с макроопределением va_arg запрещено использовать типы char, unsigned char, float. Даже в макроопределениях, предназначенных для стандартизации языка (речь идет не о Borland C++ 3.1, а о более новых), многое зависит от реализации.
  • Работа со списком параметров завершается вызовом макроопределения void va_end(ap); Это макроопределение обеспечивает корректный возврат из функции и вызывается перед выходом из функции.
Код С++ Функции с произвольным числом параметров переменной длины:

===================================
Код С++. НЕ РЕКОМЕНДУЕТСЯ. Может работать и правильно, и неправильно: зависит от компилятора и от компьютера, на котором выполняется программа.

Этот код не только не рекомендуется, но рекомендуется забыть навсегда, что так можно было когда-то. Хотя некоторые функции в языке С работают именно на этом принципе, в языке С++ для работы с переменным числом параметров, во всех компиляторах, не поддерживающих С++11, нужно использовать только макрос stdarg.
Хотя вы можете встречать следы истории в книгах и на некоторых сайтах, где может приводиться пример суммы:

Несмотря на то, что этот код может сработать у вас как и ожидается, если вам повезёт, то достаточно использовать double вместо int и всё пойдёт насмарку. Если не очень повезёт, то сработает, как ожидалось. Но это полностью платформозависимый код. Ужасный код.



23 комментария на «“С++ для начинающих. Функции с переменным числом параметров”»

  1. Марк:

    Чётко, чётко

  2. 1:

    😆 😆 😆 😆 😆 😆 😆 😆 😆 😆 😆 😆 😆 😆 😆 😆 😆 😆 😆 😆 😆 😆

  3. ttkm:

    target pattern contains no «%» stop
    выскакивает эта ошибка и не работает задача, подскажите плиз как этого миновать, плиз 😥

  4. Вовка:

    как мне подставить формулу

    S = a1 * a2 + a2 * a3 + a3 * a4 + . . .;

    допусутим для 5 переменных
    s + = (*p)*(*p) — не катит((

    • Это обязательно через такую функцию делать?
      Если я знаю ответ, то завтра отвечу. Если не знаю ответа, то завтра скажу, что не знаю.

      Могу только предположить, что в месте вызова функции, прописывать можно что-то типа

      ну и смотреть пример с суммой.
      но не могу сказать, что это вообще работать будет, проверить не могу.

      Не в описании функции, а там, откуда ее вызываем.

      • или же

        как-то так (проверить не могу)

    • проверил

      но работает это в Borland C++ 3.1, в Linux же, например, весь этот алгоритм сам по себе ошибка. Правильнее было бы использовать код подобно коду из последнего примера.

  5. Александр:

    У меня вопрос — и он на праздный вот такой — 😈 👿 — сам я знаток языка, использую эти вот штуки. но вот теперь на си++ обнаружил что обязательного параметра может и не быть — а это тот самый случай который мне нужен. у меня конструктор, и сразу можно подцепить к обьекту связанные с ним штуки(ну это обьект со штуками такой, вы меня понимаете) т.е. передаются указатели, замыкает ноль — конец списка.и синтаксичестки правильно и компилятор разделяет мою точку зрения, да вот незадача то 🙁 🙁 👿 😡 😥 😥 😥 😥 раз нет первого параметра — ЧЕМ мне бедолаге горемычному инициализировать указатель тот волшебный??? Липман молчит ка рыба об лёд, а у Интернета я только начал интересоваться и забрёл на ваш сайт.

    • Сам я совсем не знаток языка и не использую такие штуки, поэтому мне немного забавна ситуация с обращением ко мне за советом от знатока.
      Если вы знаток, то должны знать, что на компилятор надеяться глупо. Он многое допускает, но не все, что он допускает будет впоследствии работать и не все допустимые конструкции получится использовать.

      Если бы я встретил способ, о котором вы спрашиваете, то я бы описал его еще до вашего вопроса.
      Но способа такого, насколько я знаю, нет.

      Должна быть точка отсчета. Точка отсчета узнается по первому параметру.
      Грубо говоря, когда компилятор работает, чтобы запустить программу, для каждой найденной функции он выделяет какую-то область памяти, а также для каждой переменной по функции тоже выделяется память. Первый такой адрес по переменной для функции как раз и будет точкой отсчета.

      Зачем компилятор допускает что-то типа void myfunc(…) я понятия не имею.

      • Максим:

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

        • если это имеет отношение к функциям вида myfunc(…), я бы очень хотел увидеть пример. это сложно найти.
          ===================
          по поводу претензий и сегменташин фолт — я всего лишь заимствовал коды из других источников и собрал их сюда. Так как они написаны в разных книгах и на разных форумах, авторов я указывать не стал. (я не знаю истинного автора) но исправлю

          • Максим:

            этот прототип(как и любой другой) говорит транслятору только то, что для функции (будь она от void или с переменным списком параметров) к моменту раскрытия ссылок на тело, это тело будет где-то существовать и он может смело продолжать формировать объектник.

            • а поконкретнее? я ж не компилятор
              прототип прототипом, я спрашивал не про прототип, а про функцию с телом.

              вопрос, который мне задали, куда вы далее вписали и свой комментарий с пояснением — это вопрос о таких функциях. Зачем допускается возможность?

          • Максим:

            не допускается, компилятор выдаст ошибку.
            прототип возможно в некоторых компиляторах и пройдёт.

  6. Максим:

    а во большинстве листингах ошибки, функция контролирует конец списка параметров по нулю в конце, а вызывая функцию этот самый ноль в конец не добавляете, в итоге привет сегменташин фолт.
    также, шагая по списку параметров нельзя просто прибавлять размер параметра, так как некоторые типы при засовывании в стек расширяются, например на 32битных системах обычно при засовывании short и char расширяются до int т.е. записываются в стек 4 байта. так что для чтения параметров нужно пользоваться макросом va_arg(list,type), а не прибавлять размер предыдущего считанного параметра.

  7. fo:

    Админ вот решение которое в большинстве компиляторах работает:

    А вот еще вопрос: Если мы пройдемся по указателю то почему будет не 5 3 9 1 7 0,а 5 0 3 0 9 0 1 0 7 0 0 почему так?
    почему при перенаправке адреса следуещее значение 0,а не числа в аргументе функции?

    • Потому что в Вашем компиляторе это не работает и выводится мусор.
      Такое решение не нужно использовать.
      Тем не менее его можно встречать, но нужно понимать, что оно непереносимое и как решение на С++ — плохое.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Поиск

 
     

Случайная книга в электронном формате

https://www.litres.ru/gennadiy-samkov/jquery-sbornik-receptov/?lfrom=15589587
Яндекс.Метрика