22334

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

Лекция

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

ОБЩАЯ СТРУКТУРА ПРОГРАММЫ НА ЯЗЫКЕ СИ. Та функция с которой начинается выполнение программы называется главной функцией. Она по существу является входной точкой программы и должна иметь предопределенное имя main.

Русский

2013-08-04

51.5 KB

5 чел.

ЛЕКЦИЯ 7

Общая структура программы на языке Си. Время существования и видимость переменных. Блоки. Классы  памяти. Автоматические, внешние, статические и регистровые переменные.  Рекурсивные  функции. Реализация  рекурсивных алгоритмов. Препроцессор языка Си: включаемые файлы, символические  имена и  макроопределения. Модели памяти, поддерживаемые компилятором IBM C/2.

$ 1. ОБЩАЯ СТРУКТУРА ПРОГРАММЫ НА ЯЗЫКЕ СИ. ВРЕМЯ СУЩЕСТВОВАНИЯ И ВИДИМОСТЬ ПЕРЕМЕННЫХ. БЛОКИ

Как отмечалось в конце предыдущей лекции, всякая  программа  на  языке  Си  представляет  собой совокупность одной или более функций,  каждая из которых есть независимый набор описаний и операторов, заключенных между заголовком функции и ее концом. Та функция, с которой начинается выполнение программы, называется главной функцией. Она по  существу является входной точкой программы и должна иметь  предопределенное имя main(). Все остальные функции, возможно входящие в состав  программы  и  выполняющие конкретную часть работы по реализации алгоритма решения задачи, играют подчиненную роль  и  запускаются  в   работу  путем  их прямого или опосредованного (через другие функции)  вызова из главной функции. Работа всей программы обычно заканчивается по достижении конца главной функции, однако ее  выполнение  может  быть  прекращено  путем  передачи управления операционной системе из  произвольной точки программы. Любая функция, в том числе и  главная,     может  иметь один или более формальных параметров, которые используются для передачи в ее тело необходимых числовых значений  в  момент  вызова.  Наличие параметров у главной функции является специфической   особенностью языка Си и используется для обработки аргументов командной строки при обращении к программе.          Несмотря на то, что функции в языке Си представляют  собой  основные  строительные блоки всякой программы, при рассмотрении примеров и задач предыдущих лекций нам уже приходилось встречаться с  определением  переменных,  констант и литерных цепочек вне какой бы то  ни было функции, или, иначе говоря, на внешнем уровне. Именно так  мы   поступали  при работе с включаемыми файлами, содержащими определения     системных констант и предварительные  описания  функций,  или  желая   сделать  какую-либо  переменную доступной сразу нескольким функциям.  Определенные на внешнем уровне объекты уже теряют  свойство  локальности  по отношению к функциям и формируют программную оболочку, называемую  глобальной  средой.  Для  дальнейшего  изучения  структуры  Си-программы,  нам необходимо определить понятия времени существования и видимости различных объектов, участвующих в ее работе.  Под временем существования переменной или функции обычно  понимают  продолжительность  их  реального  нахождения  в памяти ЭВМ. Те  объекты программы, которые создаются в момент начала ее работы и существуют вплоть до полного завершения  последней,  принято  называть  объектами с глобальным временем существования. С другой стороны, переменные  и структуры данных, создающиеся динамически в процессе работы программы и ликвидируемые раньше  ее  фактического  завершения,  будем  называть объектами с локальным временем существования. Память     под представление локальных объектов выделяется компилятором  в  момент  входа  в  ту программную компоненту, которая содержит описания  этих объектов, и возвращается назад операционной системе по достижении ее конца.  Под видимостью переменной или функции следует понимать  ту  область исходной программы, в пределах которой разрешена работа с этой  переменной или обращение к соответствующей функции. Объекты программы, обращение к которым разрешено из любой ее точки, называют объектами с глобальной видимостью. Таковыми, например, являются все функции,  входящие  в  состав  программы, а также переменные и структуры  данных, определенные вне какой-либо функции. Наоборот,  те  переменные,  область доступа к которым ограничена некоторой частью исходной  программы, мы будем называть  переменными  с  локальной  видимостью.  Так, локальную видимость имеют любые объекты, описанные в теле функции,  если  только не принято специальных мер для расширения области     доступа к ним.  Общее правило, определяющее  время  существования  и  видимость

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

       summa(tab_1, m, tab_2, n)

       int  *tab_1, m;    /*  Адрес и размерность первого массива  */

       int  *tab_2, n;    /*  Адрес и размерность второго массива  */

       { int  i, sum;

         sum = 0;

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

            sum = sum + tab_1[i];

               { int  sum;

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

                    sum = sum + tab_2[i];

                 printf("\nСумма элементов второго массива = %d", sum);

               }

         printf("\nСумма элементов первого массива = %d", sum);

       }

