Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Электонный конспект.doc
Скачиваний:
3
Добавлен:
13.11.2019
Размер:
746.5 Кб
Скачать

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

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

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

Примерами функций с переменным числом параметров являются функции из библиотеки функций языка СИ, осуществляющие операции ввода-вывода информации (printf,scanf и т.п.).

Программист может разрабатывать свои функции с переменным числом параметров. Для обеспечения удобного способа доступа к аргументам функции с переменным числом параметров имеются три макроопределения (макросы) va_start, va_arg, va_end, находящиеся в заголовочном файле stdarg.h. Эти макросы указывают на то, что функция, разработанная пользователем, имеет некоторое число обязательных аргументов, за которыми следует переменное число необязательных аргументов. Обязательные аргументы доступны через свои имена как при вызове обычной функции. Для извлечения необязательных аргументов используются макросы va_start, va_arg, va_end в следующем порядке.

Макрос va_start предназначен для установки аргумента arg_ptr на начало списка необязательных параметров и имеет вид функции с двумя параметрами:

void va_start(arg_ptr,prav_param);

Параметр prav_param должен быть последним обязательным параметром вызываемой функции, а указатель arg_prt должен быть объявлен с предопределением в списке переменных типа va_list в виде:

va_list arg_ptr;

Макрос va_start должен быть использован до первого использования макроса va_arg.

Макрокоманда va_arg обеспечивает доступ к текущему параметру вызываемой функции и тоже имеет вид функции с двумя параметрами

type_arg va_arg(arg_ptr,type);

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

Макрос va_end используется по окончании обработки всех параметров функции и устанавливает указатель списка необязательных параметров на ноль (NULL).

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

Пример:

# include <stdio.h>

float sred_znach(int,...);

int main()

{ float n;

n=sred_znach(2,3,4,-1);

/* вызов с четырьмя параметрами */

printf("n=%f\n",n);

n=sred_znach(5,6,7,8,9,-1);

/* вызов с шестью параметрами */

printf("n=%f\n",n);

return (0);

}

float sred_znach(int x,...)

{

int j=0;

float i=0,sum=0;

va_list uk_arg;

va_start(uk_arg,x); /* установка указателя uk_arg на */

/* первый необязятельный параметр */

if (x!=-1) sum=x; /* проверка на пустоту списка */

else return (0);

j++;

while ( (i=va_arg(uk_arg,float))!=-1)

/* выборка очередного */

{ /* параметра и проверка */

sum+=i; /* на конец списка */

j++;

}

va_end(uk_arg); /* закрытие списка параметров */

return (sum/j);

}

Передача параметров функции main

Функция main, с которой начинается выполнение СИ-программы, может быть определена с параметрами, которые передаются из внешнего окружения, например, из командной строки. Во внешнем окружении действуют свои правила представления данных, а точнее, все данные представляются в виде строк символов. Для передачи этих строк в функцию main используются два параметра, первый параметр служит для передачи числа передаваемых строк, второй для передачи самих строк. Общепринятые (но не обязательные) имена этих параметров argc и argv. Параметр argc имеет тип int, его значение формируется из анализа командной строки и равно количеству слов в командной строке, включая и имя вызываемой программы (под словом понимается любой текст не содержащий символа пробел). Параметр argv это массив указателей на строки, каждая из которых содержит одно слово из командной строки. Если слово должно содержать символ пробел, то при записи его в командную строку оно должно быть заключено в кавычки.

Функция main может иметь и третий параметр, который принято называть argp, и который служит для передачи в функцию main параметров операционной системы (среды) в которой выполняется СИ-программа.

Заголовок функции main имеет вид:

int main (int argc, char *argv[], char *argp[])

Если, например, командная строка СИ-программы имеет вид:

A:\>cprog working 'C program' 1

то аргументы argc, argv, argp представляются в памяти как показано в схеме на рис.1.

argc [ 4 ]

argv [ ]--> [ ]--> [A:\cprog.exe\0]

[ ]--> [working\0]

[ ]--> [C program\0]

[ ]--> [1\0]

[NULL]

