0Pricing
C Academy · Урок

Макросы stdarg

va_list, va_start, va_arg

«Макросы stdarg» — бесплатный урок C Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C Academy содержит 4 уроков всего.

Что такое функции с переменным числом аргументов

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

В C можно создавать собственные функции с помощью макросов <stdarg.h>.

#include <stdio.h>

int main(void) {
    printf("%d %d %d\n", 1, 2, 3);
    printf("%s\n", "any count of args");
    return 0;
}

Многоточие

Функцию с переменным числом аргументов объявляют с помощью ... (многоточия) в качестве последнего параметра.

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

#include <stdio.h>
#include <stdarg.h>

void demo(int count, ...) {
    printf("This function takes %d extra args\n", count);
}

int main(void) {
    demo(3, 10, 20, 30);
    return 0;
}

Подключение stdarg.h

Чтобы считывать переменные аргументы, необходимо добавить #include <stdarg.h>.

Этот заголовочный файл определяет тип va_list и макросы va_start, va_arg и va_end.

#include <stdio.h>
#include <stdarg.h>

void show(int n, ...) {
    va_list args;
    va_start(args, n);
    va_end(args);
    printf("set up and cleaned up\n");
}

int main(void) {
    show(2, 5, 6);
    return 0;
}

va_list и va_start

Объявите переменную типа va_list для отслеживания текущей позиции. Затем вызовите va_start(list, last), передав последний именованный параметр.

Это указывает списку на первый переменный аргумент.

#include <stdio.h>
#include <stdarg.h>

void first(int count, ...) {
    va_list args;
    va_start(args, count);
    int value = va_arg(args, int);
    printf("first variadic arg = %d\n", value);
    va_end(args);
}

int main(void) {
    first(1, 99);
    return 0;
}

va_arg: чтение значений

va_arg(list, type) возвращает следующий аргумент и перемещает список вперёд. Необходимо указать точный тип для чтения.

Каждый вызов перемещает позицию на один аргумент вперёд.

#include <stdio.h>
#include <stdarg.h>

void three(int n, ...) {
    va_list args;
    va_start(args, n);
    int a = va_arg(args, int);
    int b = va_arg(args, int);
    int c = va_arg(args, int);
    printf("%d %d %d\n", a, b, c);
    va_end(args);
}

int main(void) {
    three(3, 7, 8, 9);
    return 0;
}

va_end: очистка

Всегда вызывайте va_end(list), когда закончите чтение. Эта функция выполняет необходимую платформе очистку.

Если забыть её вызвать, в некоторых системах это приведёт к неопределённому поведению.

#include <stdio.h>
#include <stdarg.h>

int add_two(int dummy, ...) {
    va_list args;
    va_start(args, dummy);
    int sum = va_arg(args, int) + va_arg(args, int);
    va_end(args);
    return sum;
}

int main(void) {
    printf("%d\n", add_two(0, 4, 6));
    return 0;
}

Определение количества аргументов

Макросы не могут определить, сколько аргументов было передано. Вы должны сообщить это самостоятельно.

Распространённый подход — передавать первым параметром количество, как в sum(3, a, b, c).

#include <stdio.h>
#include <stdarg.h>

int sum(int count, ...) {
    va_list args;
    va_start(args, count);
    int total = 0;
    for (int i = 0; i < count; i++)
        total += va_arg(args, int);
    va_end(args);
    return total;
}

int main(void) {
    printf("%d\n", sum(4, 1, 2, 3, 4));
    return 0;
}

Использование значения-маркера

Другой способ обозначить конец — использовать маркер: специальное завершающее значение, которое должен передать вызывающий код, например -1 или NULL.

Выполняйте цикл, пока не прочитаете маркер.

#include <stdio.h>
#include <stdarg.h>

int sum_until_neg(int first, ...) {
    va_list args;
    va_start(args, first);
    int total = first, v;
    while ((v = va_arg(args, int)) != -1)
        total += v;
    va_end(args);
    return total;
}

int main(void) {
    printf("%d\n", sum_until_neg(10, 20, 30, -1));
    return 0;
}

Продвижение аргументов по умолчанию

Аргументы переменного количества проходят продвижение по умолчанию: char и short преобразуются в int, а float — в double.

Поэтому всегда читайте малые целые числа как int, а числа с плавающей точкой — как double с помощью va_arg.

#include <stdio.h>
#include <stdarg.h>

void show_double(int n, ...) {
    va_list args;
    va_start(args, n);
    double d = va_arg(args, double);
    printf("%.2f\n", d);
    va_end(args);
}

int main(void) {
    show_double(1, 3.5f);
    return 0;
}

Копирование va_list

Если необходимо пройти по аргументам дважды, используйте va_copy(dest, src), чтобы скопировать список перед чтением.

После va_end нельзя повторно использовать va_list, не начав работу заново с помощью va_start.

#include <stdio.h>
#include <stdarg.h>

void twice(int n, ...) {
    va_list a, b;
    va_start(a, n);
    va_copy(b, a);
    printf("%d %d\n", va_arg(a, int), va_arg(b, int));
    va_end(a);
    va_end(b);
}

int main(void) {
    twice(1, 42);
    return 0;
}

Вычисление среднего значения

Объединив шаблон с количеством аргументов и чтение чисел с плавающей точкой, можно вычислить среднее значение списка чисел, переданных в переменном количестве.

#include <stdio.h>
#include <stdarg.h>

double average(int count, ...) {
    va_list args;
    va_start(args, count);
    double sum = 0;
    for (int i = 0; i < count; i++)
        sum += va_arg(args, double);
    va_end(args);
    return sum / count;
}

int main(void) {
    printf("%.2f\n", average(3, 2.0, 4.0, 6.0));
    return 0;
}

Быстрая проверка

Проверьте, насколько хорошо Вы поняли макросы stdarg.

Итоги

Вы изучили механизм работы с переменным количеством аргументов:

  • Объявляйте функцию с помощью ... после хотя бы одного именованного параметра.
  • va_start(list, last) начинает работу, va_arg(list, type) читает аргумент, а va_end(list) выполняет очистку.
  • Макросы не знают количество аргументов — используйте параметр с количеством или маркер.
  • Учитывайте продвижение по умолчанию: читайте малые целые числа как int, а числа с плавающей точкой — как double.

Часто задаваемые вопросы

Урок «Макросы stdarg» бесплатный?

Да — полный текст урока «Макросы stdarg» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C Academy, подпишись на CoddyKit PRO. Курс C Academy содержит 4 уроков всего.

Чему я научусь в уроке «Макросы stdarg»?

va_list, va_start, va_arg Ты практикуешь C Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C Academy?

Предыдущий опыт не требуется. C Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.

Сколько времени занимает урок «Макросы stdarg»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C Academy?

Да. Каждый урок C Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Макросы stdarg
  2. Написание функций, подобных printf
  3. Проблемы безопасности типов
  4. Практические примеры
← Назад к C Academy