Аналогичная ситуация возникает и в том случае, когда какая-либо  переменная, определенная внутри блока, имеет то же самое имя, что  и   некоторая  внешняя  переменная. Здесь, так же как и ранее, локальное   описание замещает внешнее  описание  переменной,  восстанавливая  ее  прежнее значение по завершении работы блока.          Исходный  текст  Си-программы  допускается подразделять на несколько файлов, каждый из которых содержит необходимые описания переменных и определения функций. Компиляция всех  таких  файлов  должна   производиться раздельно с последующей сборкой соответствующих объектных модулей на этапе построения готовой к выполнению программы (см. Лекцию 1). Ниже при обсуждении вопроса о классах памяти, будут сформулированы  условия, при которых разрешены ссылки на объекты за

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

     #include  <stdio.h>   /*  Включение стандартного файла stdio.h   */

     #define  MAX  100     /*  Максимальное допустимое количество     */

                           /*  элементов в массиве                    */

     float  mas[MAX];      /*  Внешнее описание числового массива     */

     main()

     { int   i;            /*  Параметр циклов                        */

       int   n;            /*  Реальное количество элементов массива  */

       FILE  *fp;          /*  Указатель на файл, содержащий          */

                           /*  входной массив                         */

     /*-------     Ввод элементов массива из файла mas.dat     -------*/

       if ((fp = fopen("mas.dat", "r")) == NULL) return;

       n = 0;

       while (fscanf(fp, "%f", &mas[n]) != EOF)

          n++;

       fclose(fp);

     /*-------              Сортировка массива                 -------*/

       { int    mind;        /*  Индекс максимального элемента        */

         float  buf;         /*  Буфер для перестановки элементов     */

         for (i = n-1; i > 0; i--)

            { mind = maxind(i);

              buf = mas[i]; mas[i] = mas[mind]; mas[mind] = buf; }

       }                     /*  Конец внутреннего блока              */

     /*-------              Печать результатов                 -------*/

       printf("\t\t\t*** Массив упорядочен по возрастанию ***\n\n");

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

          printf("%7.3f", mas[i]);

     }                       /*  Конец главной функции                */

     /*-------    Нахождение индекса максимального элемента    -------*/

     maxind(m)

     int  m;

     { int    i;             /*  Параметр цикла                       */

       int    mind;          /*  Текущее значение номера макс. эл-та  */

       float  max;           /*  Текущее значение макс. элемента      */

       max = mas[0]; mind = 0;

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

          if (mas[i] > max)

             { max = mas[i]; mind = i; }

       return (mind);

     }                       /*  Конец функции maxind                 */

В этом примере массив вещественных чисел mas определен на внешнем  уровне  и поэтому доступен обеим функциям программы. Переменные  mind и buf определены локально во внутреннем блоке функции main()  и  область  их  видимости ограничена одним этим блоком. То же самое имя  mind использовано в теле функции maxind() для  обозначения  текущего  значения  индекса максимального элемента и никоим образом не связано  с его определением функции main(). Аналогичное замечание можно  сделать  и  относительно  параметра цикла i в главной и вспомогательной  функциях.

$ 2. КЛАССЫ ПАМЯТИ

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

                   auto    extern    static    register

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

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

2.1. Автоматические переменные (класс памяти auto)

