22332

Функции в языке Си. Формальные и фактические параметры. Механизм передачи параметров. Возвращаемые значения. Использование указателей в качестве аргументов функций

Лекция

Информатика, кибернетика и программирование

ФУНКЦИИ В ЯЗЫКЕ СИ. Каждая из них в свою очередь есть независимый набор описаний и операторов заключенных между заголовком функции и ее концом. Все объекты определенные в теле функции ограниченном открывающей и закрывающей фигурными скобками являются локальными для этой функции в смысле области видимости и времени существования.

Русский

2013-08-04

44.5 KB

13 чел.

ЛЕКЦИЯ 5

Функции в языке Си. Формальные и фактические параметры. Механизм передачи параметров. Возвращаемые значения. Использование указателей в качестве аргументов функций. Предварительное описание    функций. Аргументы командной строки.

$ 1. ФУНКЦИИ В ЯЗЫКЕ СИ. ФОРМАЛЬНЫЕ И ФАКТИЧЕСКИЕ ПАРАМЕТРЫ.

МЕХАНИЗМ ПЕРЕДАЧИ ПАРАМЕТРОВ. ВОЗВРАЩАЕМЫЕ ЗНАЧЕНИЯ

Всякая  программа  на  языке Си представляет собой совокупность   функций, выполняющих основную работу по реализации некоторого  алгоритма.  Каждая из них, в свою очередь, есть независимый набор описаний и операторов, заключенных между заголовком функции и ее  концом.  Все объекты, определенные в теле функции, ограниченном открывающей и  закрывающей фигурными скобками, являются локальными для этой функции  в  смысле области видимости и времени существования. Детали этих понятий будут разобраны в Лекции 7.   В составе общей программы любая функция идентифицируется  своим   собственным  уникальным  именем, которым может быть любое правильное   имя в смысле грамматики языка Си (см. Лекцию 1, $ 3). Та функция,  с   которой начинается выполнение программы, называется главной функцией     и  должна  иметь  предопределенное  имя main. Все остальные функции,   входящие в программу, запускаются в  работу  путем  их  прямого  или  опосредованного (через другие функции) вызова из главной функции.   Функции  играют чрезвычайно важную роль при подготовке Си-программы. Действительно, используя функции, исходную задачу можно представить в виде последовательности более простых задач, каждая из

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

перепрограммировать  всякий  раз  наиболее ходовые методы, алгоритмы и  операции.          Для  организации  связи между независимыми функциями в языке Си   используется либо аппарат  формальных/фактических  параметров,  либо   набор  глобальных или внешних переменных. Формальными параметрами мы   будем называть аргументы функции, стоящие в ее заголовке и имена которых используются для построения тела функции при  ее  определении.  Они могут иметь любой простой или структурированный тип, поддерживаемый  имеющимся  компилятором языка или определенный в программе при     помощи инструкции typedef (см. Лекцию 8, $ 8). Фактическими же параметрами являются произвольные выражения, значения которых передаются  формальным параметрам при обращении к функции. Таким образом  реализуется  возможность  передачи  необходимой  информации от вызывающей  функции к вызываемой непосредственно в момент ее вызова. В свою очередь, имя вызываемой функции может служить носителем выходного  значения,  получаемого в результате работы этой функции, делая его доступным вызвавшей функции. Случай, когда результатом  работы  функции   является  совокупность  значений,  будет  рассмотрен в $ 2 настоящей  лекции. Обсуждение же вопроса об использовании  глобальных  объектов   мы пока отложим до Лекции 7.          Упоминание о том, что имя функции может быть  использовано  для  передачи  выработанного этой функцией значения, подсказывает необходимость связать с этим именем конкретный тип данных из числа поддерживаемых компилятором языка Си. Эта связь устанавливается при  определении самой функции или при составлении ее предварительного описания (см. $ 3 настоящей лекции). Определяя функцию  в  соответствующей  программной  компоненте,  нужно,  прежде  всего,  указать ее имя и тип возвращаемого значения,  задать список формальных параметров и определить тип каждого из них.  Такую совокупность описаний  принято  называть  заголовком  функции.     Вслед  за  ним должно размещаться тело функции, представляющее собой   правильный блок, т. е. набор описаний и  операторов,  заключенных  в   фигурные скобки. В языке Си определение функции имеет следующий формат:

       <sc-specifier> <type-specifier> declarator (<parameter-list>)

       <parameter-declarations>

       function-body

