АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомДругоеЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция

ФУНКЦИИ И ПРОГРАММЫ

Читайте также:
  1. I. Основы применения программы Excel
  2. II съезд РСДРП. Принятие программы и устава. Возникновение большевизма.
  3. II. Основные задачи и функции
  4. III. Предмет, метод и функции философии.
  5. IV. Конструкция бент-функции
  6. Ms Excel: мастер функций. Логические функции.
  7. V2: ДЕ 29 - Введение в анализ. Предел функции на бесконечности
  8. V2: ДЕ 32 - Дифференциальное исчисление функции одной переменной. Производная
  9. V2: ДЕ 35 - Дифференциальное исчисление функции одной переменной. Производные высший порядков
  10. V2: ДЕ 39 - Интегральное исчисление функции одной переменной. Приложения определенного интеграла
  11. V2: Функции исторической науки
  12. VIII. ФУНКЦИИ НАУЧНОГО ИССЛЕДОВАНИЯ

 

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

 

3.1. Определение функции

 

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

В теле функции может присутствовать оператор return, синтаксис которого имеет следующий вид

 

return Выражение;

 

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



 

Тип Имя(Тип1 Имя1,...,ТипN ИмяN){

Тело функции

}

 

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

В соответствии с данным определением, конструкция вида

 

void func(void){ }

 

формально является примером функции. Только эта функция не выполняет никаких полезных действий. В качестве другого примера рассмотрим определение функции Max() для нахождения наибольшего из двух объектов типа double.

 

Пример 3.1. Вычисление максимального значения

 

double Max(double x1, double x2){

return (x1<x2)?x2:x1;

}

 

В качестве более сложного примера рассмотрим определение функции для вычисления квадратного корня из заданного числа x. Оказывается, что за конечное число арифметических операций можно вычислить значения только двух математических функций. Одна из них является многочленом с рациональными коэффициентами, а другая – дробью вида , называемой дробно-рациональной функцией. Отметим, что аргумент этих функций также должен быть рациональным числом. Значения других математических функций (например, sin(x), exp(x), ) за конечное число операций можно вычислить толь­ко с некоторой погрешностью .

‡агрузка...

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

 

 

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

Применим изложенный подход к вычислению функции , то есть квадратного корня из . Для этого можно воспользоваться числовой последовательностью , определяемой по формуле, которая носит имя древнегреческого механика и математика Герона Александрийского, жившего в первом веке н.э.:

 

.

 

Доказано, что она сходится к неизвестному , и что погрешность -ого приближения удовлетворяет неравенству

 

.

 

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

 

.

 

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

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

 

Пример 3.2. Функция вычисления квадратного корня

 

double MySqrt(double x, double Eps){

// Определение переменных

double xPre, xCur, Delta;

// Выбор начального приближения

xPre=x>1?0.3*x:2*x;

// Цикл

do{

// Вычисление очередного приближения

xCur=(xPre+x/xPre)*0.5;

// Оценка погрешности

Delta=xPre>xCur?xPre-xCur : xCur-xPre;

// Подготовка к повторению цикла

xPre=xCur;

}

// Принятие решения о повторении цикла

while(Delta>Eps);

return xCur;

}

 

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

 

3.2 Правила вызова функций. Программы

 

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

 

double x=333.0, y, z;