Рис.1. Схема размещения параметров командной строки

Операционная система поддерживает передачу значений для параметров argc, argv, argp, а на пользователе лежит ответственность за передачу и использование фактических аргументов функции main.

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

Пример:

# include <stdio.h>

#include <windows.h>

char bufRus[256];

char* Rus(const char* text){

CharToOem(text, bufRus);

return bufRus;

}

int main ( int argc, char *argv[], char *argp[])

{ int i=0;

printf (Rus("\n Имя программы %s"), argv[0]);

for (i=1; i<=argc; i++)

printf (Rus("\n аргумент %d равен %s"),i, argv[i]);

printf (Rus("\n Параметры операционной системы:"));

while (*argp)

{ printf ("\n %s",*argp);

argp++; }

return (0); }

Тема 7. Язык программирования С/С++. Директивы препроцессора. Способы конструирования программ

Директивы: #define, #include, директивы условной компиляции: #if, #else, #elif и #endif, #ifdef и #ifndef. Директива #undef. Предопределенные макросы.

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

Препроцессор и его директивы. Компилятор языка С, прежде чем компилировать программу, выполняет определенные операции. Одной из таких операций является включение в программу других файлов (#include). Но это не все. Используя возможности прекомпиляции, можно создавать программы, компилируемые по разному в зависимости от текущих потребностей (например, строя или не строя отладочную информацию).

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

Директива #include. Эта директива предназначена для включения в исходный текст программы исходного кода из указанных файлов, например, заголовочных файлов. В коде программы строка, содержащая директиву заменяется содержимым файла (например, одна строка - 8 страниц).

Допустимо вложение директив, т.е. в файле, на который указывает директива #include могут также использоваться директивы #include на другие файлы. Однако глубина такого вложения ограничена. Обычно допускается до 10 уровней вложения.

Существенный момент в использовании директивы #include состоит в указании имени файла:

#include <stdio.h>

#include "myhead.h"

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

Директива #define. Эта директива предназначена для решения двух задач: сокращения длины кода и реализация макроопределений функций.

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

...grad * (grad -1) * (grad -2)

Вместо того, чтобы это выражение записывать каждый раз можно определить замену

#define MGRAD grad * (grad -1) * (grad -2)

и в программе везде, где встретится выражение, использовать его обозначение MGRAD.

Следует отметить, что макроопределение нельзя использовать внутри строк, заключенных в кавычки.

Вторая задача - реализация макроопределений функций, обеспечивается следующей конструкцией:

# define <имя макроса> (<параметры>) <выражение>

# define Tenth (x) (x)/10

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

Следует иметь в виду, что при описании тела макроса полезно обычно передаваемые ему аргументы заключать в скобки. Это делается с целью защиты от нарушений порядка исполнения операций. Например, если бы так не было сделано при определении макроса Tenth(), то его вызов с параметром-выражением давал бы ошибочный результат в случае

Tenth (arg1 + arg2) → arg1 + arg2/10 - ошибка!

При использовании функциональных макроопределений символ # используется для вывода параметра в виде строки - при компиляции параметр заключается в двойные кавычки

Предопределенные макросы

В C++ определено несколько макросов, предназначенных в основном для того, чтобы выдавать информацию о версии программы или месте возникновения ошибки.

_cplusplus — определен, если программа компилируется как файл C++. Многие компиляторы при обработке файла с расширением с считают, что программа написана на языке С. Использование этого макроса позволяет указать, что можно использовать возможности C++:

#ifdef _cpl uspl us

// Действия, специфические для C++

#endif

Применяется, если требуется переносить код из С в C++ и обратно.

__DATE__ — содержит строку с текущей датой в формате месяц день год,

__FILE__ — содержит строку с полным именем текущего файла.

__LINE__ — текущая строка исходного текста.

__TIME__ — текущее время, например:

printf(Rus(" Дата компиляции - %s \n"), __DATE__);

printf(Rus(" Ошибка в файле %s \n Время компиляции %s\n Строка %d\n"), __FILE__, __TIME__,__LINE__);

Тема 8. Язык программирования С/С++. Файлы и потоки