Здесь sc-specifier есть описатель класса памяти (static или extern),  подробно  рассматриваемый  в  Лекции  7. type-specifier и declarator  вместе определяют тип возвращаемого функцией значения и ее имя, причем отсутствие первого из них равносильно типу  int.  В  приведенной  схеме,  declarator  чаще всего является просто идентификатором функции, перед которым может стоять символ звездочка (*), если эта функция возвращает указатель на элемент  данных  соответствующего  типа.  Однако в ряде случаев он может иметь и более сложный формат. Никакая  функция  не должна возвращать массивов или других функций, но допустима передача указателей на эти об'екты. В тех случаях, когда  функция не вырабатывает никакого значения или возвращает указатель неопределенного  типа (см. Лекцию 4, $ 1), ее описание должно начинаться  с ключевого слова void, стоящего на месте имени  типа  возвращаемого     значения. Например,

                           void  work(n, beta)

                           int    n;

                           float  beta;

                           { .................

                             .................

                             .................

                           }

Parameter-list  представляет  собой  необязательный список формальных параметров (аргументов)  определяемой  функции,  разделенных  запятыми:

                     (<identifier<, identifier> ... >)

где  identifier  суть имя формального параметра. Описания формальных   параметров parameter-declarations в заголовке функции имеют  тот  же   самый  формат, что и рассмотренные ранее описания простых и структурированных переменных, и служат для определения типа  этих  параметров.  Те параметры, имена которых не объявлены явным образом в одной   из инструкций описания, по умолчанию получают тип  int,  назначаемый   компилятором. Память под размещение формальных параметров выделяется   динамически из ресурса программного стека в момент обращения к

соответствующей функции. Это означает, что параметры всегда должны иметь   класс памяти auto или register (см. Лекцию 7, $ 2), причем первый из   них назначается компилятором по умолчанию.  Тело функции, являясь правильным блоком или составным  оператором  (при  отсутствии в нем описаний переменных), выполняет основную    работу внутри этой функции. Те переменные, которые описаны  в  заголовке  блока  (т. е. в теле функции) будут локальными для этой функции, ибо область их видимости ограничена соответствующим блоком. Они   создаются в момент обращения к данной функции и исчезают по  окончании  ее работы (класс памяти auto), а их имена не должны совпадать с     именами формальных параметров.   Поскольку память под размещение локальных переменных выделяется   исполняющей  системой  динамически  из программного стека, последний    должен иметь достаточную для этого  длину.  Инициализация  локальных     объектов допустима лишь в случае простых переменных и невозможна для   массивов  и  других  структурированных данных. Подробный разговор об   этом пойдет в Лекции 7.          Выполнение инструкций в теле функции начинается с самого первого  оператора и продолжается до тех пор, пока не встретится оператор   возврата return, либо пока не будет достигнут конец внешнего  блока.   Возвращаемое  функцией значение равно значению выражения в операторе   return (см. Лекцию 3, $ 4), а при его отсутствии считается неопределенным. В случае необходимости тип результата преобразуется  к  типу

    функции стандартным образом. Инструкция  вызова  функции в общем случае имеет следующий формат:

                      expression (<expression-list>)

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

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

формальных параметров в теле функции не могут отразиться  на  значениях     фактических  параметров.  Это в свою очередь означает, что аргументы   функции являются носителями лишь входной информации и не могут  быть   использованы  для  передачи результатов ее работы вызвавшей функции.  Для преодоления этого ограничения необходимо использовать  указатели  в качестве аргументов функций, передавая тем самым числовые значения   соответствующих адресов. Таким же образом решается проблема передачи   массивов, функций и некоторых других структурированных данных.    Для иллюстрации изложенного материала приведем пример  программы,  вычисляющей квадратный корень из числа, введенного с клавиатуры  консольного терминала. Здесь основная работа по нахождению корня выполняется функцией sqrt(), реализующей итерационный метод Ньютона  с  фиксированным числом итераций.

      #include  <stdio.h>

      main()

      { float  dat;

        float  sqrt(float);     /*  Описание функции (см. $ 3)  */

        printf("\nЗадайте положительное вещественное число ... ");

        scanf("%f", &dat);

        printf("\n\nКорень из числа %.3f равен %.3f", dat, sqrt(dat));

      }

      float  sqrt(arg)

      float  arg;

      { int    count;

        float  root = arg/2.0;

        for (count = 1; count <= 5; count++)

           root = 0.5*(root + arg/root);

        return (root);

      }