z=sqrt(2.0)+Max(x, sqrt(Max(0.0, y)+33.0);

 

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

 

double Max(double, double);

// Или по-другому

double Max(double x1, double x2);

 

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

Прототипы стандартных функций хранятся в нескольких файлах с расширением .h (от слова head). Например, в файл с именем math.h сгруппированы прототипы функций для вычисления значений элементарных математических функций, а в файл с именем stdio.h – прототипы функций ввода-вывода. При использовании стандартной функции программист должен добавить файл с ее прототипом к тексту своего приложения. Для этого служит команда компилятору следующего вида

 

#include <Имя_файла>

 

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

 

#include <math.h>

 

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

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

 

void main(void){}.

 

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

 

 

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

 

Пример 3.3. Табулирование функции

 

#include <stdio.h>

#include <math.h>

void main(){

// Создание переменных

double x, y, a, b, h;

// Задание исходных данных

a=0.0, b=1.0, h=0.1;

// Организация цикла

x=a;

while(x<=b){

y=(2.1*x*x-1.0)/sqrt(x*x+3.4);

// Отображение x, y

printf("\tx=%.3f\ty=%.3f\n", x, y);

x+=h;

}

}

 

После выполнения этого приложения на экране дисплея появится следующая таблица

 

x=0.000 y=-0.542

x=0.100 y=-0.530

x=0.200 y=-0.494

x=0.300 y=-0.434

x=0.400 y=-0.352

x=0.500 y=-0.249

x=0.600 y=-0.126

x=0.700 y=0.015

x=0.800 y=0.171

x=0.900 y=0.342

x=1.000 y=0.524

 

Без использования компьютера на построение такой таблицы потребовалось бы довольно много времени.

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

 

Пример 3.4. Попытка изменения значений переменных

 

#include <stdio.h >

// Определение функции Change()

void Change (int i1, int i2) {

// Запоминание значения переменной i1

int iTemp=i1;

// Замена значений переменных

i1=i2;

i2=iTemp;

}

// Главная функция

void main(void){

int x=1, y=2;

Change(x, y);

printf(“x=%d\ny=%d\n”, x,y);

}

 

После выполнения приложение на экране дисплея появится сообщение

 

x=1

y=2

 

Оно означает, что поставленную задачу решить не удалось. О том, как можно исправить ситуацию, речь пойдет в 3.3 и 3.4.

 

3.3 Знакомство с указателями

 

Для хранения каждого объекта операционная система выделяет область памяти. Она состоит из следующих друг за другом байтов и называется блоком. Размер блока опре­деляется типом объекта. Байт с наименьшим адресом в блоке называется младшим. Адресом блока или хранящегося в нем объекта называется адрес младшего байта. Если известно имя объекта, то его адрес можно получить при помощи унарной операции со вторым приоритетом. Она называется “получение адреса объекта” и обозначается знаком “&”. Единственным операндом операции является имя (идентификатор) объ­екта, который должен следовать за символом “&”. Результатом операции является его адрес. Важной особенностью языка Си является интенсивное использование адресов для получения доступа к объектам.

Если Type – идентификатор некоторого типа данных (например, int, double или char), то множество адресов объектов типа Type образует тип, для обозначения которого используется конструкция вида Type*. Отметим, что идентификатором она не является. Переменные, хранящие адреса объектов типа Type, называются указателями на Type. Они создаются по обычным правилам:

 

// Создание указателя на объект типа Type

Type* Указатель;

// Создание и инициализация указателя

Type* Указатель=Адрес;

 

Во втором случае справа от знака “=” должен стоять адрес объекта типа Type или выражение вида &ИмяОбъекта. Обычно имена указателей начинаются с префикса p, который является первой буквой слова pointer. Например:

 

int* pInt;

double df;

double* pdf=&df;

 

Если имеется указатель pType, то при помощи унарной операции, имеющей второй приоритет, можно получить объект, хранящийся по адресу pType. Эта операция обозначается символом * (звездочка) и называется разыменованием указателя. Ее единственным операндом является указатель, следующий за символом “*”. Таким образом, если pType – указатель на переменную Var типа Type, то *pType – объект, который хранится по адресу из pType. В соответствии с правилами, конструкция *pType может находиться везде, где разрешается находиться переменной Var, в частности слева от знака = в операции присваивания. Поэтому приводимый ниже фрагмент текста является допустимым:

 

// Создание объектов

int x=1, y=2;

// Создание указателя px с адресом переменной x

int* px=&x;

// Изменение значения переменной x

*px=y; // x=y

// Изменение значения указателя px

px=&y;

// Создание указателя py с адресом переменной y

int* py=&y;

// Присваивание указателю py адреса переменной x

py=&x;

// Увеличение значения переменной x.

*py+=2; // x=4

 

3.4 Два способа передачи параметров

 

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

 

Пример 3.5 - Изменение значений переменных

 

#include <stdio.h >

// Определение функции Change()

void Change (int* px, int* py) {

// Запоминание значения переменной из p1

int Temp=*px;

// Замена значений переменных

*px=*py;

*py=Temp;

}

// Главная функция

void main(void){

int x=1, y=2;

Change(&x,&y);

printf(“x=%d\ny=%d\n”, x,y);

}

 

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

 

3.5 Стандартные функции printf() и scanf()

 

Последовательность символов алфавита, заключенная в кавычки, называется строковой константой. Символы строковой константы компилятор размещает в памяти один за другим. Для доступа к ним служит адрес первого символа, имеющий тип char*. Для того, чтобы программист мог правильно определить длину строковой константы, компилятор самостоятельно добавляет в конец последовательности символ, состоящий из восьми нулевых битов. Он называется завершающим нуль-символом и обозначается в тексте, как восьмеричная константа типа char ‘\0’. Полученных сведений о строковых константах достаточно для того, чтобы приступить к знакомству со стандартными функциями printf() и scanf(). Более подробно строковые константы будут рассматриваться в следующей главе.

Функция printf() предназначена для отображения на экране дисплея одного или нескольких объектов встроенных типов и имеет следующий прототип

 

int printf(char*, ...);

 

из файла stdio.h. Обязательным параметром функции служит адрес строковой константы, называемая строкой форматирования. Многоточие означает, что кроме обязательного параметра типа char* у функции могут быть и другие параметры. Этими параметрами являются отображаемые на экране объекты. Они перечисляются через запятую. В строковой константе содержатся указания о способе представления выводимых объектов на экране дисплея. Для каждого выводимого объекта в строке форматирования должно присутствовать отдельное указание, называемое спецификацией или командой форматирования.

Каждая команда форматирования начинается с символа % (процент) и содержит кроме него один обязательный символ. Он называется символом форматирования и задает способ представления отображаемого объекта. Соответствие между символами форматирования и способом представления выводимых объектов приведено в таблице 3.1.

 

Таблица 3.1 - Примеры символов форматирования функции printf()

 

Тип выводимого объекта Символ форматирования   Способ представления выводимого объекта
int d или i в десятичной системе
o в восьмеричной системе
x или X в шестнадцатеричной системе
float или double f или F в виде десятичной дроби; количество цифр после запятой задается программистом (по умолчанию - шесть)
e или E в экспоненциальной форме; количество цифр после запятой задается программистом (по умолчанию - шесть)
char c в виде ASCII-символа
char* (строковая константа) s в виде последовательности ASCII-символов без завершающего ‘\0’-символа
void* p вид определяется типом компилятора

 

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

 

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

Для повышения наглядности вывода программист может размещать в строке форматирования между командами форматирования любые символы. В ходе вывода они отображаются на носителе. Возвращаемым значением функции printf() является количество выведенных символов.

Для ввода с клавиатуры объектов основных типов данных в основную память служит стандартная функция scanf() с прототипом

 

int scanf(char*, … );

 

из файла stdio.h. Обязательным параметром является адрес строковой константы, называемой строкой форматирования. Многоточие означает, что у функции кроме обязательного параметра типа char* могут быть и другие параметры. Клавиатура обеспечивает представление информации в виде групп символов. Строка форматирования содержит указания о типах объектов, в которые следует преобразовать эти группы. Группы называются полями ввода. При вводе нескольких полей они отделяются друг от друга разделительными символами (или кратко разделителями). В качестве разделителей служат пробелы, символы горизонтального табулирования ‘\t’ и символы новой строки ‘\n’. Для каждого поля в строке форматирования должно присутствовать отдельное указание, которое называется командой или спецификацией форматирования. Спецификация форматирования начинается с символа % (процент) и содержит кроме него один обязательный символ, называемый символом форматирования. Именно символ форматирования указывает тип объекта, в который требуется преобразовать соответствующее поле. Объекты сохраняются в переменных, адреса которых перечисляются после запятой, следующей за строкой форматирования. Переменные должны быть определены до вызова функции scanf(). Каждое поле требует собственный адрес. Соответствие между символами форматирования и типами вводимых объектов приведено в нижеследующей таблице 3.2.

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

 

Таблица 3.2 - Примеры символов форматирования функции scanf()

 

  Представление вводимых полей Символ форматирования Тип объекта
Целое в десятичной системе d int
Целое (десятичное, восьмеричное с префиксом 0 или шестнадцатеричное с префиксом 0x) i
Целое в восьмеричной системе с префиксом 0 или без o
целое в шестнадцатеричной системе с префиксом 0x или без x
Число с плавающей точкой (присутствие десятичной точки или экспоненциальной части обязательно) e, f или g float
Число с плавающей точкой (присутствие десятичной точки или экспоненциальной части обязательно) lf double
Символ в объект типа char c char
Последовательность символов s char* (строковая константа)

 

 

3.6 Область видимости идентификаторов и время жизни объектов

 

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

 

Пример 3.6 - Локальные идентификаторы

 

#include <stdio.h>

void main(void){

// Первый блок

{

// Определение переменной i

int i=0;

}

// Второй блок

{

// Еще одно определение i

int i=2;

int ii=i+3; // ii=5

// Вложенный блок

{

ii++;

printf("ii=%i\n", ii);

}

}

}

 

После выполнения этой программы на экране дисплея появится сообщение

 

ii=6

 

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

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

 

Пример 3.7 - Сокрытие глобальных идентификаторов

 

// Глобальная переменная

float x=2.0f;

void main(void){

// Увеличение глобальной переменной

x+=1.1f; // x=3.1

// Определение локальной переменной

int x=2;

// Умножение локальной переменной на десять

x*=10; // x=20

}

 

В языке Си есть операция с названием “разрешение области видимости”. Она имеет наивысший (первый) приоритет и обозначается знаком “::”. Эта операция позволяет использовать глобальные идентификаторы в области видимости локальных. Пример по этому поводу.

 

Пример 3.8 - Разрешение области видимости

 

// Глобальная переменная

int x=0;

void main(void){

// Локальная переменная

int x=1;

// Увеличение глобальной переменной

::x+=2;

// Увеличение локальной переменной

x+=3;

// ...

}

 

Данные должны находиться в памяти до тех пор, пока в них имеется необходимость. Язык Си предоставляет программисту несколько способов для определения продолжительности хранения данных, которая называется временем жизни.

Для данных с локальными именами память выделяется при их создании. Такие данные хранятся только в течение времени необходимого для выполнения блока, в котором они определены, и называются автоматическими.

Данные, которые хранятся в памяти в течение всего времени выполнения программы, называются статическими. По определению, все данные с глобальными именами являются таковыми. Если данные с локальными именами нужно хранить до конца выполнения программы, то их определение должно начинаться со служебного слова static.

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

 

Пример 3.9 - Время жизни объектов

 

#include <stdio.h>

int a; // a=0

void f(void){

int b=0;

static int c=1;

a++;

b++;

c++;

printf(“a=%d b=%d c=%d\n”,a,b,c);

}

void main(void){

f(); // a=1, b=1, c=2

f(); // a=2, b=1, c=3

f(); // a=3, b=1, c=4

}

 

3.6 Проекты

 

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

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

 

Пример 3.10 - Глобальные идентификаторы и проекты

 

// Файл №1/2

#include <stdio.h>

// Определение глобальной переменной

int j=1;

// Прототип

int f(int);

void main(){

int r=f(3);

printf("r=%d\n", r);

}

// Файл №2/2

// Глобальная переменная, определенная в другом файле

extern int j;

// Определение функции

int f(int i){

return j+i;

}




При использовании материала, поставите ссылку на Студалл.Орг (0.099 сек.)