C установка IDE C структура програми C змінні C типи даних С функція printf C константи C арифметичні операції C операції порівняння і логічні операції C порозрядні операції C операції присвоювання C перетворення типів C умовні конструкції C цикли С масиви і рядки С функція scanf C препроцесор. Директива #include C #define директива C макроси C умовна компіляція C функції. Визначення та опис функцій C функції. Передача параметрів в функцію C функції. Повернення результату з функції C функції. Рекурсивні функції C область видимості змінних C зовнішні об'єкти C вказівники C вказівник. Операції з вказівниками C покажчики. Арифметика покажчиків C покажчики. Константи і покажчики C покажчики. Покажчики та масиви C покажчики. Масиви покажчиків, рядки і багаторівнева адресація C покажчики. Покажчики в параметрах функції C покажчики. Динамічна пам'ять C покажчики. Покажчик як результат функції C покажчики. Управління динамічної пам'яттю C покажчики. Покажчики на функцію C покажчики. Покажчики на функції як параметри і результати функцій C покажчики. Функції зі змінною кількістю параметрів C struct. Визначення структур C struct. Структури як елементи структур C struct. Покажчики на структури C struct. Масиви структур C struct. Структури і функції C struct. union об'єднання C struct. Бітові поля С file. Введення-виведення і робота з файлами C file. Читання і запис бінарних файлів C file. Читання і запис структур в файл C file. Читання і запис текстових файлів C file. Форматування вводу-виводу C file. Позиціонування в потоці C file. Консольне введення-виведення

C покажчики. Функції зі змінною кількістю параметрів


Мова програмування Сі допускає використання функцій, які мають нефиксированное кількість параметрів. Більш того може бути невідомим не тільки кількість, але і типи параметрів. Тобто точне визначення параметрів стає відомим тільки під час виклику функції.

Для визначення параметрів невизначеної довжини в таких функціях використовується три крапки:

тип імя_функції(обов'язкові параметри, ...)

При цьому треба враховувати, що функція повинна мати як мінімум один обов'язковий параметр.

Наприклад, визначимо функцію, яка обчислює суму чисел, кількість чисел нефіксованим:

#include <stdio.h>
 
int sum(int n, ...)
{
    int result = 0;
    // получаем указатель на параметр n
    for(int *ptr = &n;n>0; n--)
    {
        result+= *(++ptr);
    }
    return result;
}
 
int main(void)
{   
    printf("%d \n", sum(4, 1, 2, 3, 4));
    printf("%d \n", sum(5, 12, 21, 13, 4, 5));
    return 0;
}

При роботі з параметрами слід враховувати, що для мови Сі, як правило, менше значення адреси у першого параметра, а решта празмещаются далі поспіль. Тому ми можемо отримати адресу першого параметра і покажчиком пробігтися по адресам, які йдуть після адреси першого параметра.

Перший і обов'язковий параметр функції sum - n - вказує на кількість необов'язкових параметрів. У циклі встановлюємо покажчик ptr на адресу параметра n і послідовно переміщує його. За допомогою операції разименованія *(++ptr) після переміщення покажчика на один елемент вперед отримуємо значення і виконуємо додавання зі змінною result.

У той же час не можна не відзначити недастаток даного рішення: всі параметри представляють один і той же тип. Крім того, ми можемо при наборі коду помилитися зі значенням першого параметра, тоді результати функції будуть непередбачуваними.

І для спрощення роботи з перемінним кількістю параметрів невизначених типів в мові Сі в стандартом заголовки stdarg.h визначені спеціальні макроси:

va_start();
va_arg();
va_end();

Всі ці макроси використовують спеціальний тип даних va_list , який також визначено в stdarg.h і який дозволяє обробляти списки параметрів з нефіксованим кількістю.

Макрос va_start має наступне визначення:

void va_start(va_list param, последний_явный_параметр);

Перший параметр макросу - param пов'язує об'єкт va_list з першим необов'язковим параметром. Для його визначення в якості другого параметра в макрос передається останній обов'язковий параметр функції. Таким чином, використовуючи останній обов'язковий параметр, ми можемо націлити об'єкт va_list на адресу першого необов'язкового параметра. Тобто фактично va_list виступає в цій ролі як покажчик.

Макрос va_arg має наступне визначення:

type va_arg(va_list param, type);

Цей макрос дозволяє отримати значення параметра типу type , а також перемістити покажчик va_list на наступний необов'язковий параметр.

Макрос дозволяє вийти з функції зі змінним списком параметрів. Вона має таке визначення:

void va_end(va_list param);

Як параметр вона приймає покажчик va_start, який раніше був задіяний в макросах va_start і va_arg.

Перепишемо попередній приклад з використанням цих макрокоманд:

#include <stdio.h>
#include <stdarg.h>
 
int sum(int n, ...)
{
    int result = 0;
    va_list factor;         //указатель va_list
    va_start(factor, n);    // устанавливаем указатель
    for(int i=0;i<n; i++)
    {
        result += va_arg(factor, int);  // получаем значение текущего параметра типа int
    }
    va_end(factor); // завершаем обработку параметров
    return result;
}
 
int main(void)
{   
    printf("%d \n", sum(4, 1, 2, 3, 4));
    printf("%d \n", sum(5, 12, 21, 13, 4, 5));
    return 0;
}

У функції sum() спочатку визначається покажчик va_list factor; .

Далі пов'язуємо цей покажчик з першим необов'язковим параметром: va_start(factor, n); .

У циклі пробігає по всьому необов'язковим параметрам і їх значення додаємо до змінної result: result += va_arg(factor, int);

В кінці завершуємо обробку параметрів: va_end(factor); .

Результат цієї програми буде той же, що і в попередньому випадку. Але тут знову ж таки нам треба передавати кількість необов'язкових параметрів в якості першого параметра функції sum. І, крім того, ми точно знаємо, що необов'язкові параметри мають тип int.

Але варто відзначити, що використовувані нами функції введення-виведення printf () і scanf () то ж мають невизначений число параметрів, але їх типи також невизначені:

int printf(const char* format, ...);
int scanf(const char* format, ...);

Для ідентифікації типів аргументів параметр format використовує специфікатор% d,% c і так далі. Наприклад, визначимо собстенних функцію, яка буде виводить текст на екран, приймаючи параметри різних типів:

#include <stdio.h>
#include <stdarg.h>
 
void display(char* format, ...)
{
    int d; 
    double f;
    va_list factor;         // указатель на необязательный параметр
    va_start(factor, format);   // устанавливаем указатель
     
    for(char *c = format;*c; c++)
    {
        if(*c!='%')
        {
            printf("%c", *c);
            continue;
        }
        switch(*++c)
        {
            case 'd': 
                d = va_arg(factor, int);
                printf("%d", d);
                break;
            case 'f': 
                f = va_arg(factor, double);
                printf("%.2lf", f);
                break;
            default:
                printf("%c", *c);
        }
    }
    va_end(factor);
}
 
int main(void)
{   
    display("Age:%d \t Weight:%f", 24, 68.4);
    return 0;
}

Для спрощення прикладу тут взяті тільки два специфікатор: d (для типу int) і f (для типу double). У самій функції display за допомогою покажчика char *c пробігає по всіх символах переданого рядка format, поки цей покажчик не стане вказувати на нульовий символ ( *c!='\0' ). Якщо символ НЕ дорівнює знаку%, то виводимо цей символ. Інакше дивимося, який символ йде після знака% - d або f. Залежно від цього отримуємо або об'єкт int, або об'єкт double.


Наш партнер:
beta test mp3 playlist downloader