Если  теперь в ответ на запрос программы ввести, например, число 25,   то по окончании ее работы будет напечатан следующий результат:

                    Корень из числа 25.000 равен 5.000

Заметим, что значение переменной dat в главной функции ни при  каких     обстоятельствах  не может быть изменено при выполнении итерационного   алгоритма в теле функции sqrt(), поскольку вся работа здесь  ведется    с копией значения, переданного через параметр arg.  Использованное в    этом примере предварительное описание функции sqrt() в теле  функции    main() будет рассмотрено в $ 3 настоящей лекции.

$ 2. ИСПОЛЬЗОВАНИЕ УКАЗАТЕЛЕЙ В КАЧЕСТВЕ АРГУМЕНТОВ ФУНКЦИЙ

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

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

существу  реализует  механизм доступа к переменным, определенным вне тела   функции, по адресной ссылке, поскольку сами указатели являются носителями адресов этих переменных. Получив же необходимый адрес,  функция, в свою очередь, может использовать его для доступа к соответствующему значению.  Рассмотрение вопроса об использовании указателей в качестве аргументов  функций  мы  начнем  с простейшего случая передачи адресов  скалярных переменных. Пусть, например, необходимо иметь функцию, заменяющую значение своего аргумента на его абсолютную величину. Чтобы     вызывающая функция могла получить результат такой замены, она должна     предоставить нашей функции адрес соответствующей переменной,  т.  е.   указатель  на  ее размещение в памяти ЭВМ. Решение этой задачи может   выглядеть следующим образом:

                 void  abs(arg)

                 int  *arg;   /*  Формальный параметр  */

                 { *arg = (*arg >= 0) ? (*arg) : -(*arg);

                   return;

                 }

Теперь в случае обращения

                               abs(&value);

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

                           scanf("%d", &alpha);

делает переменную alpha доступной в теле этой функции непосредственно через ее адрес, в результате чего введенное числовое значение будет известно и в точке вызова.          Наиболее важную роль при разработке программ на языке Си играет   возможность  передачи между отдельными функциями массивов переменных   и, в частности, символьных строк. Здесь вновь  оказывается  полезным   аппарат  указателей,  ибо  для  обеспечения доступа к массиву в теле   всякой функции ей достаточно передать адрес его  нулевого  элемента,   причем носителем последнего является само имя этого массива. В  следующем  примере функция summa(), выполняющая суммирование   элементов числового  массива,  получает  от  вызывающей  ее  функции   main() адрес начала массива vector и общее количество его элементов:

      main()

      { float  s;

        float  vector[100];     /*  Определение локального массива  */

        float  summa();         /*  Описание функции (см. $ 3)      */

        .......................

        .......................

        .......................

        s = summa(vector, 100);

        .......................

        .......................

        .......................

      }

      float  summa(mas, n)

      float  mas[];             /*  Описание формального массива    */

      int    n;

      { int    i;

        float  sum = 0;

        for (i = 0; i < n; i++)

           sum += mas[i];

        return (sum);

      }

Обратим внимание на то, что при описании формального массива  mas  в   заголовке  функции  summa() его размерность явно не указана. В этом,   однако, и нет необходимости, поскольку фактическая работа будет  выполняться  здесь  над массивом vector, адрес начала и длина которого   передаются при обращении к этой функции. Более того, вместо описания   массива неопределенной длины достаточно иметь эквивалентное ему описание

                               float  *mas;

определяющее указатель на начало обрабатываемого массива.  Использованное в этом примере предварительное описание функции summa() в теле функции main() будет рассмотрено в $ 3 настоящей лекции.  Примером использования параметров для передачи символьных строк   могут служить функции strcmp(), strcpy(), strlen() и другие,

