Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Информатика_1 / C / lecture10 / lecture10

.html
Скачиваний:
11
Добавлен:
09.06.2015
Размер:
13.95 Кб
Скачать

Лекция 10. Функции с переменным числом аргументов. Аргументы функции main. Table of Contents 1. Функции с переменным числом аргументов. 2. Аргументы функции main. 3. Задачи.   Функции с переменным числом аргументов. Мы уже сталкивались в этих лекциях с использованием функций с переменным числом аргументов. Например, функциии printf и scanf могут принимать любое число аргументов большее , чем единица. Заголовки этих функций имеют вид int printf(const char *, ...); int scanf(const char *, ...); . Три точки в конце списка формальных параметров означают, эти функции при вызове принимают произвольное количество аргументов, передаваемых после первого обязательного аргумента, имеющего тип const char * (этот тип данных представляет строки в яхзыке Си). Для правильного написания фуекций с произвольным числом аргументов необходимр пояснить , как происходит передача фактических аргументов в функцию при ее вызове. Например, Вы используете в своей программе функцию с таким заголовком int rooti(double *x, double eps, double (*f)(double)); . При вызове этой функции , который может к примеру выглядеть так i=rooti(&z,eps,f); , в специальную область памяти, называемую стэком , помещаются передаваемые функции параметры. В данном случае в стэк будут помещены три значения - значение адреса вещественной переменной z , значение вещественной переменнной ерs и значение адреса функции одной вещественной переменной f. При описании функции rooti Вы не занимались извлечением параметров x, eps и f из стэка. Этот код компилятор вставляет автоматически при компиляции кода функции с фиксированным числом аргументов и Вы пользуетесь параметрами x , eps и f , как данными. Иначе обстоит дело при описании функций с произвольным количеством аргументов. В этом случае Вы должны сами вставить в тело функции код для извлечения переданных в функцию значений из стэка. Для этого используются функции type va_arg( va_list arg_ptr, type ); void va_end( va_list arg_ptr ); void va_start( va_list arg_ptr, prev_param ); . Заголовки этих функций объявлены в заголовочном файле stdarg.h. Тип данных va_list , используемый во всех этих функциях определен в заголовочном файле stdio.h с помощью объявления typedef typedef char * va_list; . Это объявление означает , что в переменных типа va_list хранятся адреса. Переменные этого типа используются для хранения адресов данных, размещенных в стэке. Извлечение данных из стэка начинается с вызова функции va_start, которая помещает в переменную arg_ptr адрес первого считываемого значения из стэка. Второй аргумент prev_param при вызове va_start - это имя переменной из списка формальных параметров , после которой в стэке располагаютя данные для считывания. Дело в том, что явно описанные параметры считываются из стэка автоматически и Ваша задача считать все остальные данные из стэка. Например , в фрагменте описания функции double prod(int i, ...){ va_list ptr; va_start(ptr,i); . . . }, в третьей строке кода va_start(ptr,i) в переменную ptr заносится адрес значения , помещенного в стэк после переменной i. Именно с этого адреса мы и начинаем считывание данных из стэка. Функция va_arg считывает из стэка значение типа type и возвращает считаннное значение с помощь оператора return. При этом указатель arg_ptr увеличивается на число считанных байтов, и указавает после этого на ячейку памяти с которой будет считываться следующее значение. Функция va_end завершает работу со стэком и обнуляет переменную arg_ptr. В качестве примера приведу пример функции , вычисляющей произведение произвольного числа данных типа int (файл l10_1.c). /*1*/ #include <stdio.h> /*2*/ #include <stdarg.h> /*3*/ int prod( int num, ... ); /*4*/ /*5*/ void main( void ) /*6*/ { /*7*/ /*8*/ printf( "Prod 6*7*2 is: %d\n", prod( 3, 6, 7, 2 ) ); /*9*/ /*10*/ /*11*/ printf( "Prod 3*4*5*1 is: %d\n", prod( 4, 3, 4, 5, 1 ) ); /*12*/ /*13*/ /*14*/ printf( "prod 0 is: %d\n", prod( 0 ) ); /*15*/ } /*16*/ /*17*/ /* Returns the prod of a variable list of integers. */ /*18*/ int prod( int num, ... ) /*19*/ { /*20*/ int count = 0, ans = 1, i; /*21*/ va_list ptr; /*22*/ if(num<1)return 0; /*23*/ va_start( ptr, num ); /* Initialize variable arguments. */ /*24*/ while( count< num ) /*25*/ { /*26*/ i = va_arg( ptr, int); /*27*/ ans *= i; /*28*/ count++; /*29*/ /*30*/ } /*31*/ va_end( ptr ); /* Reset variable arguments. */ /*32*/ return ans; /*33*/ } В строках 18-32 описана функция prod , предназначеннная для вычисления произведения произвольного числа сомножителей из данных тип int. Первым аргументом функция получает формальный параметр num - число сомножителей. В строке 23 вызывается функция va_start , которая в переменную ptr записывает адрес ячейки памяти в стэке, где хранится следующий за num аргумент, переданный в функцию prod при ее вызове. В цикле while в строках 24-30 из стэка считываются num значений типа int и вычисляется их произведение. Считывание очередного значения типа int осуществляется в строке 26 с помощью вызова функции va_arg(ptr,int); . При каждом считывании указатель ptr увеличивается на число считанных байтов. В строке 30 работа со стэком завершается и переменная ptr обнуляется. В строках 8-14 показан пример вызова функции prod с различным числом аргументов. Аргументы функции main. Передача параметров из командной строки. . До сих пор мы пользовались функцией main без аргументов. Однако в языке прграммирования Си предусмотрена возможность использовать функцию main и с аргументами. Можно описывать до трех аргументов в этой функции. Заголовок функции main с максимальным количеством аргументов обычно пишут в таком виде int main(int argc, char *argv[ ], char *envp[]). Эти аргументы имеют следующий смысл: argc - число аргументов, переданных при запуске программы с командной строки, argv - массив строк , содержащий строки переданные программе при ее запуске с командной строки. В первом элементе этого массива всегда хранится имя запущенной программы с указанием полного пути , во втором элементе массива хранится первый аргумент , переданный программе с командной строки и т.д. Всего в массиве argv содержится argc строк. Значение argc всегда больше или равно 1. envp - массив строк , содержащих переменные среды в формате "переменная=значение". Завершает этот массив всегда элемент массива со значением NULL. Вот пример программы, показывающей использование всех трех аргументов (файл l10_2.c) /*1*/ #include<stdio.h> /*2*/ void main(int argc,char *argv[],char *envp[]){ /*3*/ int i; /*4*/ printf("argc=%d\n",argc); /*5*/ for(i=0;i<argc;i++)printf("argv[%d]=%s\n",i,argv[i]); /*6*/ i=0; /*7*/ while(envp[i]!=NULL)printf("envp[%d]=%s\n",i,envp[i++]); /*8*/ } В строке 4 на экран выводится количество аргументов переданных программе при запуске с командной строки. Например, если из этого текста была приготовлена прграмма main_args.exe и вы запустили ее командной строкой main_args.exe arg1 arg2, то переменная argc примет значение 3 ( три аргумента были введены с командной строки). В строке 5 в цикле for на экран выводятся все argc строк массива argv. В строке 7 в цикле while выводятся все строки массива envp. Вот как выглядит результат результат работы этой программы на экране argc=3 argv[0]=E:\bvv\lectures\C\main_args\Debug\main_args.exe argv[1]=arg1 argv[2]=arg2 envp[0]=ALLUSERSPROFILE=C:\Documents and Settings\All Users envp[1]=APPDATA=C:\Documents and Settings\bvv\Application Data envp[2]=Basemake=C:\Program Files\Microsoft Platform SDK\Include\BKOffice.Mak envp[3]=Bkoffice=C:\Program Files\Microsoft Platform SDK\ envp[4]=CommonProgramFiles=C:\Program Files\Common Files envp[5]=COMPUTERNAME=HBVV envp[6]=ComSpec=C:\WINDOWS\system32\cmd.exe envp[7]=CORPATH=C:\WINDOWS\Microsoft.NET\Framework\v1.0.2204\ envp[8]=DXSDKROOT=C:\Program Files\Microsoft Platform SDK\ envp[9]=HOMEDRIVE=C: envp[10]=HOMEPATH=\Documents and Settings\bvv envp[11]=INCLUDE=C:\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\include\;C:\Program Files\Microsoft Platform SDK\Include\;C:\Program Files\Microsoft Visual Studio\VC98\atl\include;C:\Program Files\Microsoft Visual Studio\VC98\mfc\include;C:\Program Files\Microsoft Visual Studio\VC98\include envp[12]=INETSDK=C:\Program Files\Microsoft Platform SDK\ envp[13]=LIB=C:\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Lib\;C:\Program Files\Microsoft Platform SDK\Lib\;C:\Program Files\Microsoft Visual Studio\VC98\mfc\lib;C:\Program Files\Microsoft Visual Studio\VC98\lib envp[14]=LOGONSERVER=\\HBVV envp[15]=MSDevDir=C:\Program Files\Microsoft Visual Studio\Common\MSDev98 envp[16]=MSSdk=C:\Program Files\Microsoft Platform SDK\ envp[17]=Mstools=C:\Program Files\Microsoft Platform SDK\ envp[18]=NUMBER_OF_PROCESSORS=1 envp[19]=OS=Windows_NT envp[20]=Path=C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;c:\matlabr12\bin\win32;c:\texmf\miktex\bin;c:\matlab6p5\bin\win32;C:\Program Files\Microsoft Visual Studio\Common\Tools\WinNT;C:\Program Files\Microsoft Visual Studio\Common\MSDev98\Bin;C:\Program Files\Microsoft Visual Studio\Common\Tools;C:\Program Files\Microsoft Visual Studio\VC98\bin;c:\gs;C:\Program Files\Microsoft Platform SDK\Bin\;C:\Program Files\Microsoft Platform SDK\Bin\WinNT;C:\Program Files\Far envp[21]=PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH envp[22]=PROCESSOR_ARCHITECTURE=x86 envp[23]=PROCESSOR_IDENTIFIER=x86 Family 6 Model 3 Stepping 0, AuthenticAMD envp[24]=PROCESSOR_LEVEL=6 envp[25]=PROCESSOR_REVISION=0300 envp[26]=ProgramFiles=C:\Program Files envp[27]=PROMPT=$P$G envp[28]=SESSIONNAME=Console envp[29]=SystemDrive=C: envp[30]=SystemRoot=C:\WINDOWS envp[31]=TEMP=C:\DOCUME~1\bvv\LOCALS~1\Temp envp[32]=TMP=C:\DOCUME~1\bvv\LOCALS~1\Temp envp[33]=USERDOMAIN=HBVV envp[34]=USERNAME=bvv envp[35]=USERPROFILE=C:\Documents and Settings\bvv envp[36]=VSCOMNTOOLS="C:\Program Files\Microsoft Visual Studio .NET\Common7\Tools\" envp[37]=windir=C:\WINDOWS Обратите внимание на строку с определением переменной среды INCLUDE ( 12- ый элемент массива envp - envp[11]). Именно эта переменная используется компиляторами для поиска заголовочных файлов, включаемых в прорамму директивой include с именем файла в угловых (<>) скобках. В 21 элементе массива envp (envp[20]) можно видеть значение переменной окружения PATH, используемой для поиска выполняемых файлов и dll библиотек. А вот другой ,более полезный пример использования аргументов функции main. Предположим, что нам нужна прграмма , которая производит перекодировку текстовых файлов из формата DOS- Windows ( где признаком новой строки являются два байта CR и LF) в формат UNIX, где признаком новой строки служит один байт LF. Назовем нашу прграмму dos2unix.exe. Мы хотим имена перекодируемого и результирующего файлов передавать программе в входных аргументах при запуске. То есть для перекодировки файла dos.txt и записи результата в файл unix.txt мы должны набрать команду dos2unix.exe dos.txt unix.txt . Вот пример такой программы (файл l10_3.c ) /*1*/ #include <stdio.h> /*2*/ #include <conio.h> /*3*/ int exit(int); /*4*/ void main(int argc,char *argv[]) { /*5*/ FILE *ptr1,*ptr2; /*6*/ char c,enter=13; /*7*/ printf("DOS to Unix transformation of text files\n Copyright BVV 1996\n"); /*8*/ if(argc!=3){printf("Error syntax:\n Syntax: unix2dos infile outfile\n"); /*9*/ exit(0); /*10*/ } /*11*/ ptr1=fopen(argv[1],"rb"); /*12*/ ptr2=fopen(argv[2],"wb"); /*13*/ while(!feof(ptr1)){ /*14*/ fread(&c,1,1,ptr1); /*15*/ switch(c) { /*16*/ case 13: break; /*17*/ default: fwrite(&c,1,1,ptr2); /*18*/ } /*19*/ } /*20*/ fclose(ptr2); /*21*/ printf("Ok\n"); /*22*/ } Задачи. Задача 1. Написать функцию с произвольным числом аргументов для вычисления среднего арифметического. Задача 2. Написать программу , которая перекодирует текстовые файлы из колировки UNIX ( где признаком новой строки является байт со значением 10) в кодировку DOS-WINDOWS ( где признаком новой строки являются два байта со значениями 13 и 10). Имя входного и выходного файлов должно вводится с командной строки. Задача 3. Написать функцию, которая вычисляет интеграл произвольной кратности методом Монте-Карло. Написать вызывающую программу. previous next home

Соседние файлы в папке lecture10