Объекты программы, принадлежащие классу памяти auto и именуемые  обычно  автоматическими переменными, имеют локальное время существования и область их видимости ограничена тем блоком,  в  котором  они  определены.  Память  для  их представления выделяется динамически из  программного стека в момент входа в этот блок и  возвращается  назад  системе  по  достижении его конца. По умолчанию все переменные, описанные на внутреннем уровне (т. е. в теле функции), являются

автоматическими. Однако это можно  явно  подчеркнуть,  используя  ключевое   слово auto:

                       main()

                       { auto int   alpha, beta;

                         auto char  line[81];

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

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

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

                       }

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

данных (например, массивов), имеющих класс памяти auto, не  поддерживается компилятором языка Си.

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

2.2. Внешние объекты программы (класс памяти extern)

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

      main()

      { extern int  number;    /*  Описание внешней переменной     */

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

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

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

      }

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

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

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

      int  number = 17;        /*  Определение внешней переменной  */

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

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

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

переменная number используется функцией main() раньше, чем она определена в программе. Ее начальное значение в этой функции равно 17.  С  точки  зрения  рассмотренных понятий времени существования и  видимости объектов программы, важно различать описания внешних переменных и их определения. Так, описания начинаются с ключевого слова extern и могут встречаться многократно на  внешнем  и  внутреннем   уровнях,  задавая  тем самым область видимости соответствующих

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

2.3. Статические переменные и функции (класс памяти static)

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

такой переменной в программе. Ссылка на статические переменные из другого файла или их использование до фактического определения в  текущем  файле  не  допустимо. Таким образом, описатель static позволяет  сузить область видимости внешней переменной.          Внутренние статические  переменные,  как  и  переменные  класса  auto, имеют локальную видимость в пределах текущей функции (или блока), однако они сохраняются в памяти ЭВМ на протяжении всего времени   работы  программы. Это позволяет создавать об'екты с глобальным временем существования, ограничивая их видимость в программе. Так, например, переменная count, определенная в теле функции calc:

                          main()

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

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

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

                          }

                          calc()

                          { static int  count;

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

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

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

                          }

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

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

2.4. Регистровые переменные (класс памяти register)

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

                          func(p, q)

                          register int  p, q;

                          { register char  sym;

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

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

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

                          }

Операция  получения адреса для регистровых переменных не допустима. На многих типах ЭВМ этот класс памяти не может  быть  назначен  переменным типа float.

$ 3. РЕКУРСИВНЫЕ ВЫЗОВЫ ФУНКЦИЙ. РЕАЛИЗАЦИЯ

РЕКУРСИВНЫХ АЛГОРИТМОВ

Всякая  функция в языке Си имеет реентерабельный (повторно входимый) код, что позволяет ей обращаться к самой себе непосредственно  или через другие функции. Такие  обращения  называются  рекурсивными   вызовами или рекурсией.  При  каждом  очередном рекурсивном вызове создается новая копия   параметров функции, а также определенных в ее теле автоматических  и   регистровых  переменных.  Внешние  и статические переменные, имеющие   глобальное время существования, сохраняют при этом свои прежние значения и размещение памяти.  Несмотря на то, что ни стандарт языка Си, ни компилятор IBM C/2   формально не налагают никакого ограничения на количество рекурсивных   обращений, тем не менее оно практически всегда существует для  любых  типов  ЭВМ,  ибо каждый новый вызов требует дополнительной памяти из     ресурса программного стека. Если количество вызовов излишне  велико,  возникает  переполнение сегмента стека и операционная система уже не  может создать очередного экземпляра локальных объектов функции,  что  ведет, как правило, к аварийному завершению программы.  В качестве примера реализации рекурсивного алгоритма рассмотрим   функцию  printd(),  печатающую целое число в виде последовательности     символов ASCII (т. е. цифр, образующих запись этого числа):

        printd(num)

        int  num;                  /*  Печатаемое число           */

        { int  i;

          if (num < 0)

             { putchar('-'); num = -num; }

          if ((i = num/10) != 0)

             printd(i);            /*  Рекурсивный вызов функции  */

          putchar(num % 10 + '0')

        }

Если значение переменной value равно 123, то в случае вызова

                            printd(value);

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

$ 4. ПРЕПРОЦЕССОР ЯЗЫКА СИ