введенные в Лекции 3. Аргументами каждой из них являются массивы элементов  типа char, идентифицируемые своими начальными адресами. В этом  случае  уже не нужно дополнительно передавать количество обрабатываемых  символов, ибо конец всякой строки легко находится по завершающему ее   нуль- символу.  Рассмотрим наконец возможность использования указателей для передачи одних функций в другие, определив предварительно понятие указателя на функцию. Последнее, очевидно, должно связываться с адресом   первого исполняемого оператора соответствующей функции, задающем  ее   входную точку. Указатель на функцию определяется в программе подобно     тому, как и сами функции, с той лишь разницей, что в этом случае заголовок выглядит следующим образом:

      <sc-specifier> <type-specifier> (*identifier) (<parameter-list>)

      <parameter-declarations>

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

                      float  (*calc)(alpha, beta)

                      float  alpha, beta;

                      { .........................

                        .........................

                        .........................

                      }

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

                             (*calc)(x, y);

где переменные x и y имеют тип float и играют роль фактических параметров.          Указатели  на  функции,  в  отличие  от самих функций, могут не  только выступать в роли самостоятельных единиц программы, но и  быть   формальными  параметрами  других  функций. Именно эта их особенность   позволяет осуществить передачу входных адресов функций  через  механизм  параметров. Для иллюстрации такой возможности рассмотрим фрагмент программы, вычисляющей проекцию отрезка длины len, составляющего угол alpha с осью обсцисс, на одно из двух координатных направлений в зависимости от значения ключа direct:

     double  len;                           /*  Длина отрезка         */

     double  alpha;                         /*  Угол с осью x         */

     main()

     { char    direct;                      /*  Ключ направления      */

       double  px, py;                      /*  Длины проекций        */

       double  cos(double), sin(double);    /*  Описания вызываемых   */

       double  proect(double (*) (double)); /*  функций (см. $ 3)     */

       .................................

       .................................

       switch (direct)

       { case 'x':

                   px = proect(cos);        /*  Проекция на ось x     */

                   break;

         case 'y':

                   py = proect(sin);        /*  Проекция на ось y     */

                   break;

       }

       .................................

       .................................

     }

     double  proect(func)

     double  (*func)(double);               /*  Указатель на функцию  */

     { return (len*(*func)(alpha)); }

В этом примере формальным параметром  функции proect является указатель func на функцию, возвращающую значение типа double. Фактическими же параметрами при ее вызове становятся  имена  функций  cos()  и   sin(), задающие соответствующие входные точки.

$ 3. ПРЕДВАРИТЕЛЬНОЕ ОПИСАНИЕ ФУНКЦИЙ

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

      <sc-specifier> <type-specifier> declarator (<arg-list>)

                                      <, declarator(<arg-list>) ... >;

Здесь sc-specifier задает класс памяти (static или extern),  который   имеет  вызываемая  функция (см. Лекцию 7, $ 2), type-specifier устанавливает тип возвращаемого ей значения, а arg-list определяет количество и тип аргументов. Declarator  в  приведенной  схеме  является  идентификатором  функции, возможно модифицированным при помощи

круглых скобок и символа звездочка для указателей на функции и  функций,     возвращающих указатели. Список аргументов arg-list обычно представляет собой последовательность разделенных запятыми  имен  типов  данных,  поддерживаемых     имеющимся компилятором языка или предварительно определенных при помощи  инструкции  typedef (см. Лекцию 8, $ 8). Однако он также может   содержать и обычные описания переменных вида float g.  Использование   ключевого  слова  void на месте

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

typespecifier может задавать любой  простой  тип данных, а также структуру, объединение или перечисление   (см. Лекцию 8). В тех случаях, когда функция не вырабатывает никакого значения или возвращает указатель неопределенного типа (см.  Лекцию  4, $ 1), ее предварительное описание должно начинаться с ключевого слова void, стоящего на месте имени типа  возвращаемого  значения. Введенное таким образом понятие предварительного описания функции  дает  возможность  компилятору  построить некоторый шаблон этой  функции до ее фактического определения в текущем или внешнем  файле.  Этот  шаблон  может  быть использован для контроля правильности типа    возвращаемого функцией значения и соответствия формальных  и  фактических  параметров.  Ниже  приведены  несколько характерных примеров    построения предварительных описаний.

         1. В  этом  примере описана функция с именем add,  возвращающая  значение типа double и оба аргумента которой являются указателями на  тип float:

                       double  add(float*, float*);

         2. Если функция с именем sum имеет два аргумента типа double  и   возвращает  указатель  на  массив  трех элементов типа double, то ее   предварительное описание должно иметь следующий вид:

                   double  (*sum(double, double))[3];

         3. В том случае, когда функция не имеет аргументов и возврашает     указатель неопределенного типа, в ее предварительном описании  необходимо  использовать ключевое слово void на месте имени типа возвращаемого значения и списка аргументов:

                            void  *draw(void);

