Лекции (1171139), страница 15
Текст из файла (страница 15)
Ранее скомпилированные программы можно загружать из библиотек.В связи с этим возникают следующие вопросы:• Как писать объявления, чтобы на протяжении компиляции используемые переменныебыли должным образом объявлены?• В каком порядке располагать объявления, чтобы во время загрузки все части программыоказались связаны нужным образом?• Как организовать объявления, чтобы они имели лишь одну копию?• Как инициализировать внешние переменные?Начнем с того, что разобьем программу-калькулятор на несколько файлов. Конечно, этапрограмма слишком мала, чтобы ее стоило разбивать на файлы, однако разбиениепрограммы позволит продемонстрировать проблемы, возникающие в большихпрограммах.Областью видимости имени считается часть программы, в которой это имя можноиспользовать.
Для автоматических переменных, объявленных в начале функции, областьювидимости является функция, в которой они объявлены. Локальные переменные разныхфункций, имеющие, однако, одинаковые имена, никак не связаны друг с другом. То же52утверждение справедливо и в отношении параметров функции, которые фактическиявляются локальными переменными.Область действия внешней переменной или функции простирается от точкипрограммы, где она объявлена, до конца файла, подлежащего компиляции.
Например,если main, sp, val, push и pop определены в одном файле в указанном порядке, т. е.main() {...}int sp = 0;double val[MAXVAL];void push(double f) {...}double pop(void) {...}то к переменным sp и val можно адресоваться из push и pop просто по их именам; никакихдополнительных объявлений для этого не требуется. Заметим, что в main эти имена невидимы так же, как и сами push и pop. Однако, если на внешнюю переменную нужносослаться до того, как она определена, или если она определена в другом файле, то ееобъявление должно быть помечено словом extern.Важно отличать объявление внешней переменной от ее определения. Объявлениеобъявляет свойства переменной (прежде всего ее тип), а определение, кроме того,приводит к выделению для нее памяти. Если строкиint sp;double val[MAXVAL];расположены вне всех функций, то они определяют внешние переменные sp и val, т.
e.отводят для них память, и, кроме того, служат объявлениями для остальной частиисходного файла. А вот строкиextern int sp;extern double val[];объявляют для оставшейся части файла, что sp - переменная типа int, а val - массив типаdouble (размер которого определен где-то в другом месте); при этом ни переменная, нимассив не создаются, и память им не отводится.На всю совокупность файлов, из которых состоит исходная программа, для каждойвнешней переменной должно быть одно-единственное определение; другие файлы, чтобыполучить доступ к внешней переменной, должны иметь в себе объявление extern.(Впрочем, объявление extern можно поместить и в файл, в котором содержитсяопределение.) В определениях массивов необходимо указывать их размеры, что вобъявлениях extern не обязательно.
Инициализировать внешнюю переменную можнотолько в определении. Хотя вряд ли стоит организовывать нашу программу такимобразом, но мы определим push и pop в одном файле, а val и sp - в другом, где их иинициализируем. При этом для установления связей понадобятся такие определения иобъявления:В файле 1:extern int sp;extern double val[];void push(double f) {...}double pop(void) {...}В файле2:int sp = 0;double val[MAXVAL];Поскольку объявления extern находятся в начале файла1 и вне определений функций, ихдействие распространяется на все функции, причем одного набора объявлений достаточнодля всего файла1. Та же организация extern-объявлений необходима и в случае, когдапрограмма состоит из одного файла, но определения sp и val расположены после ихиспользования.53Представление кода программы в нескольких файлахЗаголовочные файлыПредставим себе, что компоненты программы имеют существенно большиеразмеры, и зададимся вопросом, как в этом случае распределить их по несколькимфайлам.
Программу main поместим в файл, который мы назовем main.с; функции push,pop и их переменные расположим во втором файле, stack.с; a функцию getop - в третьем,getop.c. Наконец, функции getch и ungetch разместим в четвертом файле getch.с; мыотделили их от остальных функций.Существует еще один момент, о котором следует предупредить читателя, определения и объявления совместно используются несколькими файлами. Мы бы хотели,насколько это возможно, централизовать эти объявления и определения так, чтобы дляних существовала только одна копия. Тогда программу в процессе ее развития будет легчеи исправлять, и поддерживать в нужном состоянии.
Для этого общую информациюрасположим в заголовочном файле calc.h, который будем по мере необходимостивключать в другие файлы. В результате получим программу, файловая структура которойпоказана ниже:main.с:#include <stdio.h>#include <stdlib.h>#include "calc.h"#define MAXOP 100main() {...}calc.h:#define NUMBER '0'void push(double);double pop(void);int getop(char[]);int getch(void);void ungetch(int);getop.c:#include <stdio.h>#include <ctype.h>#include "calc.h"getop (){...}getch.c:#include <stdio.h>#define BUFSIZE 100char buf[BUFSIZE];intbufp = 0;int getch(void) {...}void ungetch(int) {...}stack.с:#include <stdio.h>#include "calc.h"#define MAXVAL 100int sp = 0;54double val[MAXVAL];void push(double) {...}double pop(void) {...}Неизбежен компромисс между стремлением, чтобы каждый файл владел только тойинформацией, которая ему необходима для работы, и тем, что на практике иметь дело сбольшим количеством заголовочных файлов довольно трудно.
Для программ, непревышающих некоторого среднего размера, вероятно, лучше всего иметь одинзаголовочный файл, в котором собраны вместе все объекты, каждый из которыхиспользуется в двух различных файлах; так мы здесь и поступили. Для программ большихразмеров потребуется более сложная организация с большим числом заголовочныхфайлов.Статические переменныеПеременные sp и val в файле stack.с, а также buf и bufp в getch.с находятся в личномпользовании функций этих файлов, и нет смысла открывать к ним доступ кому-либо еще.Указание static, примененное к внешней переменной или функции, ограничиваетобласть видимости соответствующего объекта концом файла.
Это способ скрыть имена.Так, переменные buf и bufp должны быть внешними, поскольку их совместно используютфункции getch и ungetch, но их следует сделать невидимыми для "пользователей" функцийgetch и ungetch.Статическая память специфицируется словом static, которое помещается передобычным объявлением. Если рассматриваемые нами две функции и две переменныекомпилируются в одном файле, как в показанном ниже примере:static char buf[BUFSIZE]; /* буфер для ungetch */static int bufp = 0;/* след.
свободная позиция в buf */int getch(void) {...}void ungetch(int с) {...}то никакая другая программа не будет иметь доступ ни к buf, ни к bufp, и этими именамиможно свободно пользоваться в других файлах для совсем иных целей. Точно так же,помещая указание static перед объявлениями переменных sp и val, с которыми работаюттолько push и pop, мы можем скрыть их от остальных функций.Указание static чаще всего используется для переменных, но с равным успехом егоможно применять и к функциям. Обычно имена функций глобальны и видимы из любогоместа программы. Если же функция помечена словом static, то ее имя становитсяневидимым вне файла, в котором она определена.Объявление static можно использовать и для внутренних переменных.
Как иавтоматические переменные, внутренние статические переменные локальны в функциях,но в отличие от автоматических, они не возникают только на период работы функции, асуществуют постоянно. Это значит, что внутренние статические переменныеобеспечивают постоянное сохранение данных внутри функции.55ЗАДАНИЯ НА ДОММетоды сортировки должны быть реализованы в виде функций. Работу с массивамиорганизовать через указатели. Код программы должен располагаться в нескольких файла: взаголовочном файле определить необходимые переменные и заголовки функций, во второмфайле должен располагаться код функций сортировки, в главном файле – вызов функции main.1. Написать программу сортировки массивов методом отбора и методом Шелла. Сравнитьскорость работы указанных методов сортировки.2.
Написать программу сортировки массивов методом вставки и с помощью быстройсортировки. Сравнить скорость работы указанных методов сортировки.3. Написать программу сортировки массивов методом отбора и методом быстрой сортировки.Сравнить скорость работы указанных методов сортировки.4. Написать программу сортировки массивов методом вставки и методом Шелла. Сравнитьскорость работы указанных методов сортировки.Для вычисление времени работы алгоритма использовать функцию gettimeofday(&tv,NULL) изsys/time.h#include <sys/time.h>unsigned long GetTickCount(){struct timeval tv;gettimeofday(&tv,NULL);return (tv.tv_sec*1000+tv.tv_usec/1000);}56Тип charВ языке C среди базовых типов данных строк как таковых не оказалось. Вместоэтого язык C предлагает использовать одномерные символьные массивы, в которыххранятся те же строки в виде последовательности однобайтовых символов,завершающихся байтом с нулевым кодом.
Для этого признака конца строки в составEscape- последовательностей включен специальный код '\0', хотя можно было бывоспользоваться и другой комбинацией – '\0x0'.В языке C++ появился гораздо более удобный класс строковых данных string. Нознакомство с этим классом мы отложим на более поздний срок.Значения строковых констант или начальные значения строковых "переменных" вотличие от символьных данных заключаются в двойные кавычки:const char c1[]="ABCDEFGH";char str1[]="1234";char letter[]="a";char symbol='a';Байт с нулевым кодом система автоматически добавляет вслед за последним символомстроки.