Препроцессор языка Си предназначен для внесения изменений в исходный текст программы непосредственно перед ее компиляцией, а также  для  установки значений параметров, используемых компилятором. Обращение к препроцессору осуществляется при помощи набора директив, начинающихся с символа номера (#), и позволяет  добиться  изящества  и  мобильности  Си-программ. В этом параграфе рассматриваются некоторые     наиболее важные и часто используемые директивы препроцессора,  работающего в составе компилятора IBM C/2.

4.1. Директива #define

Эта директива, позволяющая присваивать текстовым строкам символические имена, может иметь один из следующих форматов:

                 #define identifier <text>

или

                 #define identifier(parameter-list) <text>

где угловыми скобками (<>) обозначена необязательная часть конструкций. Первая форма интерпретируется препроцессором как простая  литеральная подстановка, выполняемая перед фактическим обращением к компилятору. Вторая же из них служит для создания макроопределений, семантически  подобных  определению  обычных функций. Здесь identifier   есть произвольное, правильное в смысле грамматики  языка,  имя  (см.   Лекцию  1, $ 3), а text представляет собой строку символов, замещающую всякое вхождение этого имени в исходный текст  программы.  Заметим,  что  такая замена выполняется лишь для отдельно стоящих имен и  не имеет места в тех случаях, когда identifier входит в состав текстовых строк или является составной частью более длинного идентификатора.  Список аргументов parameter-list включает в себя один или большее число формальных параметров, разделенных запятыми и имеющих уникальные имена. В этом случае препроцессор заменяет имена параметров,  входящие  в  text, именами соответствующих фактических аргументов, и  лишь после этого будет выполнена реальная подстановка текста на место идентификатора.  Символьная строка text может иметь произвольную длину, возможно  даже превосходящую длину одной строки экрана. Для формирования длинного текста необходимо набрать символ \, нажать клавишу Enter  и  продолжить  ввод символов с начала новой экранной строки. С другой стороны, параметр text может вообще отсутствовать в составе  директивы,  что соответствует определению идентификатора identifier без присвоения ему какого-либо конкретного значения.          Приведем несколько примеров использования директивы #define для   задания имен текстовых цепочек и формирования макроопределений функций.

         1.  Наиболее  характерным  применением  этой директивы является  назначение символических имен констант:

                       #define  WIDTH   100

                       #def1ne  LENGTH  (WIDTH + 30)

Здесь идентификатор WIDTH объявлен числовой константой, равной  100,   а  имя  LENGTH определено при помощи WIDTH и целого числа 30, причем   круглые скобки во втором случае являются существенными.

         2. Программисты, желающие видеть свою  программу  синтаксически   поможет,  например,  на  исходный текст программы на языке Алгол-60,   могут воспользоваться директивой #define для замены ключевых слов  и   специальных символов, принятых в языке Си:

                          #define  begin    {

                          #define  end      ;}

                          #define  integer  int

                          #define  real     float

                          #define  then

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

                             if (a >= b) then

                             begin

                                max = a;

                                min = b;

                             end

не нарушая тем самым синтаксических правил языка Си.

         3.  Вместо  определения  функции abs(x), вычисляющей абсолютную   величину переменной x, в программе можно иметь семантически  эквивалентное ему макроопределение вида

                #define  abs(x)    ((x) >= 0) ? (x) : (-x)

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

4.2. Директива #undef

Данная директива имеет формат

                             #undef identifier

и заставляет препроцессор игнорировать все последующие вхождения определенного ранее в инструкции #define имени identifier. Это  позволяет  ограничить  область  исходной  программы,  в  пределах которой   identifier обрабатывается препроцессором и имеет специальное  значение.  В следующем примере область действия  идентификаторов  WIDTH  и   ADD  ограничена  лишь частью исходного файла, в пределах которой они     принимаются во внимание препроцессором языка Си:

                       #define  WIDTH      100

                       #define  ADD(x, y)  (x) + (y)

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

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

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

                       #undef  WIDTH

                       #undef  ADD

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

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

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

4.3. Директива #include

Формат  этой  директивы  в  общем случае определяется следующей   схемой:

                           #include  "pathname"

или

                           #include  <pathname>

где угловые скобки (<>) являются составной частью конструкции. Здесь   pathname  есть правильное имя файла в смысле операционной системы MS  DOS.  Директива  #include  позволяет  включать  текст  файла с именем   pathname в состав файла, содержащего обращение к этой директиве. Если pathname задает полное имя файла от корневого  каталога,  то  две  приведенные  выше формы записи директивы эквивалентны. В том же случае, когда задано относительное имя файла, использование двойных кавычек (" ") заставляет препроцессор прежде  всего  искать  требуемый  файл в текущем каталоге, затем просматривать каталог, определенный в  команде  вызова  компилятора и, наконец, в случае необходимости продолжить поиск в стандартном каталоге. Однако  при  заключении  имени  файла в угловые скобки текущий каталог не просматривается.          Препроцессор  допускает  последовательные  вложения  инструкций  #include максимально до десяти уровней.  Это  означает,  что  всякий  файл,  подключаемый с помощью этой директивы, может содержать внутри   себя новое обращение к #include.          В составе исходного текста программы директива  #include  может  размещаться в произвольном месте содержащего ее файла.

          $ 5. МОДЕЛИ ПАМЯТИ, ПОДДЕРЖИВАЕМЫЕ КОМПИЛЯТОРОМ IBM C/2

Сегментная организация доступа к оперативной памяти ЭВМ, характерная  для  микропроцессоров  Intel  8086/80286 и тесно связанная с  особенностями их внутренней архитектуры, требует специального  рассмотрения  вопроса о размещении программного кода и данных в процессе   работы программы. Этот вопрос имеет важное значение с  точки  зрения   формирования  исполнительных  адресов  при  обращении к памяти и его     изучение необходимо для полного понимания аппарата работы с указателями и механизма передачи параметров между отдельными функциями.  Всякая программа на языке Си после ее подготовки  к  выполнению  (т. е. компиляции и редактирования связей) и последующей  загрузки в  память персонального компьютера IBM PC представляется тремя основными типами независимых сегментов: кода, данных и стека. Размер одного     сегмента каждого из этих типов не может превышать  64K  байт  памяти  (1K = 1024 байт). Принципиально для нормальной работы любой программы  необходимо иметь по крайней мере два таких сегмента, один из которых хранит инструкции выполняемой программы (сегмент кода), а второй содержит данные (сегмент данных), обрабатываемые  этой  программой.  Однако  если  программа достаточно велика или большим является     набор обрабатываемых ею данных, то может потребоваться более  одного  сегмента  того  или  иного  типа. Кроме этого, в большинстве случаев  оказывается необходимым  иметь  специальным  образом  организованный  сегмент  стека для хранения автоматических переменных и передачи параметров функциям при их вызове. По существу обсуждаемые ниже модели  организации памяти имеют самое прямое отношение к количеству

сегментов различного типа и определяют тем  самым  максимально  допустимый   объм программы и ее данных.  Компилятор IBM C/2 поддерживает пять основных моделей сегментации  памяти: small, medium, compact, large и huge. Каждая из них может быть выбрана при помощи соответствующей опции при вызове  компилятора и обеспечивается своими собственными библиотеками стандартных   функций.  Модель  small обычно используется для небольших по объему программ, машинный код которых и данные могут разместиться в одном  сегменте  каждый. Общий размер программы в этом случае не должен превышать 128K байт памяти. Такая модель является наиболее экономичной  в     смысле использования оперативной памяти и скорости работы программы.  Для  нее  компилятор производит короткие 16-разрядные указатели (тип  near) при обращении к данным и инструкциям программы.   Модель medium позволяет иметь большой объем программного  кода,  но  допускает  работу лишь с относительно небольшим количеством данных. В этом случае код программы может занимать  произвольное  количество  сегментов  по  64K каждый, однако сегмент данных должен быть  всего один. Отдельные сегменты кода создаются компилятором из  независимых программных модулей, каковыми являются файлы, содержащие исходный  текст программы. Для такой модели инструкции программы адресуются с помощью 32-разрядных указателей (тип far), а данные выбираются в пределах единственного сегмента при  помощи  указателей  типа     near.   Модель  compact в определенном смысле противоположна предыдущей  модели, ибо для нее характерен большой объем данных, занимающих произвольное количество стандартных сегментов, и малый объем  программного  кода, помещаемого компилятором в один сегмент. В рассматриваемом случае доступ к данным осуществляется посредством указателей типа far, а для  выбора  из  памяти  инструкций  программы  достаточно  16-разрядного адреса.   Модель  large допускает создание больших программ, состоящих из  произвольного количества сегментов кода и данных, общий объем  которых  ограничен  лишь адресным пространством используемой ЭВМ (1M для     микропроцессора Intel 8086 и 16M для микропроцессора Intel 80286)  и     реальным  об'емом оперативной памяти. Однако несмотря на это, создание массивов переменных, превышающих 64K байт, не является  допустимым  для рассматриваемой модели. В данном случае все указатели имеют   тип far.   Модель huge полностью идентична модели large с той лишь  разницей,  что  она  позволяет готовить массивы переменных, требующие для  своего хранения более 64K байт памяти, размещая каждый из них в нескольких сегментах.   Рациональное использование пяти  рассмотренных  моделей  памяти   дает  возможность  создавать гибкие, эффективные и большие по объему   программные системы. Более  того,  допустимо  произвольное  смешение

этих  моделей путем игнорирования стандартных соглашений о длине адресов и явного назначения типа указателей с  помощью  ключевых  слов  near, far и huge при их определении в программе. Так, например, декларация

                          int  (far *vector)[30];

определяет  указатель  типа  far на массив vector тридцати элементов  типа int независимо от того, какая модель памяти будет  выбрана  при  компиляции программы.


 

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

19766. Состав сооружений магистральных газопроводов и нефтепроводов 1.02 MB
  Состав сооружений магистральных газопроводов и нефтепроводов. В состав подземного магистрального газопровода входят линейная и наземные объекты рис. 1. Рис. 1. Состав магистрального газопровода: 1 газовая скважина с газопроводом от ее...
19767. Ремонт магистральных трубопроводов 2.15 MB
  Труба в трубе Нанесение Новой изоля ции поверх старой Замена изоляции Замена изоляции и частичная замена или восстановление труб амена участка трубопровода Восстановление старой изоляции Капитальный ремонт трубопроводо
19768. Сооружение насосных и компрессорных станций 392.5 KB
  Назначение и классификация НС и КС. Генеральный план. Планировка строительной площадки. По технологическому принципу КС делят на: головные компрессорные станции линейные компрессорные станции дожимные компрессорные станци
19769. Машины и оборудование для гн проводов 1.55 MB
  26 41. Структура годового фонда работы машин при строительстве и ремонте г/н проводов Фактическое число дней работы в году комплектов машин в различных подразделениях колеблется в широких пределах: в Центральном регионе 145 220 Среднеазиатском 150 230 Северном
19771. Сооружение и ремонт сетей газоснабжения и нефтепродуктообеспечения 525.69 KB
  1.Классификация газопроводов сетей газораспределения и газопотребления прокладываемых в больших городах и населенных пунктах. По давлению. В зависимости от максимального давления газа городские газопроводы разделяют на следующие группы: 1 газопроводы низ...
19772. Механика грунтов 440 KB
  Физические свойства грунтов Степень уплотненности грунта в условиях природного залегания оценивается на основе физических характеристик выявляемых путем постановки опытов в полевых условиях или испытания в лабораториях. Рассматрив...
19773. Строительные конструкции 6.62 MB
  71. Конструктивные схемы прокладки линейной части трубопроводов Основной составляющей магистрального трубопровода является линейная часть непрерывная нить сваренная из отдельных труб или секций и уложенная вдоль трассы тем или иным способом. В настоящее время су
19774. Сооружение и ремонт резервуарных парков 3.72 MB
  81. Стальные вертикальные цилиндрические резервуары с плавающими крышами. Для сокращения потерь нефтепродуктов от испарения поверхность жидкости в резервуаре закрывают круглой мембраной – понтоном или плавающей крышей рис.1.. Основное отличие данного типа резервуар...