$ 4. АРГУМЕНТЫ КОМАНДНОЙ СТРОКИ

Те, кому хоть раз приходилось  работать в операционной среде MS   DOS, видимо обратили внимание на то, что большинство команд  пользовательского  интерфейса могут иметь один или более параметров, называемых аргументами командной строки. Так, например, обращение к  команде  copy, выполняющей копирование файлов, обычно выглядит следующим образом:

                      copy  oldfile.txt  newfile.txt

где параметры oldfile.txt и newfile.txt определяют  имена  файла-источника  и файла-приемника соответственно. Эти параметры обрабатываются командным процессором и передаются в тело программы copy, в результате чего последняя узнает о файлах, над  которыми  должна  быть   выполнена  операция  копирования.  Можно с уверенностью сказать, что   именно наличие параметров делает всевозможные команды  удобными  для     практического использования.   Поскольку язык Си часто применяется при  разработке  системного   программного обеспечения, он имеет встроенные средства для получения     аргументов  команды  непосредственно от командного процессора. Такая   возможность, на первый взгляд кажущаяся несколько необычной, в действительности находится в полном соответствии  с  архитектурой  этого  языка.  В самом деле, любая функция, входящая в состав Си-программы,  может иметь параметры, через которые она получает необходимую информацию от вызывающей ее функции. Совершенно аналогично, главная функция main(), с которой начинается выполнение всякой программы,  могла   бы  в момент вызова получать исходные данные через аргументы командной строки. Для этого достаточно снабдить функцию main() набором параметров, которые обычно имеют имена argc и argv:

                            main(argc, argv)

Параметр argc (ARGument Count) является  переменной  типа  int,   получающей от командного процессора информацию о количестве аргументов, набранных в командной строке, включая и имя самой команды. Второй  параметр  argv (ARGument Vector) обычно определяется как массив   указателей типа char, каждый из которых хранит адрес начала  отдельного слова командной строки. Их описание в программе может выглядеть     следующим образом:

                             int   argc;

                             char  *argv[];

В  соответствии с принятым соглашением, нулевой элемент argv[0]   массива указателей ссылается на строку символов, содержащую имя  самой  команды  и  поэтому параметр argc всегда имеет значение большее   или равное единице. Следующий элемент argv[1] задает адрес  размещения в памяти первого аргумента команды, также представленного последовательностью  символов,  и  т.  д. Обращаясь к очередному элементу   массива argv нетрудно получить доступ ко всем  аргументам  командной   строки.  В  качестве примера, иллюстрирующего работу с параметрами функции main(), рассмотрим программу, выводящую на экран терминала  свои   собственные  аргументы и реализующую команду echo в составе оболочки   операционной системы MS DOS:

          #include  <stdio.h>

          main(argc, argv)

          int   argc;

          char  argv[];

          { int i;

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

               printf("%s%c", argv[i], (i < argc-1) ? ' ' : '\n');

          }

Поместив  теперь загрузочный модуль этой программы в файл echo.exe и   обратившись к ней при помощи команды

                       C:\> echo  first second third

получим на экране терминала такое сообщение

                            first second third

                            C:\>

представляющее собой простое эхо аргументов командной строки.  Поскольку массив указателей в определенном смысле  эквивалентен   "указателю на указатель" (см. Лекцию 4, $ 3), мы могли бы определить   переменную  argv  в заголовке функции main() как косвенный указатель   типа char:

                              char  **argv;

что полностью равносильно предыдущему описанию. В этих терминах наша   программа echo могла бы выглядеть, например, следующим образом:

             #include  <stdio.h>

             main(argc, argv)

             int   argc;

             char  **argv;

             { while (--argc > 0)

                  printf((argc > 1) ? "%s " : "%s\n", *++argv);

             }

где выражение ++argv увеличивает на единицу значение указателя, заставляя его ссылаться на очередную строку, полученную  от  командного   процессора.


 

А также другие работы, которые могут Вас заинтересовать

36435. Экологический туризм: предпосылки и особенности развития 29.5 KB
  Предпосылки зарождения экологического туризма Ведущая среди основных причин зарождения экологического туризма – это усиливающаяся изза массовости туризма нагрузка на природные и культурноисторические ресурсы. По мере роста глобализации мирового хозяйства росли и негативные изменения в геосфере Земли: – климатические изменения; – деградация почвы; – разрушение экосистем и уменьшение биологического разнообразия; – увеличение загрязнения воды почвы и воздуха; – природные бедствия вызванные деятельностью человека; – неконтролируемый прирост...
36436. Рекреациооные ресурсы 32.5 KB
  К рекреационным ресурсам относятся: природные комплексы и их компоненты рельеф климат водоемы растительность животный мир; культурноисторические достопримечательности; экономический потенциал территории включающий инфраструктуру трудовые ресурсы. Рекреационные ресурсы это совокупность элементов природных природнотехнических и социальноэкономических геосистем которые при соответствующем развитии производительных сил могут быть использованы для организации рекреационного хозяйства. Рекреационные ресурсы кроме природных...
36437. Эстетическая привлекательность ландшафтов 31 KB
  Современные подходы к оценке эстетической привлекательности ландшафтов. Объективистский подход предполагает выявление объективных критериев эстетической привлекательности кроющихся в физиономических характеристиках самого ландшафта субъективный же указывая на субъективную природу красоты исследует особенности ландшафтноэстетических предпочтений у разных групп людей.Объективистский подход к оценке эстетической привлекательности ландшафтов является в настоящее время наиболее признанным и распространенным. Также к его недостаткам...
36438. Виды воздействия рекреационной деятельности на ОС 25.5 KB
  Ранее исследованиям по анализу туристской деятельности уделялось мало внимания да и то рассматривали воздействие туризма только в определённых точках земного шара или воздействие отдельных его видов. Воздействие туризма на окружающую среду может быть прямым косвенным и побудительным а также положительным и отрицательным. Туризм не может развиваться без взаимодействия с окружающей средой однако с помощью управления развитием туризма и чёткого планирования возможно уменьшить негативное воздействие и увеличить положительное. Положительное...
36439. Национальные парки 30 KB
  Основные задачи национальных парков На национальные парки возлагаются следующие основные задачи: а сохранение природных комплексов уникальных и эталонных природных участков и объектов; б сохранение историкокультурных объектов; в экологическое просвещение населения; г создание условий для регулируемого туризма и отдыха; д разработка и внедрение научных методов охраны природы и экологического просвещения; е осуществление экологического мониторинга; ж восстановление нарушенных природных и историкокультурных комплексов и объектов. Исходя...
36440. Государственные природные заказники России: статус, режим, функции, задачи, перспективы развития ФЗначения 29 KB
  Государственные природные заповедники являются природоохранными научноисследовательскими и экологопросветительскими учреждениями имеющими целью сохранение и изучение естественного хода природных процессов и явлений генетического фонда растительного и животного мира отдельных видов и сообществ растений и животных типичных и уникальных экологических систем. Задачи государственных природных заповедников а осуществление охраны природных территорий в целях сохранения биологического разнообразия и поддержания в естественном состоянии...
36441. Категории и виды ООПТ Арх. Области 26.5 KB
  Области сеть ОПТ: заповедники национальные парки заказники памятники природы. На западе области организовано два среднетаежных НП: Водлозерский природный 3411 км2 и Кенозерский 1397 км2. Первый находится на границе Карелии и Архангельской области и включает девственную тайгу на югозападе Онежского района. В области организовано 36 видовых и комплексных заказников 55 681 км2 или 135 площади области.
36442. Восточно-европейская зона 37 KB
  Ее туристские ресурсы определяются помимо живописности природы море реки леса средневысотные горы возможностью заниматься летними и зимними видами спорта а также большим числом историкокультурных памятников разных эпох музыкальными традициями страны и пр. Природной аттрактивностыо отличаются в первую очередь север страны с его Балтийским побережьем и юг где расположена часть Карпатской горной системы. Что касается привлекательности историкокультурной историкоархитектурной то ею обладают главный туристский центр страны и ее...
36443. Западно-европейская 33.5 KB
  Она привлекает туристов и очень разнообразной природой и множеством историкокультурных историкоархитектурных объектов.; преобладанием воздушных перевозок своих и иностранных туристов над автомобильными. При этом в районе велико число туристов не только летом но и зимой в период когда в доступных для туристов горах есть необходимый снежный покров. Наиболее интересны для туристов города Женева Цюрих Берн столица страны.