таким цветом Мои задачи выделены вот в конце каждой темы

advertisement
Мои задачи выделены вот
таким цветом в конце каждой темы
1
Все трудно до тех пор,
пока не станет легким
Т.Фуллер
Введение
Специалисты, работающие в сфере телекоммуникаций, должны обладать
определенным уровнем подготовки в области программирования.
Для решения поставленной
задачи специалисту часто приходится
использовать компьютер. Можно написать программу, реализующую алгоритм
решения поставленной задачи, на разных языках программирования, но в
любом случае этот язык должен быть понятным человеку. Поэтому были
созданы так называемые императивные языки высокого уровня ( Pascal,
C/C++, Ada, Java и др). Название «императивные» произошло от
латинского imperativus, что означает – повелительный. Императивный стиль
программирования предполагает, что компьютеру указывается что и в какой
последовательности он должен делать, чтобы от исходных данных прийти к
конечному результату. (Такие языки называют еще алгоритмическими или
процедурными языками).
Написанную человеком программу компьютер должен выполнить. Но
компьютер умеет исполнять только такие программы, которые написаны в
машинных кодах. Других языков компьютер не понимает. Поэтому написанную
на языке высокого уровня программу необходимо перевести на язык, понятный
процессору – в машинные коды. Специальные программы, осуществляющие
перевод
с языков высокого уровня в коды машины, называются
компиляторами. Для того чтобы программа стала исполняемым файлом
(файлом, имеющим расширение .exe), она должна быть обработана еще одной
специальной программой, называемой компоновщиком (редактор связей).
Именно на этапе компоновки будут подключены библиотеки функций, которые
хранятся в уже откомпилированном виде.
Этот процесс определяет путь прохождения программы в компьютере,
состоящий из нескольких этапов (см. рис.В1.). Исходный текст программы на
языке С++ пишется в редакторе, затем передается препроцессору, который в
соответствии с директивами, содержащимися в программе, присоединяет к
тексту программы текст заголовочных файлов, образуя так называемый полный
текст модуля с расширением .cpp. Этот текст поступает на вход компилятора,
который сначала выделяет лексемы, затем в соответствии с грамматикой языка
на основе этих лексем формирует выражения и операторы, ищет
синтаксические ошибки, если они есть, выдавая об этом соответствующие
сообщения. После исправления вами всех ошибок строит объектный модуль.
Объектный модуль поступает на компоновщик, который формирует
исполняемый модуль, работая в качестве редактора связей и подключая к
объектному модулю необходимые библиотеки функций. На выходе
компоновщика получаем исполняемый модуль, который и запускается на
исполнение.
2
Исходный текст модуля ( .cpp)
Включаемые файлы (.h)
Препроцессор
Полный текст модуля(.cpp)
Компилятор
Объектный код модуля (.obj)
Библиотечные файлы (.lib)
Компоновщик
Исполняемая программа (.exe)
Рис.В.1. Путь прохождения программы в компьютере
Для облегчения работы компилятор, компоновщик, а также редактор текста
программ, отладчик и другие средства объединяют в так называемые
интегрированные среды программирования (IDE
–
Integrated
Development
Environment). Различные среды программирования
создаются под различные платформы и имеют различный интерфейс.
Вам предлагается работать в интегрированной среде Net Beans IDE 6.8.
NetBeans
работает
на
многих
платформах,
например,
Unix,Linux,Windows,Mac OS X, Solaris. Легко устанавливается и
используется.
Именно в среде NetBeans вам предстоит научиться создавать программы на
языке С/С++. Эти программы должны иметь достаточно простую структуру,
определенную иерархическую упорядоченность, хорошо воспринимаемую при
чтении, возможность быстрой модификации, легкое сопровождение и высокую
надежность. Будем для этого использовать технологию создания программ,
которая носит название структурное программирование.
3
Особенностью структурного программирования является:
 модульность;
 использование базовых конструкций
ветвления и цикла;
 ограниченное
использование
переменных.
следования,
глобальных
Модульность позволяет свести к минимуму взаимовлияние логически
независимых частей программы и осуществить принцип сокрытия информации.
В С++ отдельным модулям будут соответствовать файлы с исходным текстом на
С++.
Использование структурного программирования на всех стадиях разработки
проекта позволяет улучшить визуальное восприятие программы, уменьшить
количество ошибок, сократить время разработки и облегчить возможности
модификации программ. Структурное программирование напрямую связано с
фон-неймановской архитектурой (управление потоками команд). Алгоритм в
явном виде (с помощью выполняемых операторов присваивания) определяет
сами действия и их последовательность. Концепция памяти - преобразование
исходного состояния памяти (значений переменных) в заключительное
состояние.
Машина фон-Неймана – это однородная, состоящая из ячеек память и
процессор, имеющий локальную память в виде регистров. Процессор может
загружать данные из памяти в регистры, выполнять арифметические и
логические операции над содержимым регистров и отсылать значения из
регистров в память.
ПК работает на ограниченном множестве операций, и программист
вынужден мыслить в терминах этого ограниченного множества. Программа для
таких машин – это последовательность команд, выполняющих перечисленные
операции вместе с дополнительным кодом управления, влияющим на выбор
очередной команды.
Учебное пособие посвящено изучению основ программирования на базе
языков C/C++. Будут рассмотрены возможности языка С++ в рамках
структурной парадигмы написания программ. Слово парадигма означает
стиль программирования.
Существует более 3000 языков программирования. Какое место среди других
языков программирования занимает язык С/С++?
С – универсальный язык, эффективный и удобный для решения самых
различных задач. Первоначально автором его был Денис Ритчи. Язык тесно
связан с системой UNIX,так как был в этой системе и разработан.
Однако язык не привязан к какой-либо системе, он универсален. Определение
современного языка С дано в соответствии со стандартом ANSI (American
National Standards Institute). В начале 80 годов Бьерн Страуструп
вместе с коллегами из AT&T Bell Laboratories разработал С++,
который является расширенной версией языка С с подключенными средствами
4
поддержки объектно-ориентированного программирования(ООП). В настоящее
время этот язык продолжает непрерывно развиваться. Это гибкий, мощный,
имеющий эффективный объектный код язык. С++ поддерживает как
структурное программирование, так и ООП. Стандартом языка С++ является
ISO/IEC
14882
«Standard
for
the
C++
Programming
Language»(1998) .
Без хорошего знания языка невозможно воспользоваться современными
технологиями разработки приложений в одной из операционных систем (ОС).
Без современных технологий разработки приложений вы не сможете гибко и
эффективно управлять средствами ввода данных и вывода результатов работы
ваших программ на экран монитора, то есть способом представления
информации.
5
Тема 1. Линейные вычислительные процессы
Теоретический материал
Язык программирования С++ .
Язык состоит из алфавита (перечень элементарных символов) и синтаксиса,
который определяет правила построения выражений из этих символов.
Минимальная единица языка - лексема. Она имеет самостоятельный смысл.
Выражение определяет правило вычисления некоторого значения.
Оператор задает законченное описание некоторого действия.
Символы, лексемы, выражения, операторы
Символы алфавита С++ :
 строчные и прописные латинские буквы и знак подчеркивания;
 арабские цифры от 0 до 9;
 cпециальные символы :
( )
[ ] { }
+
/
%
*
.
\
, : ; <
=
> &
#
^ |
! ?
‘
“
 пробелы, табуляция, переход на новую строку.
Лексемы языка:





идентификаторы;
ключевые слова (зарезервированные);
знаки операций;
константы;
разделители (скобки, точка, запятая, пробелы)
Идентификатор (имя программного объекта) - любая последовательность
прописных и строчных букв латинского алфавита, цифр, знака
подчеркивания, начинающаяся с буквы или знака подчеркивания.
Прописные и строчные буквы различаются (a и A – это разные
идентификаторы). Внутри имени не могут использоваться пробелы.
Ключевые слова - зарезервированные идентификаторы,
компилятором только в том смысле, который в них заложен.
auto
break
double
else
int
long
использующиеся
struct
switch
6
case
char
const
continue
default
do
enum
extern
float
for
goto
if
register
return
short
signed
sizeof
static
typedef
union
unsigned
void
virtual
while
и др.
Программист не может использовать ключевые слова для обозначения своих
пользовательских идентификаторов.
Знаки операций - один или более символов, определяющих действие над
операндами. Внутри знака операции пробелы не допускаются.
Операции бывают унарные (один операнд), бинарные (два операнда) и
тернарные (три операнда) по количеству участвующих в них операндов. Один и
тот же знак может интерпретироваться по-разному в зависимости от контекста.
Константы могут быть целыми, вещественными, символьными
(литерными),
стринговыми. Компилятор определяет принадлежность
константы к тому или иному типу по внешнему виду. Константа редставляет
фиксированную величину, которая не может быть изменена в программе.
Целые константы могут задаваться в десятичном, восьмеричном и
шестнадцатеричном форматах. В восьмеричном формате целая константа
начинается с 0, за которым следуют восьмеричные цифры (от 0 до 7) в
шестнадцатеричном формате целая константа начинается с 0x или 0X, за
которым следуют шестнадцатеричные цифры (от 0 до 9,A,B,C,D,E,F).
Вещественная константа в экспоненциальной форме представляется в виде
мантиссы и порядка. Мантисса записывается слева от знака экспоненты (E или
e), порядок – справа от знака. Пробелы внутри числа недопустимы, отделение
целой части числа от дробной выполняется с помощью точки, а не запятой
(0.35e-01, 3E7).
Символьная константа содержит один или два символа, заключенные в
апострофы (одинарные - 'A', '\n').
Строковая константа – это последовательность символов, заключенная в
кавычки (”Изучаем С/С++”).
Escape оследовательности (управляющие последовательности) начинаются с
обратной косой черты :
новая_строка \n (при выводе на экран или принтер переводит строку);
горизонтальная табуляция
\t;
вертикальная табуляция \v;
возврат каретки
\r;
сигнал звонок
\a;
знак вопроса
\?;
одиночная кавычка \’;
7
двойная кавычка
\”
Комментарии. В программе можно использовать комментарии, которые не
влияют на выполнение программы, но служат либо для пояснения текста
программы, либо для временного исключения блоков программы при отладке.
Договоримся каждую строку комментариев начинать с двух символов
“косая черта” - //.
Для временного исключения блоков при отладке программы будем заключать
блок с двух сторон символами /*
*/.
Концепция типов данных
Данные, обрабатываемые на ПК, относятся к разным типам и в зависимости
от этого по-разному хранятся и обрабатываются. Каждая константа,
переменная, результат вычисления выражения или функции всегда имеют
определенный тип.
Тип данных определяет:
 множество значений, которые могут принимать величины этого типа;
 внутреннее представление данных в памяти ПК;
 функции и операции, которые могут применяться к величинам этого типа
Программист для представления объектов своей задачи сам выбирает тип
каждой величины. Компилятор проводит проверку конструкций программы в
соответствии с указанными типами. Тип величины определяет машинные
команды, которые будут использоваться для обработки данных, объем
выделяемой компилятором памяти под тот или иной объект программы. От
выбранного типа зависит скорость вычислений на данном компьютере.
Есть несколько основных ( базовых) типов:
сhar - символьный ;
int- целый;
float - вещественный ;
double – вещественный с двойной точностью;
void - пустой, не имеющий значения.
Понятие ТИП – фундаментальное для всех языков программирования.
Структура программы
Программа состоит из функций, описаний и директив препроцессора. Одна из
функций является главной. Имя главной функции - main(). Выполнение
программы начинается с первого оператора этой функции. Завершение работы
этой функции означает завершение работы всей программы. Поэтому в любой
программе всегда имеется функция с именем main().Она может быть
единственной, если программа состоит из одной функции. Все остальные
функции программы имеют отличные от main имена.
8
Определение любой функции имеет вид:
тип имя_функции ([параметры]) {
Операторы тела функции
}
Параметры помещаются в круглые скобки, наличие квадратных скобок не есть
синтаксис языка, квадратные скобки просто означают, что внутри круглых
скобок параметры могут быть, но могут и не быть.
Примечания:
 если функция возвращает в точку своего вызова некоторое значение, но
тип возвращаемого значения не указан, то по умолчанию считается, что
этот тип- int.
 если функция не возвращает значение, то тип void(пустой);
 тело функции – это блок, который заключается в фигурные скобки;
 функции не могут быть вложенными;
 операторы функции заканчиваются точкой с запятой.
Структура программы
директивы препроцессора
описания
int main(){
операторы главной функции
}
int f1(){
операторы функции f1
}
int f2(){
операторы функции f2
}
и т.д.
Функции создаются в виде файлов, из которых потом образуется модуль.
Функция может иметь параметры, или не иметь их, но круглые скобки
обязательны. Именно наличие круглых скобок после имени позволяет
компилятору понять, что речь идет о функции.
Так как программа практически всегда использует в своей работе
стандартные функции, то компилятору надо объяснить, где эти функции искать,
поэтому любая программа, в которой содержатся вызовы стандартных функций,
должна начинаться с директивы препроцессора:
# include <имя заголовочного файла >
Директива # include (включить файл) может стоять в любом месте
программы, но обычно программа с нее начинается.
9
Потом заголовочный файл будет найден, и строка #include будет заменена на
все содержимое этого заголовочного файла. Эффект такой, как если бы вы
набрали содержимое заголовочного файла непосредственно в программе.
Каждая библиотечная функция, определенная стандартом языка Си++ имеет
прототип в соответствующем заголовочном файле. Прототип- это заголовок
функции. Именно по этому заголовку и будет проверена правильность вызова
вами этой функции и найдено определение соответствующей функции в
библиотеке.
Переменные и выражения
Выражения – это последовательность операндов, знаков операций и скобок.
Операнды – это переменные, константы или другие выражения. Выражение
определяет выполнение одной или нескольких операций по преобразованию
информации. Порядок интерпретации выражений определяется компилятором в
соответствии с их приоритетами (смотри таблицу приоритетов операций табл.1.1).
Переменная – это именованная область памяти, в которой хранятся данные
определенного типа. Значения переменной в ходе выполнения программы
можно менять. Перед использованием переменная должна быть
задекларирована:
int x;
//целая переменная x
float y;
// вещественная переменная y
char z;
//символьная переменная z
const int
k=5;
// для сравнения константа целого
//типа k должна быть инициализирована при объявлении, она не может
//изменить свое значение в процессе выполнения программы.
Тип объявляется при декларации переменных:
тип
список_переменных;
Например, int x,y,z;
//переменные x,y,z целого типа
double m, n; // переменные m, n типа double
Компилятор выделяет место в памяти компьютера в соответствии с
декларацией. Важное значение имеет то, в каком месте программы эта
декларация стоит (правило видимости - scope rules). В Си++ есть три
места, где переменная может быть объявлена. Если переменная объявлена вне
функций (в том числе и вне функции main),то она называется
глобальной(global) и видна из любого места программы (то есть может быть
использована везде). Переменная может быть объявлена внутри блока, в том
числе внутри тела функции, тогда она называется локальной(local) и
10
может использоваться только внутри этого блока. Переменная может быть
объявлена как формальный параметр функции, тогда переменная по сути
локальна и служит для передачи информации в эту функцию. Формальным
параметром называется параметр, стоящий в круглых скобках в заголовке
определения функции. Важно помнить следующие правила:
 две глобальные переменные не могут иметь одинаковые имена;
 локальная переменная одной функции может иметь такое же имя, как
локальная переменная другой функции (или формальный параметр
другой функции).
 две локальные переменные в одном блоке не могут иметь одинаковые
имена, и локальный параметр не может совпадать с формальным
параметром функции.
Пример использование глобальных, локальных и формальных параметров:
#include<stdio.h>
char ch; //глобальная переменная ch
main() {
int n;
//локальная переменная n
printf(“введи символ ch:”);
scanf(“%c”,&ch);//использование глобальной переменной
printf(..);
. . . . . .
}
funct(int m) {
// формальный параметр
int h;
//локальная переменная h
printf(“%c\n”,ch); //использование глобальной
// переменной ch
. . . . . .
}
Примечание. Еще раз вернитесь к записи const int k=5 на предыдущей
странице. Модификатор const показывает, что значение переменной целого
типа k изменить нельзя. Такую переменную называют именованной
константой или просто константой.
11
Операции. Приоритеты операций и порядок их выполнения
Таблица 1.1
Приоритет Название оператора Символ
Порядок выполнения
операции (ассоциативность)
Обращение
к
( )
функции
1
(высший)
2
3
4
5
6
7
8
9
Выделение элемента
массива
Инкремент
постфиксный
Декремент
постфиксный
Логическое
отрицание
Инкремент
префиксный
Декремент
префиксный
Унарное сложение
Унарное вычитание
Разименование
Взятие адреса
Приведение типа
Размер объекта
Умножение
Деление
Вычисление остатка
от деления
Сложение бинарное
Вычитание бинарное
Меньше
Не меньше
Больше
Не больше
Логическое И
Логическое ИЛИ
Операция условия
Присваивание
[ ]
++
левосторонняя
-!
++
-+
*
&
(type)
sizeof
*
/
%
+
<
>=
>
<=
&&
| |
?:
=
+=
-=
*=
%=
/=
правосторонняя
левосторонняя
правосторонняя
12
Операции задают действия, которые необходимо выполнить.
Операции,
которые распознает компилятор, упорядочены по приоритету. Приоритет
определяет порядок интерпретации выражений (правила предшествования).
Приоритет можно изменить с помощью круглых скобок. При наличии
нескольких операций одного приоритета компилятор учитывает еще и порядок
выполнения операций, например, слева направо или справа налево. Будем
называть это левосторонней и правосторонней ассоциативностью (смотри
таблицу 1.1). Если скобок несколько, то первым будет выполняться выражение в
“самых глубоких” скобках.
Арифметические операции
+
*
/
%
++
--
сложение
вычитание
умножение
деление
деление по модулю
инкремент ( увеличение операнда на единицу)
декремент (уменьшение операнда на единицу)
Операции инкремента и декремента могут применяться только к
переменным.
(Например, запись (a+b)++ недопустима, так как (a+b)является
выражением))
Существуют две формы записи:
префиксная ++a , в которой сначала выполняется увеличение операнда на
единицу, а затем это увеличенное значение используется в выражении;
постфиксная a++, в которой в выражении используется исходное
значение операнда и только потом это значение увеличивается на единицу.
Пример
#include<stdio.h>
int main() {
int m=0,n=1,p;
p=m++;
printf("m=%d p=%d\n",m,p);
p=++m;
printf("m=%d p=%d\n",m,p);
p=++n;
printf("n=%d p=%d\n\n",n,p);
//---------------------------int x=10, y=10;
x++;
++y;
printf("x=%d y=%d\n",x,y);
printf("x=%d y=%d\n\n",x++,++y);
13
//-----------------------------------int a=5,b=2;
printf("%d\n",a/b);
printf("%d\n",b-a);
printf("%d\n",a%b);
return 0;
}
Протокол работы
m=1
p=0
m=2
p=2
n=2
p=2
x=11
x=11
y=11
y=12
2
-3
1
Внимательно ознакомьтесь с примером. Работа с переменными m, n, и p ,
а также с переменными x и y демонстрирует различия в использовании
постфиксного и префиксного инкрементов.
Работа с переменными a и b демонстрирует использование арифметических
операций
над целочисленными операндами – целочисленное деление,
вычитание и получение остатка от целочисленного деления.
Операции отношений
>
>=
<
<=
==
! =
больше
не меньше
меньше
не больше
равно
не равно
Операции отношений используются в условных выражениях, в которых
проводится проверка некоторого условия на истинность (true) или
ложность(false).
Логические операции
&&
||
!
(and)
(or)
(not)
логическое И
логическое ИЛИ
отрицание
14
Операции присваивания
=
простое присваивание
дополнительные операции присваивания:
+=
- =
*=
/ =
% =
Пример
m - =20
(m=m-20)
m * =20
(m=m*20)
m / =10
(m=m/10)
m % =10
(m=m%10)
m+=50
(m=m+50) - аддитивная операция присваивания, в результате
выполнения которой величина, стоящая справа от знака присваивания,
прибавляется к значению переменной, стоящей слева от знака присваивания.
Операция m+=50 выполняется быстрее, чем m=m+50.
Примечание . Разрешается использовать многократное присваивание:
a=b=c=x*y.
В этом случае работает правосторонняя ассоциативность(x*y->c=x*y>b=c->a=b). В левой части (a) должно стоять выражение (может быть
просто переменная), которому можно присвоить значение, но не может стоять
константа.
Математические функции
При решении задач могут понадобиться стандартные математические
функции. Прототипы таких функций находятся в заголовочном файле math.h.
Прототипы некоторых математических функций приведены в таблице 1.2
Таблица 1.2
Имя
функции
abs
прототип
ceil
double
num);
double
num);
cos
действие
Int abs(int num);
Возвращает
числа.(<stdlib.h>)
ceil(double Округляет вверх
модуль
cos(double Вычисляет косинус
15
exp
fabs
floor
log
log10
pow
sin
sqrt
tan
double
exp(double
num)
double
fabs(double
num)
double floor(doudle
num)
double
log(double
num)
double log10(double
num)
double
pow(double
base,double x)
double
sin(double
num)
double
sqrt(double
num)
double
tan(double
num)
Возвращает степень числа
e
Возвращает модуль числа
Округляет вниз
Вычисляет
натуральный
логарифм
Вычисляет
логарифм
по
основанию 10
Возводит число в степень
Вычисляет синус
Вычисляет
корень
Возвращает
аргумента
квадратный
тангенс
Ввод/вывод информации
Форматированный ввод/вывод
Для элементарного ввода-вывода информации используются библиотечные
функции scanf() и printf(). Эти функции позволяют осуществить
форматированный ввод-вывод. За одно обращение к этим функциям можно
обработать несколько элементов, при этом функции способны выполнить
преобразования данных. В качестве первых аргументов эти функции имеют
форматную строку, которая задает способ преобразования данных.
Функция scanf() принимает с клавиатуры все символы до нажатия
клавиши ‘Enter’ и помещает их в буфер. Форматная строка определит способ
преобразования введенных символов в соответствии с заданной
спецификацией. Преобразованное число будет помещено по адресу переменной.
Функция printf() выдает на экран монитора всю форматную строку, в
которой знак % и все символы после него, включая тип переменной, будут
заменены символами выводимой информации. Символы после % до первого
разделителя рассматриваются как спецификация преобразования значения
выводимой переменной.
Между знаком % и типом
переменной могут стоять некоторые
модификаторы, которые позволяют изменить положение выводимого числа в
пределах выделенного поля, указать систему счисления для целых типов ( так
называемые флаги), определить ширину поля вывода и точность числа.
Для переменных типа int будем использовать в формате буквы d и i ,
которые для функции printf() преобразуют аргумент к десятичному виду.
Для переменных типа float – буквы f , e , g, которые рассматривают
16
аргумент как переменные типа float. Спецификатор формата g применяется для
вывода вещественных чисел в широком диапазоне. При этом число выводится
либо в формате f, либо в формате e(с порядком), в зависимости от того, какой
из них получится короче.
Возьмем маленькую программу на Си:
#include<stdio.h>
int main() {
int year;
printf(“Enter year:\n”);
scanf(“%d”,&year);
printf(“You enterd %d year\n”,year);
return 0;
}
Протокол работы
Enter year:
2012
<Enter>
you enterd 2012 year
Примечания:
Каждый оператор заканчивается точкой с запятой.
Прежде чем запустить программу, ее надо сохранить, чтобы не “зависнуть”.
Анализ программы
<stdio.h> -заголовочный файл, в котором хранятся прототипы функций
ввода-вывода. Например, форматный вывод printf()имеет прототип:
int printf(char*format,arg1,arg2,….)
Первый вызов функции printf(“Enter year”) содержит только
форматную строку с отсутствующей в ней спецификацией преобразования. На
экран будет выведено содержимое всей форматной строки – интерфейсная
подсказка, необходимая для того, чтобы пользователь знал, что ему сейчас
необходимо сделать. Пользователю предлагают ввести год.
Далее следует вызов функции scanf(“%d”,&year), с помощью которой
пользователь введет с клавиатуры целое число, соответствующее году,
например, 2012. Эта функция имеет два аргумента. Первый аргумент –
форматная строка “%d”, которая говорит о том, что вводиться с клавиатуры
будет одно число, и это число целого типа. Второй аргумент &year назначает
17
адрес оперативной памяти, куда будет помещено введенное пользователем
число c учетом указанного им формата.
Вызов последней функции printf(“You enterd %d year\n”, year)
выводит результат работы данной программы. Функция имеет два аргумента.
Первый аргумент – форматная строка. До и после спецификации формата %d
стоят символы, которые будут выведены на экран в том виде, в каком вы их
видите, кроме двух последних символов \n, которые означают перевод курсора
на новую строку.
Второй аргумент функции year .Значение year должно быть в виде
изображения выведено на экран в формате, заданным
спецификацией
преобразования %d . Это изображение числа будет помещено вместо
спецификации
преобразования. Таким образом функция printf()
преобразует, форматирует и печатает свои аргументы в стандартном выводе под
управлением формата.
Еще одна программа:
#include<stdio.h>
// деление целых чисел
- комментарий
int main() {
int a,b,c;
printf(“input a,b,\n”);
scanf(“%d”,&a);
scanf(“%d”,&b);
c=a/b;
printf(“result=%d\n”,c);
// вывод в десятичном формате
printf(“result=%o\n”,c); //вывод в восьмеричном формате
printf(“result=%x\n”,c);//вывод в шестнадцатеричном формате
printf(“result=%d\n”,a%b);//остаток от целочисленного деления
return 0;
}
Протокол работы
nput a,b
22
<enter>
2
<enter>
result=11
result=13
result=b
//десятичный формат
//восьмеричный формат
//шестнадцатеричный формат
Примечание. Еще раз обратите внимание на унарный оператор &, который
выдает адрес объекта.
(Инструкция p=&c присваивает адрес ячейки с переменной p—говорят, что
18
p указывает на с, или p ссылается на с).
Оператор & применяется только к объектам, расположенным в памяти: к
переменным и элементам массивов. Его операндом не может быть ни
выражение, ни константа, ни регистровая переменная.
Тип данных указывает компилятору, сколько байтов надо выделить для
размещения объекта. Чтобы проверить объем памяти, выделяемый объекту
данного типа можно написать программу, использующую операцию sizeof.
Значением этой операции является размер любого объекта или спецификации
типа, выраженной в байтах.
Пример:
#include<stdio.h>
void main() {
printf(“тип\tразмер в байтах\n\n”);
printf(“int\t\t\t%d\n”,sizeof(int));
printf(“char\t\t\t%d\n”,sizeof(char));
printf(“float\t\t\t%d\n”,sizeof(float));
}
Протокол работы
тип
int
char
float
размер в байтах
2
1
4
Замечание. Размер в байтах зависит от конкретной машины, на которой вы
работаете.
Функции printf() и scanf() унаследованы из библиотеки языка C,
поэтому такой ввод/вывод называется вводом/выводом в стиле С. Однако
реализовать ввод/вывод можно также с помощью потоков С++. Такой способ
называется вводом-выводом в стиле С++. Смешивать эти два стиля в одной
программе без специальной синхронизации нельзя, то есть можно пользоваться
либо одним стилем, либо другим. У каждого из этих способов есть свои
преимущества.
Существует большое количество программ, написанных в
стиле С, а потом перенесенных на С++, и разработчику придется с ними
сталкиваться.
Стиль С++ удобен для простых случаев ввода/вывода, не требующих
форматирования. Хорошо, если вы умеете пользоваться и тем, и другим
стилями. Функции scanf() и printf() удобно использовать в программах,
в которых вы хотите получить тщательно отформатированные результаты. В
19
других случаях для ввода/вывода можно использовать объекты cin и cout
cсоответствующих классов.
Потоковый ввод/вывод
Язык С++ добавляет новые элементы языка – объекты. Например, такими
объектами являются объекты для ввода/вывода данных cin и cout. Это
позволяет нам вместо форматированного ввода/вывода, использующего
функции scanf() и printf() из библиотеки С, доступных при
подключении заголовочного файла
stdio.h, использовать потоки
библиотеки С++, доступной при подключении заголовочного файла
iostream.h.
( При
использовании библиотек нельзя обойтись без
заголовочных файлов. Это единственный способ предоставления программе
информации о функциях, хранящихся в библиотеках.)
В библиотеке iostream представлена объектно-ориентированная версия
потоков. Поток служит для переноса данных от источника к приемнику. Обмен
с потоком производится через специальную область ОП – буфер. Это помогает
значительно повысить скорость передачи информации. Поток всегда относится
к какому-либо классу. Для поддержки потоков библиотека С++ содержит
иерархию классов, построенных на основе двух базовых классов – ios и
streambuf. Потоки, связанные с клавиатурой и экраном, называются
стандартными. Стандартному потоку ввода соответствует класс istream(
класс входных потоков, по умолчанию связанный с клавиатурой). Стандартному
потоку вывода соответствует класс ostream(класс выходных потоков, по
умолчанию связанный с экраном монитора). Есть еще класс iostream - класс
двунаправленных потоков ввода/вывода.
Класс является абстрактным типом данных и определяется пользователем.
Конкретные переменные типа класс называются объектами. Объекты будут
изучаться нами в курсе «Современные методы программирования», а пока
будем просто использовать стандартные объекты cin и cout и операции
чтения с клавиатуры >> и вывода на экран <<.
объект Класс Описание
cin
istream Связывает
с
клавиатурой
(стандартный
буферизованный
ввод)
cout
ostream Связывает
с
экраном
(стандартный
буферизованный
вывод)
20
Эти объекты создаются при включении в программу заголовочного файла
<iostream.h>. При этом программе автоматически становятся доступными
объекты cin и cout, а также связанные с ними средства ввода/вывода: >> ввод данных с клавиатуры, << - вывод на экран изображения полученного
результата. В потоковых классах форматирование выполняется по умолчанию
или с помощью манипуляторов (флагов) – функций, которые можно включать в
цепочку операций помещения и извлечения для форматирования данных.
Например, setw(5) устанавливает для очередного выводимого значения
ширину поля в 5 позиций; setprecision(5) устанавливает точность
вывода числа на экране. Это означает максимальное количество цифр в дробной
части для вещественных чисел в форме с фиксированной точкой, или общее
количество значащих цифр для чисел в форме с мантиссой и порядком. Для
использования манипуляторов надо подключить заголовочный файл
iomanip.h.
Например, пусть величина x -целого типа.
Выведем на экран значение величины x,устанавливая максимальную ширину
поля вывода в 5 позиций. Endl означает перевод курсора на новую строку:
cout<<setw(5)<<x<<endl;
Замечание:
-при использовании стандартной библиотеки С++(ISO/IEC 14882)
заголовочные файлы указываются без расширения .h, но используется
директива using
namespace
std; (использование стандартного
пространства имен), так как все имена в стандартной версии библиотек
принадлежат пространству std. В случае использования более старой версии
заголовочный файл пишется с расширением h, а using namespace std не
используется.
21
Ввод-вывод информации
Форматированный в стиле С
#include<stdio.h>
scanf()
printf()
Потоковый в стиле С++
#include<iostream.h>
cin >>
cout <<
Рис. 1.2. Различные стили ввода – вывода информации
При потоковом вводе-выводе используется объект cin для связи с
клавиатурой, операция ввода с клавиатуры >>; объект cout используется для
связи с монитором, операция вывода на экран <<.
Рассмотрим маленькую программу:
#include<iostream.h>
int main() {
int x,y,z;
cout<<”input x=”;
cin>>x;
cout<<”input y=”;
cin>>y;
z=x+y;
cout<<”x=”<<x<<”
y=”<<y<<”
return 0;
}
z=”<<z<<endl;
В программе надо вычислить сумму двух чисел x и y, введенных с
клавиатуры, и результат поместить в переменную z.
Первая строка программы - это директива препроцессора, в соответствии с
которой к исходному тексту программы подключается заголовочный файл
<iostream.h>, который содержит описания элементов стандартной
библиотеки, с помощью которых будет выполнен ввод/вывод информации в
стиле С++. В этом файле описаны стандартные объекты cin для ввода с
клавиатуры и cout для вывода на экран, а также операции ввода с клавиатуры
>> и вывода на экран <<.
Программа состоит из единственной функции и поэтому должна иметь имя
22
main. Перед этим именем стоит тип int. Это значит, что функция main
должна возвратить во внешнюю среду целочисленное значение. Так как наша
функция ничего не возвращает во внешнюю среду, то в конце программы стоит
return 0. После имени main стоят пустые скобки. Обычно в скобках
пишется список передаваемых функции параметров, но в данном случае таких
параметров нет, но пустые скобки обязательны, чтобы компилятор понимал, что
речь идет именно о функции. Сразу после фигурной скобки, открывающей тело
функции, следует строка, в которой декларируются три переменные x,y,z –
все три целого типа. В соответствии с декларацией на этапе компиляции в
оперативной памяти будет выделено место под все три переменные.
Задекларировав переменные, вы не только позволили компилятору выделить
место в памяти, но указали ему, как необходимо интерпретировать значения
этих
переменных (представление внутри компьютера), а также дали
возможность компилятору проверять правильность выбираемых вами действий
с этими величинами.
Строки
cout<<”input x=” и
cout<<”input y=” являются
интерфейсными подсказками для того, чтобы пользователь знал, что от него
требуется ввести с клавиатуры в данный момент и что будет выведено на экран
монитора. Стандартный объект cout связывает нас с экраном. Именно этому
объекту мы с помощью операции <<
передаем то, что хотим вывести на
экран. При выводе все символы внутри кавычек выводятся без изменений. Но
при выводе значений переменных будет выполнено преобразование из
внутреннего представления в изображение этих чисел на экране.
Два оператора cin>>x и cin>>y выполняют ввод с клавиатуры, используя
стандартный объект cin и операцию чтения >>. В процессе ввода оба
вводимых числа преобразуются из последовательности символов, набранных на
клавиатуре, во внутренне представление целых чисел и помещаются в ячейки
памяти, зарезервированные на этапе компиляции для переменных x и y.
Обратите внимание, тело функции обрамлено фигурными скобками.
Общие выводы и рекомендации
 Хороший стиль программирования требует, чтобы тело функции,
заключенное в фигурные скобки, писалось с отступом от заголовка
функции.
 Тип переменных программист выбирает самостоятельно. При этом он
должен учитывать и диапазон изменения переменных и требуемую
точность представления данных.
 Имена переменным выбирает также сам программист. Желательно
давать эти имена осмысленно, исходя из их назначения.
 Не забывайте об интерфейсных подсказках. Это корректно по
отношению к пользователям. Они не должны сидеть перед пустым
экраном.
 До запуска программы подготовьте тесты с вашими исходными
данными и ожидаемыми результатами. Иначе трудно будет определить,
23
насколько корректно работает ваша программа. Нелишним будет
проверить реакцию вашей программы на неверно введенные исходные
данные.
 Старайтесь как можно меньше использовать глобальные переменные.
Чем меньше будет область действия переменной, тем легче отследить
возможные ошибки.
 При написании программы прибегайте к отладке программы, используя
те средства отладки, которые предоставляет вам конкретная среда
разработки.
Пример линейной программы
Постановка задачи
4 sin 2 x  3
Вычислить значения функций у=
и
2
1
sin 2 (a(3 y 2  ))  11,75
3
z=
1
sin 2 (a(3 y 2  ))  b
3
для рабочего набора данных x=2,2; a=3,2; b=6,8.
Анализ задачи
Для решения поставленной задачи необходимо ввести в компьютер рабочий
набор данных. В качестве исходных данных имеем три вещественных числа:
x,a,b. Результатом решения задачи являются два других вещественных числа
–значения функций y и z.
Текст программы
#include<stdio.h>
#include<math.h>
int main() {
float x,y,z,a,b;
printf("enter x,a,b:\n");
scanf("%f%f%f",&x,&a,&b);
y=(4*sin(x)*sin(x)+3)/2;
float m=sin(a*(3*y*y-1.0/3));
z=(m*m+11.75)/(m*m+b);
printf("y=%f z=%f",y,z);
return 0;
}
//(1)
//(2)
//(3)
//(4)
//(5)
//(6)
//(7)
//(8)
//(9)
//(10)
//(11)
Протокол работы.
enter x,a,b:
2.2 3.2 6.8
y=2.807333
z=1.676159
Перед постановкой программы на счет, провели контрольный расчет в одной
из математических систем – в системах Maxima , MathCAD или любой другой.
24
Контрольный расчет дал результат: y=2.807333, z=1.676159.
подтвердила правильность работы программы.
Проверка
Пояснения к программе
Первые две строки начинаются со знака #. Следом за этим знаком всегда
идут директивы препоцессора. Директива #include<имя файла> вставляет
содержимое файла в точку исходного файла, где она записана. Файлы stdio.h
и math.h называются заголовочными файлами и обычно имеют расширение
h(от слова header).
Под номером (3) стоит строка, являющаяся заголовком функции. Так как в
данной программе у нас всего одна функция, то эта функция обязана иметь имя
main, ибо работа любой программы начинается с первого оператора функции
main.Заголовок состоит из имени main, типа возвращаемого этой функцией
результата int и круглых скобок. В круглых скобках обычно пишутся
параметры, связывающие функцию с внешним миром. Но даже если в круглых
скобках нет никаких параметров, все равно сами круглые скобки обязательны,
ибо круглые скобки говорят компилятору о том, что речь идет именно о
функции, а не о какой-либо другой конструкции языка.
Стоящие следом фигурные скобки открывают тело функции.
Тело функции состоит из операторов. Строка (4) декларирует пять переменных
вещественного типа. Это необходимо компилятору для выделения достаточного
количества оперативной памяти для хранения значений этих переменных.
Именно для этого в декларации указывается тип переменных. Отвечает за это
сам программист, решающий конкретную задачу.
Строка(5) является интерфейсной подсказкой, которая говорит пользователю,
какие именно переменные и в какой последовательности надо ввести в
компьютер. Вывод подсказки на экран производится с помощью функции
printf().
Строка (6) работает с функцией scanf(), которая ожидает ввода с
клавиатуры переменных x,a, b. Они будут введены в формате вещественных
чисел (%f) и отправлены по адресам(&),по которым на этапе компиляции
было выделено место для переменных x,a,b в соответствии с декларацией.
Строки (7), (8) и (9) с помощью оператора присваивания вычисляют выражения
и помещают результаты вычислений в оперативную память в переменные y, m
и z. Обратите внимание на строку (8). Мы ввели некоторую промежуточную
переменную m , причем задекларировали ее прямо в том месте программы, где
она нам понадобилась (float m). Язык С++ это позволяет. С помощью
переменной m мы упростили слишком длинное выражение, определяющее
функцию z(y,a,b):
m=sin(a*(3*y2-1/3)).Обратите внимание на величину 1/3. Если бы мы
написали ее именно так, то в результате деления получили бы ноль, так как
результат должен быть целочисленным при делении целого на целое.
Округления при этом не произошло бы, дробная часть была бы отброшена. В
конечном итоге мы получили бы неправильный результат, поэтому при
написании выражения надо использовать запись 1.0/3, или 1.0/3.0,
25
или 1/3.0.
Строка (10) выводит на экран изображения содержимого ячеек памяти y и z
в формате вещественных чисел.
Последняя строка (11) нужна потому, что функция должна вернуть во
внешний мир величину целого типа, но наша функция ничего во внешний мир
не возвращает, поэтому мы пишем return 0.
Последняя фигурная скобка говорит о том, что тело функции закрывается.
Общие требования к
написанию отчетов:
выполнению
лабораторных
работ
и
 Разработка любой программы должна начинаться с постановки
задачи;
 Поставленная задача должна быть проанализирована на предмет
использования в программе тех или иных структур данных. Это
необходимо для представления входных, промежуточных и выходных
данных; на данном этапе разработчик должен учесть и размер данных,
и необходимую точность, и быстродействие;
 Следующий этап – определение общей структуры программы и
возможность разбиения задачи
на функционально законченные
подзадачи меньшей сложности. Здесь необходимо учесть способы
взаимодействия таких подзадач, то есть те интерфейсы, которые для
этого будут использоваться. Подзадачи могут решать разные
исполнители. Каждый из них должен знать, что он получит через
интерфейс от других исполнителей и что он в результате решения
своей части задачи должен передать им.
 На данном этапе обучения обязательное использование структурного
программирования (то есть использование всего того, о чем
говорилось в теоретическом материале).
1. Программа должна быть написана на языке C++, который мы
используем в рамках структурной парадигмы.
2. Контрольные расчеты должны быть выполнены до запуска
программы (на калькуляторе, в системе Maxima,в системе
MathCAD или любым другим способом); контрольные расчеты
предполагают набор некоторых проверочных исходных данных и
получение соответствующих этим наборам результатов. При тех
же исходных данных результаты работы программы должны
совпасть с контрольными расчетами.
3. Ввод/вывод исходных данных и результатов в работах с
линейными, разветвляющимися и циклическими программами
должен производиться в стиле С; в работах с массивами – в
стиле С++.
4. Обработка данных в соответствии с заданием;
26
5. Отладка
программы,
то
есть
процесс
исправления
синтаксических ошибок.
6. Тестирование, то есть проверка правильности работы программы
для всех ветвей алгоритма. Желательно проверить, как
программа реагирует на ошибочные исходные данные.
Варианты заданий к работе 1
Первая работа посвящена линейным вычислительным процессам. Такие
процессы реализуются с помощью простых программ, в которых все операторы
выполняются линейно, один за другим сверху вниз( слева направо).
Варианты заданий предлагают решить задачу вычисления и вывода значений
функций y=f1(x) и z=f2(y,a,b). Варианты заданий представлены в
таблице 1.3
Таблица 1.3
N
Функция
Функция
Рабочий набор
y  f1 ( x)
z  f 2 ( y, a, b)
x
a
b
3,7
-2
8,1
3
12
x  8,3
x  0,3
4  y 2  sin x  a
sin x  y 2  0,2b
27
Тема 2. Разветвляющиеся вычислительные процессы
Теоретический материал
Базовые конструкции структурного программирования
Программу для решения задач любой сложности можно составить, используя
только три базовые конструкции - линейную, разветвления и циклы. В теории
программирования это математически строго доказано. Особенность этих
конструкций состоит в том, что они имеют один вход и один выход, поэтому эти
конструкции могут вкладываться друг в друга произвольным образом.
линейная
цикл
разветвление
Рис.2.1 Базовые конструкции структурного программирования
Операторы управления вычислительным процессом
В работе 1 мы познакомились с линейной программой, в которой операторы
выполнялись последовательно один за другим. Операторы управления
вычислительным процессом позволяют нарушить линейный ход программы и
выполнить ветвление в зависимости от исходных данных, или циклическое
повторение одного или нескольких операторов, а также передачу управления в
нужное место программы.
Операторы управления, как и другие операторы программы, могут быть
простыми и составными.
Простой оператор- это оператор, не содержащий другие операторы.
Последним символом в каждом операторе является символ точка с запятой.
Есть понятие – пустой оператор. Пустой оператор состоит из единственного
символа – точка с запятой.
Составной оператор
или БЛОК - это любая совокупность простых
операторов, заключенных в фигурные скобки.
Управляющие операторы, реализующие разветвляющиеся алгоритмы
28
Для выполнения ветвления применяются операторы if
также операция условия ?:.
и switch, а
Условный оператор if.
Условное
выражение
оператор
Условное
выражение
Оператор1
Оператор2
Рис.2.2. Структурная схема условного оператора
Оператор if используется для разветвления, то есть для выбора возможного
продолжения вычислительного процесса.
(1)
if (выражение)
(2)
оператор;
if (выражение) оператор 1;
else
оператор 2;
Алгоритм работы
Вычисляется выражение. (Результат вычисления может иметь
арифметический тип или тип указателя.)
Если результат вычисления отличен от нуля (true), то выполняется
<оператор> или <оператор1>.
Если результат равен нулю (false), то выполняется < оператор 2>.
Для случая (1) <оператор > пропускается, и управление передается на
следующий после if оператор.
Операторы <оператор1> и <оператор2> сами могут быть операторами if,
образуя вложенный if. Компилятор интерпретирует вложенные if,сопоставляя
каждое из ключевых слов else с последним встретившимся словом if, не
имеющим своего else. Соответствие ищется в пределах блока, в котором
находится данный if. Внутренние и внешние блоки при этом не
рассматриваются. Если соответствие для if не найдено, компилятор полагает,
что if не имеет ветви else.
29
Пример
Ввести с клавиатуры два целых числа и найти максимальное.
#include<stdio.h>
int main() {
int x,y,max;
printf("enter x=");
scanf("%d",&x);
printf("enter y=");
scanf("%d",&y);
if(x>y)
max=x;
else
max=y;
printf("x=%d y=%d max=%d\n",x,y,max);
return 0;
}
Протокол работы
enter x=5
enter y=8
x=5 y=8 max=8
enter x=8
enter y=5
x=8 y=5 max=8
Работа оператора if начинается с проверки условия в круглых скобках
(if x > y). Если вы ввели x=8, а y=5, то условие оказалось выполненным и,
следовательно будет выполнен оператор, стоящий в веточке if, а именно,
max=x, то есть 8. В противном случае сработает веточка else и будет
выполнен оператор max=y.
Точка с запятой является заключительным символом любого оператора (а не
разделителем), поэтому точка с запятой ставится и после max=x; и после
max=y; .
Операция условия ?:
Кроме рассмотренного оператора if
есть операция условия ? :.
условное_ выражение ? TRUE_оператор : FALSE_оператор;
При выполнении операции условия сначала вычисляется значение условного_
выражения. Если результат истина(true) , то результатом всей операции
является результат выражения TRUE_оператор, если – ложь(false) ,
результат выражения FALSE_оператор.
30
Рассмотренная выше программа могла бы использовать вместо оператора if
эту операцию условия, и тогда программа нахождения максимального числа
выглядела бы так:
#include<stdio.h>
int main() {
int x,y,max;
printf("enter x=");
scanf("%d",&x);
printf("enter y=");
scanf("%d",&y);
max=(x>y)? x:y;
printf("x=%d y=%d max=%d\n",x,y,max);
return 0;
}
протокол работы
enter x=8
entery=5
x=8 y=5 max=8
Если (x>y) ,то max=x, иначе max=y.
Результаты получили точно такие же, как и при использовании оператора if ,
но запись получилась проще и нагляднее.
Рассмотрим еще один фрагмент:
int x=1,y=1;
if(x==1)
if(y==1)
printf(“x равно 1 и y равно 1”);
else
printf(“x не равно 1”);
Строка “x не равно 1” будет выводиться тогда, когда значение x на самом
деле равно 1. Ошибка связана с тем, что компилятор сопоставляет if с
ближайшим else.
Для исправления ошибки надо ввести {}:
int x=1, y=1;
if(x==1)
{if(y==1)
printf(“x равно 1 и y равно 1”);
}
else
printf(“x не равно 1”);
31
Операторы switch и break
Оператор switch используется для выбора одного варианта из многих.
(вместо нечеткого if-else).
switch(выражение-cелектор)
{
case constant_1: < оператор1>;[break;]
case constant_2: <оператор2>; [break;]
. . . . . . . . . . . . .
. . . . . . . . . . . . . .
case constant_N: <операторN>;[break;]
default: <оператор N+1;>
}
Сначала вычисляется значение выражения - селектора, которое сравнивается
со значением констант (или константных выражений). Тип значения
выражения-селектора –целый: char, int.
Выполняются только те операторы, которые помечены меткойconstant,совпадающей с селектором. Break дает возможность осуществить
выход из оператора switch. Если break будет отсутствовать, то будут
выполнены все операторы, следующие после метки, совпавшей с селектором,
включая default.Ветка default может отсутствовать. Если ни одна из
меток-констант не совпадает с селектором,
то выполнится оператор,
следующий за default.
Пример
программы,
реализующей
мини-калькулятор,
производить сложение, вычитание, умножение и деление.
#include<iostream.h>
int main() {
float a,b,f,res;
char op;
cout<<"\n enter a=";
cin>>a;
cout<<"\n enter op="; cin>>op;
cout<<"\n enter b=";
cin>>b;
f=1;
switch(op) {
case'+': res=a+b; break;
case'-': res=a-b; break;
case'*': res=a*b; break;
case'/': res=a/b; break;
default: cout<<"try again\n"; f=0;
}
if(f) cout<<"\nresult="<<res<<endl;
return 0;
способный
32
}
Протокол работы
enter a=6.6
enter op=/
enter b=2.0
result=3.3
Оператор switch более нагляден, и его применяют, если в программе более 2-3
разветвлений. Выражение, по которому осуществляется переход на ту или иную
ветвь op, является целочисленным.
Пример разветвляющейся программы.
Решить на компьютере задачу вычисления значений функции y=f(x),где
27  (x - 3) 3
 3
x
y=  x
 2
 sin x
 2
при
при
при
при
x  3,
3  x  1,
1  x  0,
x 
. 0.
Рабочий набор x=13
Текст программы
#include<stdio.h>
#include<math.h>
int main() {
float x,y,z;
printf("\n x=");
scanf("%f",&x);
if(x>3)
y=27+pow((x-3),3);
else
if (x>1)
y=pow(x,3);
else
if(x>0)
y=x;
else
y=sin(x)*sin(x)/2;
printf("\n y=%f при x=%f",y,x);
return 0 ;
33
}
Выполнить вариант при x=13 Контрольные расчеты провести для всех ветвей
алгоритма. В зависимости от введенного значения x, будет работать та или
иная веточка алгоритма.
Правильность работы программы должна быть
проверена для каждой веточки алгоритма
Контр расчет:
x=5.0
x=2.0
x=0.8
x=-3.1415
x=13
y=35.000000 (x>3)
y=8.000000
(x>1)
y=0.000000
(x>0)
y=0.000000 (x<0)
y=1027.000000
(при заданном рабочем значении x=13)
Примечание. При проверке условий нельзя использовать двухсторонние
неравенства, то есть x<y<z.Синтаксически ошибки нет, и компилятор не
выдаст сообщения. Но что же произойдет при вычислениях? Операции одного
приоритета выполняются слева – направо, поэтому сначала выполнится x<y и
сформируется результат в виде true или false. Далее произойдет сравнение
true<z или false <z.Значения true и false преобразуются соответственно в 1 и 0
того же типа, что и z. Полученный результат вряд ли отвечает нашим
ожиданиям.
операторы, следующие после метки, совпавшей с селектором,
включая default.Ветка default может отсутствовать. Если
ни одна из меток-констант не совпадает с селектором, то
выполнится оператор, следующий за default.
Варианты заданий к работе 2
Необходимо написать программу для решения задачи вычисления некоторой
функции y=f(x) , заданной различными формулами на различных участках ее
определения. Используйте различные формы условных операторов в
соответствии с теоретическим материалом, описанным выше
Таблица 2.1
34
Номер
варианта
Функция y = f(x)
Рабочий набор
данных
X
12
x  (| x | 12) 2

 sin(x)  3
 cos(x)  15


2
 x (x  36)

x 8
при
x  8,
при  8  x  7,
7,6
при
7  x  10,
при
x  10
Общие требования к выполнению лабораторных работ и оформлению отчетов
приведены в работе 1.
35
Тема 3: Циклические вычислительные процессы
Теоретический материал
Виды циклов
Циклы служат для многократного повторения одного или нескольких
операторов. Число таких повторений может быть заранее известно
(фиксировано) или может определяться в процессе счета на основе проверок
одного или нескольких условий. Такие проверки условий могут выполняться
перед началом выполнения тела цикла, или по окончании выполнения тела
цикла.
Начальная установка
Начальная установка
операторы
выражение
операторы
Модификация
параметров цикла
а)
Модификация
параметров цикла
выражение
б)
Рис.3.1. Виды циклов: с предусловием – а) с постусловием – б)
В С++ для этих целей используются три взаимозаменяемых оператора цикла while, do while и for. Во всех трех операторах должны присутствовать
начальные установки, тело цикла, включающего операторы, которые и должны
выполниться несколько раз, модификация параметра цикла и проверка условия
продолжения цикла.
Начальные установки задают значения переменным, используемым в цикле,
еще до входа в цикл.
Параметрами цикла называются переменные, изменяющиеся в теле цикла и
используемые для проверки продолжения цикла.
Цикл завершается, если условие его продолжения не выполняется.
36
Цикл с предусловием while
while (условное выражение)
оператор;
Проверка условия проводится перед выполнением цикла. Возможна ситуация,
когда тело цикла не выполнится ни разу. Это случится, если первая же проверка
условия окажется ложной. Если же вычисленное условное выражение
принимает значение true (отличается от нуля), то выполняется тело цикла
(оператор) и повторяется вычисление условного выражения. Цикл
продолжается до тех пор, пока условное выражение не станет false (равным
нулю), после чего происходит выход из цикла. Как правило, в теле цикла
должны изменяться значения одной или нескольких переменных, входящих в
условное выражение c тем, чтобы в конце концов условное выражение стало
ложным (обратилось в нуль), и цикл завершился. Для того чтобы условное
выражение имело какое-то значение, перед входом в цикл в первый раз обычно
надо инициализировать одну или несколько переменных, входящих в условное
выражение.
Замечание. Будьте осторожны при работе с циклами. Cуществует
опасность попасть в бесконечный цикл.
Пример бесконечного цикла
#include<stdio.h>
int main() {
int n=2;
while(n>0)
printf(“работаем с циклом while”);
return 0;
}
Так как n всегда больше нуля, мы попадаем в бесконечный цикл.
Выход из цикла происходит в следующих случаях:
 Условное выражение в заголовке цикла стало ложным (равным 0).
 В теле цикла встретился оператор break.
 В теле цикла выполнился оператор return.
В первых двух случаях управление передается на оператор, cтоящий сразу за
оператором цикла. В третьем случае происходит возврат из функции.
Пример
Написать программу, которая печатает таблицу значений функции y= x , для
37
аргументов, изменяющихся в заданных пределах на интервале от 1.0 до 2.0 с
шагом 0.1.
#include<stdio.h>
#include<math.h>
int main() {
float xn,xk,dx,x,y;
printf(“enter xn,xk,dx:\n”);
scanf(“%f %f %f\n”,&xn,&xk,&dx);
x=xn;
while(x<xk){
y=sqrt(x);
printf("\nx=%f y=%f\n",x,y);
x+=dx;
}
return 0;
}
Протокол работы
x=1.000000
x=1.100000
x=1.200000
x=1.300000
x=1.400000
x=1.500000
x=1.600000
x=1.700000
x=1.800000
x=1.900000
y=1.000000
y=1.048890
y=0.094980
y=1.140175
y=1.183216
y=1.224745
y=1.264911
y=1.303841
y=1.341641
y=1.378405
Прототип функции вычисления квадратного корня находится в заголовочном
файле math.h, поэтому кроме подключения заголовочного файла stdio.h,
необходимого для работы с функцией printf(), подключаем еще и
заголовочный файл math.h.
По условию задачи необходимо получить таблицу значений корня квадратного
для разных значений аргумента x.
Компилятор, прочтя первую строку тела функции выделит память для
размещения данных xn, xk, dx, и память для текущих значений x и y.
Следующая строка является интерфейсной подсказкой для пользователя,
говорящая о том, что и в какой последовательности должно быть введено в
память, выделенную для исходных данных.
Следующая строка позволяет пользователю ввести с клавиатуры по адресам,
выделенным компилятором для исходных данных конкретные значения этих
данных в формате f.
Для решения задачи будем использовать, например, цикл while.
Почему вообще здесь необходим цикл? Для любого очередного значения
38
аргумента значение функции будет вычисляться по одной и той же формуле и
сразу же выводиться на печать. Для каждого следующего аргумента надо
изменить его значение на шаг dx и снова повторить однотипные действия
вычисления функции и печати ее значения.
Обратите внимание, что до входа в цикл надо задать текущему значению x
начальное значение, то есть x=xn.
При входе в цикл сразу же осуществляется проверка (x<xk). Если очередное
текущее значение x <xk , то тело цикла будет выполнено от начала до конца,
то есть будет вычислено значение функции при этом значении x , значения x и
y будут выведены на печать, после чего прежде чем возвратиться в начало
цикла, будет изменено текущее значение x на шаг dx. Только после этого мы
вернемся в начало цикла, чтобы проверить выполняется ли условие(x<xk) для
нового значения текущего x. Если выполняется, то тело цикла будет выполнено
снова, но уже для другого значения x. И так будет повторяться до тех пор, пока
x не достигнет значения, равного xk. В этот момент цикл закончит свою
работу, и программа выйдет из цикла и перейдет к оператору, следующему за
циклом. У нас после выхода из цикла больше операторов нет, следовательно,
программа закончит свою работу.
Обратите внимание, что тело цикла заключено в фигурные скобки. По
синтаксису языка и в теле цикла while, и в теле цикла do while разрешен
только один оператор (простой). Если же вам необходимо использовать в теле
цикла более одного оператора, то это уже будет составной оператор – блок,
который необходимо взять в фигурные скобки.
Цикл с постусловием do while
Проверка условия осуществляется после выполнения тела цикла.
do
оператор
while (условное _выражение);
Цикл заканчивается, когда условное выражение становится ложным
(обращается в нуль). Условия выхода из цикла такие же, как и для оператора
while
Хотя бы один раз тело цикла выполнится до проверки условного выражения в
отличии от цикла while в котором тело цикла может вообще не выполниться
ни разу, если первая же проверка условия окажется ложной.
Пример. Решим ту же самую задачу, которую решали с циклом while
используя цикл с постусловием do while.
,
#include<stdio.h>
#include<math.h>
39
int main() {
float xn=1.0,xk=2.0,dx=0.1,x,y;
x=xn;
do{
y=sqrt(x);
printf("\nx=%f y=%f\n",x,y);
x+=dx;
}
while(x<xk);
return 0;
}
Результаты работы будут точно такими же, как и при решении задачи с
помощью цикла while.
Рассмотрим еще один пример
Число x уменьшать в 2 раза до тех пор, пока оно не станет меньше 0.1
#include <stdio.h>
int main() {
float x=10;
do {
x=x/2;
printf(“x=%f”,x);
}
while(x>=0.1);
return 0 ;
}
Самостоятельно проанализируйте эту маленькую программу.
Цикл
for
for([выражение1];[выражение2];[выражение3])
оператор
Каждое из 3 выражений можно опустить. Обычно первое выражение служит
для инициализации индекса, второе –для проверки продолжения цикла, третье для изменения значения индекса.(хотя программист имеет право использовать
эти выражения как ему угодно).
Формально алгоритм работы выглядит так:
1 Если первое выражение присутствует, то оно вычисляется.
2 Если присутствует второе выражение, то и оно вычисляется. Если при
вычислении получается значение false (0),то цикл прекращается, иначе40
будет продолжен.
3 Исполняется тело цикла.
4 Если присутствует третье выражение, то оно вычисляется
5 Переходим к пункту 2.
Появление в любом месте тела цикла оператора continue приведет к
немедленному переходу к шагу 4. Оператор continue пропускает все
операторы, оставшиеся до конца цикла и передает управление на начало
следующей итерации.
Пример
Напечатать четные числа от 10 до 0
Вариант 1:
#include<stdio.h>
int main() {
int i;
for(i=10; i>=0;
i - =2)
printf(“%d ”, i);
return 0;
}
Протокол работы:
10
8
6
4
2
0
или вариант 2:
#include<stdio.h>
int main() {
int i;
for(i=10; i>=0; printf(“%d
return 0;
”,i), i - =2);
или вариант3:
#include<stdio.h>
int main() {
int i;
for(i=10; i>=0; i- -)
if(i%2==1)
continue;
else
printf(“\n%d”,i);
printf(“\n”);
}
Результат работы этих программ (всех трех вариантов) будет одинаков.
Проанализируйте
все
три
варианта.
Первый
вариант
выглядит
предпочтительнее. Он более выразителен и понятен.
41
Выводы:
Выражение1 Инициализация служит для объявления величин, используемых
в цикле и присвоения им начальных значений. Несколько операторов пишутся
через запятую. Областью действия переменных, объявленных в части
инициализации, является цикл. Инициализация выполняется один раз в начале
исполнения цикла.
Логическое Выражение 2 определяет условие выполнения цикла. Если его
результат – true, цикл выполняется. Цикл for реализован как цикл с
предусловием.
Выражение3 Модификация выполняется после каждой итерации цикла и
служит обычно для изменения параметров цикла. Здесь можно записать
несколько операторов через запятую.
Лабораторная работа по циклам состоит из двух заданий. Первое задание
лабораторной работы 3_1 посвящено использованию циклов для построения
таблицы значений функции. Второе задание лабораторной работы 3_2
вычислению по рекуррентным формулам.
Пример циклической программы Построение таблицы значений функции
Постановка задачи:
Пусть функция имеет вид у=
cos x  cos x
a 1  x
.
Рабочий набор исходных данных: N=5, a=3, xn=4, xk =8.
Необходимо построить таблицу значений функции для 5 значений аргумента от
xn до xk.
Анализ задачи
Для решения поставленной задачи удобно воспользоваться циклом for., так как
точно известно количество повторений цикла (которое совпадает с количеством
точек таблицы N=5).
Текущее значение x будет изменяться от xn до xk. Для определения шага dx
при переходе от одного текущего значения x к следующему значению
воспользуемся формулой:
dx=(xk-xn)/(n-1)
42
Одно из слагаемых в знаменателе исходной формулы, а именно, a  1 не
зависит от x и при переходе от одной итерации к другой в теле цикла
изменяться не будет, поэтому целесообразно сосчитать его один раз до входа в
цикл, и присвоить полученное значение некоторой промежуточной переменной
b:
b=sqrt(a+1)
Текущее значение x при каждой новой итерации цикла будет меняться,
увеличиваясь на величину dx, но при первом вхождении в цикл текущему
значению x должно быть присвоено начальное значение xn. Это следует
сделать до вхождения в цикл:
x=xn
В теле цикла при каждой итерации будет вычислено значение функции y для
очередного значения аргумента x , сразу же полученный результат будет
выведен на печать. Одна строка таблицы готова. Но прежде чем вернуться в
начало цикла для выполнения новой итерации необходимо изменить текущее
значение аргумента, увеличив его на величину dx.
Программа построения таблицы значений функции имеет вид:
// табулирование функции
#include<stdio.h>
#include<math.h>
#define XN 4.0
#define XK 8.0
#define A
3.0
int main(){
int n;
float dx,b,x,y;
printf(“введите количество точек таблицы n=”);
scanf(“%d\n”,&n);
printf("таблица значений функции:\n\n");
b=sqrt(A+1);
dx=(XK-XN)/(n-1);
x=XN;
// подготовка цикла
for(n=1; n<=5; n++)
{ y=(cos(sqrt(x))+cos(x))/(b+x);
printf("x=%f y=%f\n",x,y);
x=x+dx;
}
return 0;
}
В этой программе добавлена директива препроцессора
#define, которая
43
определяет подстановку в тексте программы:
#define имя подстановочный текст
При любом появлении имени (если оно встречается не в тексте, заключенном в
“ ”) и не является частью определения другого имени, имя будет заменяться на
соответствующий ему подстановочный текст. Подстановочный текст может
быть любой последовательностью символов, среди которых могут быть не
только цифры. Эти имена рекомендуется писать большими буквами, чтобы
зрительно их можно было отличить от имен переменных и функций.. Это –
именованные константы , а не переменные, поэтому для них нет описания.
Варианты заданий к работе №3_1
Циклические программы. Построение таблицы значений функций
Требуется решить на ЭВМ задачу вычисления N значений функции y =f(x)
для ряда равноотстоящих значений аргумента x , начиная от значения x=xn до
значения x=xk включительно. Функция y=f(x) зависит от параметра a.
Результаты вычислений следует оформить в виде таблицы, снабженной
заголовком. В таблице 3_1 для каждого варианта задан вид функции y=f(x) и
рабочий набор данных.
Таблица 3.1
Рабочий набор исходных данных
N
11
Вид функции y = f(x)
ex  a
1,7
N
a
x
10
1,1
1
нач
x
кон
2
Прмер циклической программы. Вычисления по рекуррентным формулам
Рассмотрим возможность применения циклов для вычисления по
рекуррентным формулам.
Пусть необходимо вычислить значения функции, содержащей сумму или
произведение.
Функция y=5x+
n
  1
k 1
 k  1
2
при n=3 и x=5
k 1
Программа, реализующая вычисление функции y:
#include<stdio.h>
#include<math.h>
#define N 3
#define X 5
44
int main() {
int k,y,znac,s=0;
znac=1;
for(k=1;k<=N;k++) {
s+=znac*(k+1)*(k+1);
znac=-znac;
}
y=5*X+s;
printf("y=%d",y);
getch();
return 0;
}
Переменная k- параметр цикла( по сути счетчик цикла).
Переменная y выделена для хранения результата работы нашей программы.
Переменная znac обеспечит знакопеременность ряда.
Как ни сложна была бы вычисляемая вами функция, цикл потребуется только
для вычисления самой суммы
n
  1
k 1
 k  1 , поэтому вычисление суммы
2
k 1
надо выделить в отдельное производство и оформить его в виде цикла. Для
n
этого введем промежуточную переменную s=   1k 1  k  12 , в которой и будем
k 1
накапливать слагаемое за слагаемым нашу сумму. До входа в цикл значение
переменной s обнулим. Под знаком суммы стоит множитель  1k 1 , что делает
ряд знакопеременным. Для нечетных к знак дает минус, для четных к знак
становится плюсом. Вводим переменную с именем znac и для первого
слагаемого делаем его положительным znac=1. Перед возвращением на
следующую итерацию знак меняем.
Оставим условия задачи прежними, но заменим знак суммы  знаком
произведения  .
Обратите на маленькие изменения в программе. Вместо переменной с именем s
ввели переменную с именем p.(хотя конечно имя переменной не играет никакой
роли, и мы могли бы оставить старое имя s). Важно то, что до входа в цикл для
того, чтобы не нарушить рекуррентность формулы для расчета произведения
при первом прохождении цикла мы присвоили ей значение eдиница(p=1)
#include<stdio.h>
#include<math.h>
#define N 3
#define X 5
int main() {
int k,y,znac,p=1;
znac=1;
for(k=1;k<=N;k++) {
p*=znac*(k+1)*(k+1);
45
znac=-znac;
}
y=5*X+p;
printf("y=%d",y);
return 0;
}
Варианты заданий к работе №3_2
Циклические программы. Вычисления по рекуррентным формулам
Таблица 3.2
Номер
варианта
Рабочий
набор
х
Функция
n
x k
y  1 2  (
) coskx
k 1 x  5
11
n
20
3,1415
Анализ программы по совместному использованию оператора switch и
циклов
После выполнения работ с использованием базовых конструкций следования,
разветвления и циклов проанализируйте пример работы оператора switch c
различными видами циклов while, do while и for.
В зависимости от введенного символа (w, d и f) оператор switch должен
переключить вас на работу с соответствующим видом цикла. Программа
должна с помощью различных видов цикла вычислить значение функции
n
1
k 1 k
n
y=x*   
k 1
(k  x)
.
k 1
Проведенный анализ поможет вам закрепить материал по использованию
оператора switch,а также еще раз провести сравнение трех видов цикла.
Текст программы
#include<stdio.h>
int main() {
float x=1.8,y,s=0,p=1;
int n=5,k;
char ch;
printf(“enter symbol ch:\n”);
scanf(“%c”,&ch);
46
switch(ch) {
case ‘f’:
for(k=1;k<=n;k++) {
s+=1/k;
p*=(k+x)/(k+1);
}
y=x*s+p;
printf(“\ny=%7.2f”,y);
break;
case ‘d’:
k=1;
do {
s+=1/k;
p*=(k+x)/(k+1);
k++;
}
while(k<=n);
y=x*s+p);
printf(“\ny=%7.2f”,y);
break;
case ‘w’:
k=1;
while(k<=n) {
s+=1/k;
p*=(k+x)/(k+1);
k++;
}
y=x*s+p);
printf(“\ny=%7.2f”,y);
break;
default: printf(“\ntry again”);
}
return 0;
}
Выводы
 Циклы используются для организации многократно повторяющихся
вычислений.
 Переменные, встречающиеся в правой части операторов
присваивания в теле циклов, должны получить до этого начальные
значения.
 Переменная, отвечающая за условия выхода из цикла, должна
изменяться в цикле, чтобы в определенный момент достичь
граничного значения, которое как раз и определяет условия выхода из
цикла.
 По синтаксису языка в теле цикла разрешено иметь только один
оператор. Если в теле цикла более одного оператора, то тело цикла
47






заключается в фигурные скобки(блок).
Помните, что операторы цикла взаимозаменяемы, но одни являются
циклами с предусловием (while,for), другие циклами с постусловием
(do while). Последний хотя бы один раз, но будет выполнен
обязательно. Цикл while используют, когда число повторений цикла
заранее не известно. Оператор for используют для организации
циклов со счетчиками.
Выражения, стоящие в круглых скобках операторов do while, while и
for принимают значения true-истина, или false-ложь.
Выражения, стоящие в скобках после ключевого слова switch и
константные выражения в case должны быть целочисленного типа.
В конце оператора switch рекомендуется ставить веточку default.
После каждой из веточек case желательно использовать оператор
break.
В случаях, когда количество повторений цикла заранее неизвестно,
желательно предусмотреть аварийный выход из цикла.
Задания для самостоятельной работы
Для закрепления пройденного материала и подготовки к экзамену выполните
следующие задания:
Задание 1
Составить программу, которая вычисляет и выводит на экран монитора
таблицу “n” значений функции y на интервале от xn до xk:
y= a*lg|x+15| при x<-3
при -3<=x<7
x 2  24
a * ( x  5)
x
при x>=7
a=-3,7.
Значения xn, xk и количество точек таблицы n вводятся с клавиатуры.
Задание 2
Составить программу, которая вычисляет и выводит на экран монитора таблицу
“n” значений функции y на интервале от xn до xk:
y=
a*x2
a * sin 2 x
ln|x-3|
при x<-5
при -5<=x<1
при x>=1
a=1,37.
Значения xn, xk и количество точек таблицы n вводятся с клавиатуры.
48
Задание 3
Написать программу
выражением:
табулирования
функции,
заданной
следующим
m
3
x   (k  1) 2
k 1
где m=8,xn=1, xk=7, n=15 (количество точек)
Задание 4
Написать программу
выражением:
табулирования
функции,
заданной
следующим
m
3
x   (k  1) 2
k 1
где m=8,xn=1, xk=7, n=15 (количество точек)
Задание 5
Написать программу
выражением:
m
 (x  k)
2
табулирования
где m=8, xn=2
xk=8,
функции,
заданной
следующим
n=10(кол-во точек табуляции)
k 1
Контрольные вопросы по разветвлениям и циклам
1. Какие виды циклов вам известны?
2.В каких ситуациях используется тот или иной вид цикла?
3.Что принято называть параметром цикла?
4.Можно ли вне цикла использовать значение параметра цикла?
5.Какие
управляющие
структуры
используются
для
разветвляющихся программ?
6.В каких ситуациях удобнее пользоваться оператором switch?
реализации
49
Тема 4 Массивы и указатели
Теоретический материал
Одномерные и двумерные массивы
Между массивами и указателями существует тесная связь, поэтому имеет
смысл разбирать их вместе.
Массив – это расположенные вплотную друг за другом в памяти элементы
одного и того же типа. Всем элементам массива дается одно и то же имя, а
различают их по порядковому номеру (индексу). Это позволяет компактно
50
записывать однотипные действия с элементами массива, используя циклы.
Можно сказать, что массив - это структура данных, которую можно
рассматривать как набор переменных одинакового типа, имеющих общее имя.
Массив используется для хранения однородной по своей природе информации
(массив отсчетов напряжения входного сигнала, массив отсчетов времени и т.
п.)
Описание одномерных массивов
тип имя [размерность]
int x[5] //описание массива с именем x из 5 целых чисел
Массивы, как и другие объекты, можно размещать в памяти либо с помощью
операторов описания в сегменте данных или в стеке, либо в динамической
области памяти с помощью операции выделения памяти. Нельзя задать массив
переменного размера. Для этого существует отдельный механизм
динамического выделения памяти
В зависимости от места объявления массив располагается либо в сегменте
данных, либо в сегменте стека. Все инструкции по выделению памяти
формирует компилятор до выполнения программы, поэтому размерность
массива может быть задана только const или константным выражением. Удобно
задавать размерность массива с помощью именованной константы.
При описании массива его можно инициализировать, то есть присвоить его
элементам начальные значения:
int x[5]={1,5.4,8,4}
Индексация начинается с нуля.
int x[10] –массив целого типа, содержащий 10 элементов от x[0] до
x[9].
Количество байтов = <размерность базового типа> * <количество элементов>
Массив занимает непрерывную область памяти.
Доступ к элементам массива осуществляется по имени массива и индексу
элемента.
Основные свойства одномерных массивов
 все элементы имеют один и тот же тип;
 все элементы располагаются в памяти друг за другом;
 индекс первого элемента равен 0;
 имя массива является указателем - константой, равной адресу начала
массива (первого байта первого элемента массива);
 признак массива при описании – наличие парных квадратных скобок [].
51
Константа или константное выражение в [ ]
массива.
int x[10];
задает число элементов
char y[80];
Инициализация одномерных массивов
При описании массивов может быть выполнена явная инициализация его
элементов.
Для этого после описания массива помещается список начальных значений
элементов массива, заключенных в фигурные скобки {}.
Известны две формы явной инициализации массива:
 явное указание числа элементов массива и список начальных
значений, может быть, с меньшим числом элементов:
int array[10]={5,2,1,3};
Описывается массив из 10 элементов. Первые 4 элемента массива
инициализированы числами 5, 2, 1, 3. Значения остальных 6 элементов либо
равны 0, либо не определены.
Если список начальных значений содержит больше элементов, чем число в [], то
компилятор генерирует сообщение об ошибке;
 только со списком начальных значений. Компилятор определяет
число элементов массива по списку инициализации:
char arr[ ]={‘A’,’B’,’C’};
В результате создается символьный массив ровно из 3 элементов, и эти
элементы получат начальные значения из списка инициализации.
В Си для повышения производительности программы не выполняются многие
проверки корректности вычислений, в том числе и контроль допустимости
значения индекса массива. (Автоматического контроля выхода за границы
массива нет!) Для упрощения контроля границ индекса массива удобно
использовать операцию sizeof, которая применительно к массивам
возвращает не размер элемента, а число байтов памяти, зарезервированное
компилятором для массива. Так можно определить число элементов массива:
int array[10]={1,2,3,4,5,6,7};
int members;
members=sizeof(array)/sizeof(int);
При выводе на экран значения переменной members получим ответ:
52
members=10;
Описание двумерных массивов
тип
имя_массива [размерность1][размерность2]
При определении массива с помощью операторов описания его размерность
должна быть константой или константным выражением, так как память для него
выделяется на этапе компиляции до выполнения программы.
int x[2][3] – целочисленная матрица из 2 строк и 3 столбцов.
Массив хранится по строкам в непрерывной области памяти. При просмотре
массива от начала первым будет изменяться правый индекс (номер столбца!)
Доступ к отдельному элементу - x[i][j],где i, j - номера строки и
столбца соответственно.
Имя массива представляет собой константный указатель на начало массива.
Инициализация двумерных массивов
int x[2][3]={1,2,3,4,5,6}
int x[2][3]={{1,2,3},{4,5,6}}
int x[][3]={{1,2,3}{4,5,6}} - память будет выделена под столько
строк массива, сколько серий значений в фигурных скобках будет указано в
списке.
Пример
программы,
массивом(вектором)
работающей
с
одномерным
Постановка задачи
Ввести с клавиатуры значения элементов исходного массива а, состоящего из
5 элементов целого типа.
Сформировать новый массив b в соответствии со следующим правилом:
b[i]=a[i]+2(max-min), где max и min – максимальное и минимальное значения
исходного массива a. Ввод-вывод элементов массива – потоковый.
Анализ задачи
В данной задаче речь идет о двух массивах: исходном массиве а и
результирующем массиве в. Для работы с этими массивами под каждый из них
необходимо выделить память, для чего их необходимо задекларировать.
Размерность массивов может быть только константой (const int n=5;)
Декларация самих массивов соответствует строке (int a[n],b[n];)
Для хранения значений максимума, минимума и индекса выделяются
переменные с именами max,min и i (int i,max,min;)
Цикл(1) осуществляет поэлементный ввод конкретных значений массива а в
53
ячейки памяти, выделенные под массив на этапе компиляции.
Цикл (2) ищет максимальное и минимальное значения массива а.
Цикл (3) формирует значения нового массива в в соответствии с заданием и
тут же поэлементно выводит сформированные значения на экран монитора.
В самом конце программы вычисляется размер памяти в байтах, выделенный
под массив а с помощью операции sizeof.
Текст программы
#include<iostream.h>
int main() {
const int n=5;
int a[n], b[n];
int i, max, min;
cout<<"enter array a:"<<endl;
for(i=0;i<n;i++) {
cout<<"a["<<i<<"]=";
//цикл (1)
cin>>a[i];
}
max=a[0];
min=a[0];
for(i=0;i<n;i++) {
if(a[i]>max) max=a[i];
// цикл (2)
if(a[i]<min) min=a[i];
}
cout<<endl<<"results:"<<endl;
cout<<"N
a[i]
b[i]"<<endl;
for(i=0;i<n;i++) {
b[i]=a[i]+2*(max-min);
//цикл(3)
cout<<i<<"
"<<a[i]<<"
"<<b[i]<<endl;
}
int kol=sizeof(a)/sizeof(int);
cout<<endl<<"kol="<<kol;
return 0;
}
Варианты заданий к
работе 4_1
21. Дан вектор, состоящий из 6 вещественных элементов. Заменить все
отрицательные элементы массива их квадратами, после чего упорядочить
элементы массива по неубыванию, используя один из известных вам методов
сортировки .
Ввод/вывод – потоковый.
Пример программы, работающей с двумерным массивом (матрицей)
54
Дана целочисленная матрица, состоящая из 3 строк и 4 столбцов. Найти
количество положительных элементов в каждой строке матрицы и среднее
арифметическое значение элементов всей матрицы. Элементы матрицы
вводятся с клавиатуры. Ввод/вывод элементов – потоковый.
Текст программы
#include<iostream.h>
#include<iomanip.h>
int main() {
const int nrow=3,ncol=4;
int a[nrow][ncol];
int i,j;
cout<<"enter array:"<<endl;
for(i=0;i<nrow;i++) //цикл ввода матрицы
for(j=0;j<ncol;j++)
// (1)
cin>>a[i][j];
cout<<endl;
for(i=0;i<nrow;i++) { //контрольный вывод
for(j=0;j<ncol;j++)
cout<<setw(5)<<a[i][j]<<" ";
cout<<endl;
//(2)
}
cout<<endl;
int n_pos_el;
float s=0;
for(i=0;i<nrow;i++) {
//цикл обработки матрицы
n_pos_el=0;
for(j=0;j<ncol;j++) {
s+=a[i][j];
//(3)
if(a[i][j]>0) n_pos_el++;
}
cout<<"in row "<<i<<" n_pol_el=:"<<n_pos_el<<endl;
}
cout<<endl;
s/=nrow*ncol;
cout<<"sr="<<s<<endl;
return 0;
}
Пояснения к программе:
Заголовочный файл <iostream.h> позволяет воспользоваться объектами
cin и cout для связи с клавиатурой и экраном соответственно и операциями
ввода/вывода >> и << .
Заголовочный
файл
<iomanip.h>
позволяет
воспользоваться
55
манипулятором setw(int), который устанавливает максимальную ширину
поля вывода.
nrow-количество строк матрицы
ncol – количество столбцов матрицы
int a[nrow][ncol] – декларация матрицы, в соответствии с которой на
этапе компиляции будет выделена память для хранения значений матрицы,
состоящей из nrow строк и ncol столбцов.
Цикл(1) позволяет ввести значения элементов матрицы в оперативную память,
которая была выделена под матрицу на этапе компиляции в соответствии с
вашей декларацией. Ввод значений осуществляется по строкам.
Цикл(2) проводит контрольный вывод только что введенной матрицы, выделяя
под каждый элемент 5 позиций и выводя матрицу по строкам.
Представляет интерес последний цикл(3). До входа в этот цикл
задекларирована переменная n_pos_el целого типа. Это счетчик, который
будет считать количество положительных элементов в каждой отдельной
строке. Здесь же задекларирована вещественная переменная s, которая будет
накапливать сумму всех элементов матрицы. Эта сумма необходима для расчета
среднего арифметического значения, которое будет вещественного типа, ибо
является результатом деления суммы всех элементов на их количество. До входа
в цикл значение s обнулили.
При первом входе в цикл фиксируем нулевую строку(i=0) и работаем с ней.
Прежде чем пробежаться по всем ее столбцам(j от 0 до ncol)обнулим
переменную n_pos_el=0.Счет положительных элементов еще не начался. Мы
в начале нулевой строки.
Теперь войдем во внутренний цикл по j и прогуляемся по столбцам нулевой
строки. При этом в теле внутреннего цикла выполним два оператора. Первый
будет добавлять к сумме значение очередного элемента строки, а второй – в
случае, если этот очередной элемент положителен, увеличит счетчик
положительных элементов строки на единицу.(n_pos_el++).
По окончании работы с нулевой строкой вернемся в начало внешнего цикла по
i , которое теперь станет равным единице. Это означает, что мы переходим к
работе с первой строкой(i=1).Все операторы тела цикла повторят ту же самую
работу, но только теперь для элементов первой строки и т.д..
По выходе из цикла останется найти среднее арифметическое значение.
Варианты заданий к работе 4_2
Задание к выполнению лабораторной работы 4_2 (общее для всех):
Написать программу, реализующую алгоритм решения задачи. Размерность
массива nrow (количество строк) и ncol (количество столбцов) задать
именованными константами. Значения элементов массива
вводить в
оперативную память (ОП) с клавиатуры. Ввод/ вывод – потоковый.
56
Варианты:
15. Дана целочисленная квадратная матрица, размерностью 4*4.
Сформировать вектор, содержащий только положительные элементы второй
строки матрицы.
Теоретический материал
Указатели
Указатели - это переменные, предназначенные для хранения адресов
областей памяти. Значения указателей показывают, где в памяти хранится
объект, а не что хранится по адресу.
В Си существует два способа доступа к переменным:
 ссылка на переменную по имени:
int a;
a=5;
 использование механизма указателей:
int a,*pa;
pa=&a;
*pa=15;
pa
a
&a ---15 , где pa – указатель, ссылающийся на а (говорят, что pa
указывает на а, или pa ссылается на а).
Так как унарный оператор & выдает адрес объекта, то можно написать
pa=&a. Еще раз напоминаем, что оператор & применяется только к объектам,
расположенным в памяти: к переменным и элементам массивов.
Указатели могут быть:
указателями – константами, и тогда они ссылаются на один и только
один адрес памяти;
указателями – переменными, и тогда они служат для хранения
различных адресов ОП( их называют просто указатели).
Признаком указателя – переменной для компилятора является наличие в
описании переменной двух компонентов:
тип объекта данных, для доступа к которому используется указатель;
символ * перед именем переменной.
57
В совокупности тип и * воспринимаются компилятором как особый тип
данных – “указатель на что-либо”.
Итак, указатель может быть константой или переменной, а также он может
указывать на константу или переменную:
int x;
//целая переменная
const int n=3;
int *px;
// целая константа
//указатель на целую переменную
const int * pn; //указатель на целую константу
(изменить константное значение по адресу pn нельзя, но значение самого
указателя может быть изменено, то есть его можно заставить указывать на
другую константу).
int *const np=&x; //указатель-константа на целую переменную
(если надо, чтобы указатель ссылался на один и только один адрес памяти, то
его надо объявить как константный указатель на тип).
const int *const npn=&n; //указатель-константа на целую константу
(если надо заморозить как адрес, так и значение по этому адресу, то следует
использовать константный указатель на константу).
Модификатор const,находящийся между именем указателя и звездочкой,
относится к самому указателю и запрещает его изменение.Const слева от
звездочки задает постоянство значения, на которое он указывает.
В пару к унарному оператору &-взятия адреса имеется унарный оператор
* - раскрытие ссылки. (обращение к содержимому по адресу, равному
значению указателя):
*p1=*p2+4; // взять содержимое памяти по адресу, равному значению
указателя p2, прибавить к этому содержимому 4, а результат поместить по
адресу, равному значению указателя p1.
Пример использования оператора * (косвенной адресации):
#include<stdio.h>
//использование операции косвенной адресации
int main() {
// без косвенной адресации
int x,y;
58
clrscr();
x=2;
y=x;
printf(“y=%d\n”,y);
//с косвенной адресацией
int *px;
px=&x;
y=*px;
printf(“y=%d\n,y);
//
//будет напечатано y=2
будет напечатано
// использование косвенной
присваивания
адресации
y=2
в левой
части оператора
int z,*pz;
pz=&z;
// инициализировать pz адресом z
*pz=10; // изменить значение, находящееся по указанному
/ / адресу (то есть значение z) Присвоить 10
// по адресу, содержащемуся в переменной pz.
printf(“z=%d\n”,z);
//z=10
return 0;
}
Замечание. Посмотрите таблицу приоритетов операций (табл.1.1)
Убедитесь в том, что приоритет двух унарных операций * и & достаточно высок
Выводы
Значение указателя сообщает о том, где размещен данный объект, но при этом
не говорит ничего о самом объекте.
Звездочка * относится непосредственно к имени, поэтому для объявления
нескольких указателей надо ставить звездочку перед именем каждого из них.
int *x,y,*z;
// описаны два указателя на целое c
//именами x и z, а также целая переменная y.
Инициализация указателей
Осуществляется с помощью присваивание указателю адреса существующего
объекта:
int x;
int * px=&x; // использование операции &)
59
char arr[10];
char * parr=arr; //с помощью имени массива, так как имя массива в
этом случае само является адресом начала массива).
arr==&arr[0], где arr[0] –первый элемент массива (элемент
массива с индексом =0).
Операции с указателями
Мы уже выделили две особые операции & и *.
( операции получения адреса и разадресации для доступа к величинам, адрес
которых хранится в указателе)
Кроме этого важны еще арифметические операции
(сложение с константой, вычитание, инкремент, декремент)
Эти операции автоматически учитывают размер типа величин, адресуемых
указателями.
Эти операции применимы только к указателям одного типа и имеют смысл в
основном при работе со структурами данных, последовательно размещенных в
памяти, например, с массивами:
int
a[5];
int *pa=a; // инициализировать pa адресом первого элемента массива.
Можно ли с помощью переменной pa осуществить адресацию к
определенному элементу массива?
Альтернативные способы задания элементов массива
pa=&a[0]--------|a[0] | a[1]| a[2] |a[3] |a[4]|
a
a+1
a+2
a+3
a+4
&a[0] &a[1] &a[2] &a[3] &a[4]
pa
pa+1 pa+2
pa+3 pa+4
Арифметические действия над указателями обеспечивают удобный способ
манипулирования массивами или другими непрерывными участками памяти.
Добавление целого числа к указателю увеличивает содержащийся в нем адрес
на произведение этого целого числа на размер (в байтах) того объекта, на
который этот указатель показывает. Именно поэтому надо описать тип данных,
на который показывает указатель. За счет этого выполняется масштабирование
в соответствии с размером указанного типа.
60
Таким образом, в языке предусмотрены операции, которые
использовать для доступа к указателям и для манипулирования ими.
можно
При составлении программ можно обойтись и без явного применения
указателей. Однако они помогают упростить алгоритм и повысить его
эффективность, так как они обеспечивают простые способы ссылок на массивы,
списки, другие блоки данных. Именно для таких ссылок предлагается простой
элемент данных
- указатель. Эффективнее манипулировать простым
указателем, чем управлять полным массивом.
Замечание. Мы узнали, что имя массива и адрес его первого элемента
эквивалентны. Имя массива в действительности является указателем.
Поэтому мы не можем присвоить ему новое значение. Имена массивов не
являются адресуемыми значениями, и оператор вида
имя массива =
выражение недопустим, так как нельзя изменить адрес массива.
Выводы
 Массив- это структура данных, представляющая набор переменных
одинакового типа, имеющих общее имя.
 Размерность массива может быть только константой или константным
выражением. Рекомендуется задавать размерность с помощью
именованной константы.
 Нумерация элементов массива начинается с нуля, поэтому
максимальный индекс для элементов массива на единицу меньше
размерности массива.
 Автоматический контроль выхода индекса за границы массива
отсутствует, поэтому программисту необходимо следить за этим
самостоятельно.
 Массив, как и любой другой объект программы, должен быть
задекларирован, чтобы дать возможность компилятору выделить под
него память. После выделения памяти надо эту память заполнить
конкретными значениями элементов массива, например, с помощью
ввода этих элементов с клавиатуры. Только после этого можно начать
обработку элементов массива в соответствии с заданием.
 Ввод / вывод элементов массива и его обработка выполняются только в
цикле.
 Работая с массивом в цикле обратите внимание на то, что параметр
цикла и индекс массива всегда имеют одно и то же имя. Это
принципиально важно.
 Указатель – это переменная, в которой хранится адрес области памяти.
 При работе с массивами автоматически формируется указатель с
именем массива. Имя массива является указателем на его нулевой
элемент(значение указателя равно адресу нулевого элемента массива).
 Доступ к элементам массива можно осуществить через указатель с
именем массива. Так как указатель является константой, то его можно
61
использовать в выражениях, но нельзя изменять.
Вопросы:
1. Понятие массива
2. Данные каких типов могут выступать в качестве индексов и элементов
массива?
3.Какая связь между индексом массива и параметром цикла?
4.Каковы особенности работы с двумерными массивами?
9
ТЕМА:
ФУНКЦИИ
Теоретический материал
Структурное программирование предполагает, что большие вычислительные
задачи
могут быть разбиты на более простые логически законченные
подзадачи, которые можно оформить как функции. Программа на Си состоит из
некоторого набора таких небольших функций. Использование функций
позволяет выполнить описание функции только один раз, а вызвать функцию
для выполнения можно многократно из разных точек программы. Это позволяет
избежать избыточности кода, упрощает отладку и сопровождение программы.
ФУНКЦИЯ – это логически самостоятельная именованная часть программы,
которой можно передать параметры, и которая может возвратить значение.
62
Помещение фрагмента кода в функцию и передача всех необходимых ей
данных в качестве параметров называется инкапсуляцией, то есть скрытием
деталей реализации.
Для вызова такой функции достаточно знать ее интерфейс, определяемый ее
заголовком.
Для работы с функциями необходимо уметь:
 объявить (задекларировать) функцию;
 определить (описать) функцию;
 вызвать функцию.
Объявление (декларация) функции
Задает имя функции, тип возвращаемого значения и список передаваемых
параметров:
тип
имя_функции ([список формальных параметров]);
Если тип не указан, предполагается по умолчанию, что функция возвращает
целое типа int.
Список формальных параметров состоит из перечня типов и имен параметров,
разделенных запятыми.
Если параметров нет, то ( ) все равно обязательны.
В списке параметров для каждого параметра должен быть указан тип:
(int x, int y, float z)
Объявление функции – это ее заголовок, который называют прототипом или
сигнатурой. До первого вызова функции компилятор должен знать тип
возвращаемого результата, а также количество и типы аргументов. Только тогда
может быть создан правильный машинный код функции ( то есть объявления
функций должны находиться в тексте программ раньше ее вызова). Обычно
прототипы функций помещают в заголовочный файл, подключаемый
директивой #include к тексту программы.
Чаще всего прототип функции полностью совпадает с заголовком в описании
функции, хотя и не всегда так. Дело в том, что имена формальных параметров
не играют никакой роли, и могут игнорироваться компилятором:
int func(int a, float b, char*c);
int func(int, float, char*); //оба прототипа эквивалентны
Определение (описание) функции (стандарт ANSI)
тип имя_функции
{
([список формальных параметров])
63
тело функции
}
 Имя функциии -это особый тип указателя, называемый указателем на
функцию. Его значением является адрес точки входа в функцию.
Выполнение функции возвращает управление в точку вызова;
 Тип возвращаемого значения может быть любым, кроме массива и
функции (но может быть указателем на массив и функцию);
 Список формальных параметров - определяет величины,
передаваемые в функцию (типы, имена). Если в функцию ничего не
передается, то поле списка аргументов либо пусто (), либо (void).
Элементы списка разделяются запятыми;
 В объявлении, определении и вызове одной и той же функции типы и
порядок следования параметров должны совпадать. Имена
параметров могут не совпадать, так как функцию можно вызвать с
различными аргументами, а в прототипах имена игнорируются
компилятором.
Вызов функции
В простейшем случае надо указать имя
аргументов:
функции и имена передаваемых
имя_ функции([список фактических параметров]);
Вызов функции может находиться в любом месте программы, где по
синтаксису допустимо выражение того типа, которое формирует и возвращает
функция.
Обмен информацией между функциями
Любая программа на С++ - это совокупность определений переменных и
функций. Связи между функциями осуществляются
 с помощью глобальных переменных;
 через возвращаемое значение;
 с помощью передачи параметров.
Глобальные переменные видны во всех функциях, где не описаны
локальные переменные, имеющие те же имена. Однако их использование не
рекомендуется, так как это затрудняет отладку программы, поиск в ней ошибок
и препятствует помещению функций в библиотеки общего пользования.
Функции предпочтительно иметь максимально независимыми, и их интерфейс
должен полностью определяться прототипом функции.
64
Возвращаемые значения реализуется оператором return.
Оператор return имеет два варианта использования:
 вызывает немедленный выход из текущей функции и возврат в
вызывающую программу;
 может использоваться для возврата значения функции
return в теле функции может быть в нескольких вариантах, но может и не
быть вовсе, и тогда возврат в вызывающую программу происходит после
последнего оператора тела функции.
Нахождение наибольшего из двух чисел:
1)
#include <iostream.h>
int func(int a,int b); //прототип функции
int main() {
int x,y,max;
cout<<"input x,y";
cin>>x>>y;
max=func(x,y); //вызов функции
cout<<"max="<<max;
return 0;
}
int func(int a,int b) {
//определение функции
int m;
if(a>b) m=a;
else
m=b;
return m; //возвращение значения максимума в точку в
// вызова функции
}
Функция нахождения максимума может выглядеть по-другому( 2),3).4)):
2)
int func(int a, int b) {
if(a>b) return a;
else
return b;
}
3)
int func(int a, int b) {
if(a>b) return a;
return b;
}
65
4) int func(int a, int b) {
return(a>b)? a:b;
}
Все величины, описанные внутри функции, являются локальными. Областью
их действия является функция. При вызове функции в специальной области
памяти, называемой стеком, выделяется память под локальные
автоматические переменные. Большинство переменных являются именно
автоматическими, то есть они относятся к классу хранения auto.
Автоматические переменные – это всегда локальные переменные, но локальные
переменные не обязательно должны быть автоматическими. Локальные
переменные могут быть, например, статическими. Класс хранения определяет
место, где будет расположен объект (сегмент стека или сегмент данных) и
одновременно время жизни этого объекта. Сегмент стека и сегмент данных –
это разные области памяти. Сегмент данных хранит значения переменных в
течение жизни всей программы. Стек – только время жизни функции. При
выходе из функции стек освобождается и готов к приему значений переменных
следующей функции.
Кроме того, в стеке сохраняется содержимое регистров процессора на
момент, предшествующий вызову функции, и адрес возврата из функции для
того, чтобы при выходе из нее можно было продолжить выполнение
вызывающей функции.
При выходе из функции соответствующий участок
стека освобождается, поэтому значения локальных переменных между
вызовами одной и той же функции не сохраняются. Если вы все-таки решили
сохранить значения переменных, то при объявлении локальных переменных
используют модификатор static.(видимость в пределах модуля, в котором
определена функция). Все глобальные переменные по умолчанию являются
статическими объектами. Локальная же переменная должна быть явно отнесена
к классу static. Статические переменные существуют все время, пока
выполняется программа.
Пример 1
#include<iostream.h>
int func( int x) {
int m=0;
cout<<”n m p\n”;
while(x--) {
static int n=0;
int p=0;
cout<<n++<<” ”<<m++<<”
return 0;
}
“<<p++<<endl;
66
cout<<endl;
}
int main() {
func(3);
func(2);
return 0;
}
Протокол работы:
n
0
1
2
m
0
1
2
p
0
0
0
n
3
4
m
0
1
p
0
0
Пояснения
1) static int n=0
статическая переменная n
размещается в сегменте данных и инициализируется один раз
при
первом
выполнении
оператора,
содержащего
ее
определение
2) автоматическая переменная int m=0 инициализируется
при каждом входе в функцию
3) автоматическая переменная int p=0 инициализирется
при каждом входе в блок цикла.
Функция func() вызывается дважды. При этом статическая
переменная n после выхода из функции сохраняет свое
значение Автоматическая переменная при выходе из функции
теряет свое значение.
Пример 2
#include<iostream.h>
int func(int);
int main() {
int count;
for(count=9;count>=5;count-=2)
func(count);
return 0;
}
int func(int x) {
int f=1;
static int stat=1;
67
cout<<”x=”<<x<<”f=”<<f<<”stat=”<<stat<<endl;
stat++;
f++;
return 0;
}
Протокол работы
x=9
f=1
stat=1
x=7
f=1
stat=2
x=5
f=1
stat=3
Пояснения
1) ststic int stat=1 –это статическая переменная stat. Она размещается в
сегменте данных и инициализируется один раз при первом выполнении
оператора, содержащего ее определение, то есть при загрузке программы.( в
пошаговом режиме выполнения программы поэтому мы этого не увидим).
2) int f=1 – это автоматическая переменная. Она инициализируется при
каждом входе в функцию.( то есть при каждом входе в функцию ей
присваивается значение, равное 1).
3)
Функция
func()
вызывается
трижды
(в
цикле
при
count=9,count=7,count=5).При этом статическая переменная stat после выхода
из функции сохраняет свое значение, полученное внутри функции.
Автоматическая переменная f при выходе из функции теряет свое значение.
Замечания. Статические объекты с локальной областью определения имеют
статическое время жизни (время жизни всей программы), но они невидимы из
других функций и не могут этими функциями модифицироваться. Это
позволяет создавать специальные функции-менеджеры ресурса: менеджеры
очередей, памяти и т.п. Для доступа к ресурсу вы вынуждены вызвать
функцию - менеджер, так как управляемый ресурс невидим из других функций.
Объекты с динамическим временем жизни создаются и разрушаются
специальными функциями динамического управления памятью при выполнении
программ (будем изучать в курсе “Современные методы программирования”).
Каждая программа имеет только одну функцию с именем main. C этой
функции начинается исполнение программы. Другие функции могут быть
вызваны из функции main или из любой другой функции в процессе
исполнения программы. Каждая функция может иметь 0 или более параметров.
Параметры являются переменными, которые используются для передачи
данных между функциями. Имена параметров в вызываемой и вызывающей
функциях – независимы.
Если описание функции сделано до функции, в которой она вызывается, то
можно не создавать прототип. Если же описание следует после вызывающей
функции, то прототип обязателен. Корректный стиль написания программы
68
предполагает обязательное наличие прототипа.
Передача параметров
Механизм передачи параметров – это основной способ обмена информацией
между функциями. Параметры функции при описании называются
формальными параметрами. При вызове используются фактические
параметры (аргументы), которые потом становятся на место
формальных параметров.
При вызове функции сначала вычисляются выражения, стоящие на месте
аргументов; затем в стеке выделяется память под формальные параметры в
соответствии с их типами. Затем каждому формальному параметру
присваивается значение соответствующего аргумента. При этом идет проверка
соответствия типов и их преобразование в случае необходимости.
Существует два способа передачи параметров в функции:
 по значению: в стек заносятся копии значений аргументов, и операторы
функции работают с этими копиями; доступа к исходным значениям
параметров у функции нет, а значит, нет и возможности их изменения;
 по адресу (с помощью указателя и по ссылке) В стек заносятся копии
адресов аргументов, а функция обращается к сегменту данных по этим
адресам и может изменить исходные значения аргументов, так как имеет
к ним доступ.
Пример
#include<iostream.h>
int func(int i, int *j, int& k);
int main() {
int i=1, j=2, k=3;
cout<<” i j k\n”;
cout<<i<<” “<<j<<” “<<k<<endl;
func(i, &j, k);
cout<<i<<” “<<j<<” “<<k;
return 0;
}
int func(int i, int* j, int& k){
i++;
(*j)++;
k++;
}
протокол работы.
i
j
k
1
2
3
1
3
4
69
Первый параметр i передается по значению. Его изменение в функции не
влияет на исходное значение.
Второй параметр j передается с помощью указателя, при этом для передачи в
функцию адреса фактического параметра используется операция взятия
адреса, а для получения его значения в функции требуется операция
разыменования.
Третий параметр k передается по адресу с помощью
ссылки. При передаче по ссылке в функцию передается адрес указанного при
вызове параметра, а внутри функции все обращения к параметру неявно
разыменовываются.
Использование ссылок вместо указателей улучшает
читаемость программы, избавляя от необходимости применять операции
получения адреса и разыменования. Использование ссылок вместо передачи по
значению более эффективно, так как не требует копирования параметров, а это
важно при передаче структур большого объема.
Замечания.
1) если мы хотим запретить изменение параметра внутри функции, то
используют модификатор const:
int function(const char*); (константная ссылка).
2) Любая переменная обладает двумя основными характеристиками временем жизни и областью действия. Эти характеристики зависят от места
и способа описания переменной.
. . . . . . . . .
int x; // глобальная переменная
int main() {
int f=1;
//локальная переменная
static int stat=1; //локальная статическая переменная
. . . . . . . . .
}
Имя
Место
описания
Размещение
Время жизни
Глобальная Локальная
статическая
x
stat
Вне любого Внутри блока с
блока
ключевым словом
ststic
Сегмент
Сегмент данных
данных
Вся
Вся программа
локальная
f
Внутри блока
Сегмент
стека
(память
выделяется
в
момент
выполнения
операции
описания)
Блок, в котором
70
программа
Область
видимости
она
описана,
начиная с точки
описания
блок
Весь файл, Блок
начиная с
точки
описания
Инициализация Изначально Инициализируется Автоматического
обнуляется однократно
обнуления
не
происходит.
Сколько
раз
выполняется
блок,
столько
раз рождается и
умирает
локальная
переменная
Память под эти переменные выделяет компилятор
Область видимости – это та область исходного кода программы, из которой
возможен корректный доступ к памяти с использованием данного
идентификатора. По сути, область определения задает его видимость по
умолчанию. Если хотим, что бы несмотря на область определения, область его
видимости была бы отлична от того, что задается по умолчанию, то используют,
например, объявление с атрибутом extern(внешняя ссылка).
Область
видимости в большей степени касается не компилятора, а компоновщика.
Почему? Чаще всего текст программы размещается в нескольких текстовых
файлах, каждый из которых содержит целиком одну или несколько функций.
Для объединения в одну программу эти текстовые файлы компилируются
совместно. Информация обо всех объединяемых в одну программу файлах
помещается в так называемый файл проекта. Компилятор порождает для
каждого исходного текстового файла отдельный объектный файл. Затем эти
файлы объединяются компоновщиком в EXE-файл.
Область определения – это та часть программы, в пределах которой
идентификатор может использоваться для доступа. Область определения нужна
компилятору для того, чтобы сгенерировать корректный машинный код. Если
место описания идентификатора внутри блока, то он имеет локальную область
определения, ограниченную размером блока. Все переменные, описанные
внутри функции, а также формальные параметры имеют локальную область
определения. Если идентификатор описан вне блоков, он является глобальным
и имеет область определения, начиная с точки описания, и продолжается до
конца файла. Он виден из всех функций, расположенных ниже точки его
описания.
71
Имена функций – это всегда глобальные имена, видимые по умолчанию из всех
файлов проекта. Однако, прототипы функций действуют только в пределах
одного файла. Поэтому приходится иметь директивы препроцессора, связанные
с подключаемыми .h- файлами, содержащими прототипы библиотечных
функций.
Передача массивов в качестве параметров
При использовании массива в качестве параметра в функцию передается
указатель на его первый элемент, то есть массив всегда передается по адресу.
Но адрес указывает только на начало массива и ничего не говорит о его размере,
поэтому размерность массива надо передавать через отдельный параметр:
int function(int arr[], const int n);
int function(int *arr, const int n);
Оба варианта приведут к одному и тому же результату. И в том, и в другом
случае вы передаете адрес начала массива и количество его элементов.
Пример программы, работающей с функциями
Ввести с клавиатуры два целочисленных массива ‘а’ и ‘в’ и их размерности.
Кол-во элементов в массивах не должно превышать 10. В каждом массиве
вычислить произведение элементов, кратных 3. Первые элементы в каждом
массиве заменить на полученное произведение.
#include<stdio.h>
#define N 10
//(1)
int input(int a[],const int n);
int change(int a[],const int n);
int output(int a[],const int n);
//(2)
int main() {
int a[N],b[N],n1,n2;
printf("input razmer a=");
scanf("%d",&n1);
printf("input razmer b=");
scanf("%d",&n2);
input(a,n1);
change(a,n1);
output(a,n1);
input(b,n2);
change(b,n2);
output(b,n2);
//(3)
72
return 0;
}
int input(int a[],const int n) {
int i;
printf("input array:\n");
for(i=0;i<n;i++) {
printf("a[%d]=",i);
scanf("%d",&a[i]);
}
printf("\n");
return 0;
}
int change(int a[],const int n){
int i,pr=1;
for(i=0;i<n;i++)
if(a[i]%3==0)
pr*=a[i];
a[0]=pr;
}
int output(int a[],const int n) {
int i;
for(i=0;i<n;i++)
printf("a[%d]=%d\n",i,a[i]);
}
//(4)
//(5)
//(6)
Пояснения.
(1)-директивы препроцессора для подключения заголовочного файла
<stdio.h> и определение подстановки в текст программы;
(2) – прототипы функций input(),change(),output();
(3) – определение главной функции main(). Внутри функции main()
расположены вызовы функций input(),change() и output() и
настройка параметров этих функций сначала на работу с массивом а, затем с
массивом в;
(4) – определение функции input(), которое показывает, как будет
работать эта функция, когда мы ее вызовем сначала для ввода элементов
массива а, затем для ввода элементов массива в;
(5) –определение функции change(), которая ищет произведение только
тех элементов массивов а и в соответственно, которые нацело делятся на 3,
и помещает результат этого произведения на место первого элемента массива;
(6) – определение функции output(), которая печатает новые массивы.
73
Выводы
1.Функция – это логически самостоятельная именованная часть программы.
Использование функций упрощает структуру программы.
2. Интерфейс функции определяется ее заголовком.
3. Для вызова функции надо указать ее имя и набор фактических
параметров.
4. В определении, объявлении и вызове функции типы и порядок следования
параметров должны совпадать.
5. Передача параметров в функцию может выполняться по значению и по
адресу.
6. Входные данные функции надо передать по значению или по константной
ссылке, а результаты работы – через возвращаемое значение или при
необходимости вернуть более одной величины – через параметры по ссылке
или указателю.
7. Массивы всегда передаются в функцию по адресу. Количество элементов в
массиве должно передаваться отдельным параметром.
8. В многофайловом проекте надо уметь разбить задачу на подзадачи и
распределить функции по файлам. По сути файлы соответствуют отдельным
модулям. Но в С++ нет конструкций для обозначения модулей, но есть понятие
– единица трансляции. Единица трансляции представляет отдельный файл с
исходным текстом на С++, который получается после обработки
препроцессором. Такое разбиение на отдельные файлы требует последующей
сборки. Так как в С++ отсутствуют конструкции для обозначения модулей, то
нет и конструкций для их сборки в исполняемую программу. Эта задача
решается средствами интегрированной среды, в которой создается проект. В
составе проекта перечисляются все объектные модули. Процесс сборки полной
программы из объектных модулей называется компоновкой. Программа
линкования(компоновки)
входит
в
состав
системы
программирования.(linker- сборщик).При компоновке в программу
собираются и разрабатываемые пользователем модули(файлы) , и стандартные
модули из стандартных объектных библиотек. Разделение программы на модули
требует согласования определений и объявлений в разных единицах
трансляции.
О том, как оформить вашу задачу в виде проекта, можно прочитать в
приложении.
Задания к работе 5
1. Возьмите ваше задание по лабораторной работе 4_1 (Одномерные
массивы). Оформите каждый пункт задания в виде отдельной функции. Все
необходимые данные для функции передать в качестве параметров.
Использование глобальных параметров в функциях не допускается.
74
Дополнительное задание
2. Ввести с клавиатуры два целых числа и выбрать из них наибольшее. Ввод
осуществить в главной функции. Передачу исходных чисел произвести тремя
способами : передача по значению, по адресу с помощью ссылки и по адресу с
помощью указателя. Соответственно оформить три варианта написания
программы (повторить тему Указатели).
Контрольные вопросы.
1. Как связан принцип: “Разделяй и властвуй” с использованием функций?
2. Что требуется знать, чтобы использовать функцию? Как это можно назвать
одним словом?
3.Что необходимо знать для вызова функции?
4.Как передаются одномерные массивы в функцию?
5. Какие параметры функции называются формальными, а какие –
фактическими (аргументами)?
К лабораторным работам №8 , №9
ФУНКЦИИ
Структурное программирование предполагает, что большие вычислительные
задачи
могут быть разбиты на более простые логически законченные
подзадачи, которые можно оформить как функции. Программа на Си состоит из
некоторого набора таких небольших функций. Использование функций
позволяет выполнить описание функции только один раз, а вызвать функцию
для выполнения можно многократно из разных точек программы. Это позволяет
избежать избыточности кода, упрощает отладку и сопровождение программы.
ФУНКЦИЯ – это логически самостоятельная именованная часть программы,
75
которой можно передать параметры, и которая может возвратить значение.
Помещение фрагмента кода в функцию и передача всех необходимых ей
данных в качестве параметров называется инкапсуляцией, то есть скрытием
деталей реализации.
Для вызова такой функции достаточно знать ее интерфейс, определяемый ее
заголовком.
Для работы с функциями необходимо уметь:
 объявить (задекларировать) функцию;
 определить (описать) функцию;
 вызвать функцию.
Объявление (декларация) функции
Задает имя функции, тип возвращаемого значения и список передаваемых
параметров:
тип
имя_функции ([список формальных параметров]);
Если тип не указан, предполагается по умолчанию, что функция возвращает
целое типа int.
Список формальных параметров состоит из перечня типов и имен параметров,
разделенных запятыми.
Если параметров нет, то ( ) все равно обязательны.
В списке параметров для каждого параметра должен быть указан тип:
(int x, int y, float z)
Объявление функции – это ее заголовок, который называют прототипом или
сигнатурой. До первого вызова функции компилятор должен знать тип
возвращаемого результата, а также количество и типы аргументов. Только тогда
может быть создан правильный машинный код функции ( то есть объявления
функций должны находиться в тексте программ раньше ее вызова). Обычно
прототипы функций помещают в заголовочный файл, подключаемый
директивой #include к тексту программы.
Чаще всего прототип функции полностью совпадает с заголовком в описании
функции, хотя и не всегда так. Дело в том, что имена формальных параметров
не играют никакой роли, и могут игнорироваться компилятором:
int func(int a, float b, char*c);
int func(int, float, char*); //оба прототипа эквивалентны
Определение (описание) функции (стандарт ANSI)
76
тип имя_функции ([список формальных параметров])
{
тело функции
}
 Имя функциии -это особый тип указателя, называемый указателем на
функцию. Его значением является адрес точки входа в функцию.
Выполнение функции возвращает управление в точку вызова;
 Тип возвращаемого значения может быть любым, кроме массива и
функции (но может быть указателем на массив и функцию);
 Список формальных параметров - определяет величины,
передаваемые в функцию (типы, имена). Если в функцию ничего не
передается, то поле списка аргументов либо пусто (), либо (void).
Элементы списка разделяются запятыми;
 В объявлении, определении и вызове одной и той же функции типы и
порядок следования параметров должны совпадать. Имена
параметров могут не совпадать, так как функцию можно вызвать с
различными аргументами, а в прототипах имена игнорируются
компилятором.
Вызов функции
В простейшем случае надо указать имя
аргументов:
функции и имена передаваемых
имя_ функции([список фактических параметров]);
Вызов функции может находиться в любом месте программы, где по
синтаксису допустимо выражение того типа, которое формирует и возвращает
функция.
Обмен информацией между функциями
Любая программа на С++ - это совокупность определений переменных и
функций. Связи между функциями осуществляются
 с помощью глобальных переменных;
 через возвращаемое значение;
 с помощью передачи параметров.
Глобальные переменные видны во всех функциях, где не описаны
локальные переменные, имеющие те же имена. Однако их использование не
рекомендуется, так как это затрудняет отладку программы, поиск в ней ошибок
и препятствует помещению функций в библиотеки общего пользования.
Функции предпочтительно иметь максимально независимыми, и их интерфейс
77
должен полностью определяться прототипом функции.
Возвращаемые значения реализуется оператором return.
Оператор return имеет два варианта использования:
 вызывает немедленный выход из текущей функции и возврат в
вызывающую программу;
 может использоваться для возврата значения функции
return в теле функции может быть в нескольких вариантах, но может и не
быть вовсе, и тогда возврат в вызывающую программу происходит после
последнего оператора тела функции.
Каждая программа имеет только одну функцию с именем main. C этой
функции начинается исполнение программы. Другие функции могут быть
вызваны из функции main или из любой другой функции в процессе
исполнения программы. Каждая функция может иметь 0 или более параметров.
Параметры являются переменными, которые используются для передачи
данных между функциями. Имена параметров в вызываемой и вызывающей
функциях – независимы.
Если описание функции сделано до функции, в которой она вызывается, то
можно не создавать прототип. Если же описание следует после вызывающей
функции, то прототип обязателен. Корректный стиль написания программы
предполагает обязательное наличие прототипа.
Передача параметров
Механизм передачи параметров – это основной способ обмена информацией
между функциями. Параметры функции при описании называются
формальными параметрами. При вызове используются фактические
параметры (аргументы), которые потом становятся на место
формальных параметров.
При вызове функции сначала вычисляются выражения, стоящие на месте
аргументов; затем в стеке выделяется память под формальные параметры в
соответствии с их типами. Затем каждому формальному параметру
присваивается значение соответствующего аргумента. При этом идет проверка
соответствия типов и их преобразование в случае необходимости.
Существует два способа передачи параметров в функции:
 по значению: в стек заносятся копии значений аргументов, и
78
операторы функции работают с этими копиями; доступа к исходным
значениям параметров у функции нет, а значит, нет и возможности
их изменения;
 по адресу (с помощью указателя и по ссылке) В стек заносятся
копии адресов аргументов, а функция обращается к сегменту
данных по этим адресам и может изменить исходные значения
аргументов, так как имеет к ним доступ.
Пример
#include<iostream.h>
int func(int i, int *j, int& k);
int main() {
int i=1, j=2, k=3;
cout<<” i j k\n”;
cout<<i<<” “<<j<<” “<<k<<endl;
func(i, &j, k);
cout<<i<<” “<<j<<” “<<k;
return 0;
}
int func(int i, int* j, int& k){
i++;
(*j)++;
k++;
}
протокол работы.
i
j
k
1
2
3
1
3
4
Первый параметр i передается по значению. Его изменение в функции не
влияет на исходное значение.
Второй параметр j передается с помощью указателя, при этом для передачи в
функцию адреса фактического параметра используется операция взятия
адреса, а для получения его значения в функции требуется операция
разыменования.
Третий параметр k передается по адресу с помощью
ссылки. При передаче по ссылке в функцию передается адрес указанного при
вызове параметра, а внутри функции все обращения к параметру неявно
разыменовываются.
Использование ссылок вместо указателей улучшает
читаемость программы, избавляя от необходимости применять операции
получения адреса и разыменования. Использование ссылок вместо передачи по
значению более эффективно, так как не требует копирования параметров, а это
важно при передаче структур большого объема.
79
.
Имена функций – это всегда глобальные имена, видимые по умолчанию из всех
файлов проекта. Однако, прототипы функций действуют только в пределах
одного файла. Поэтому приходится иметь директивы препроцессора, связанные
с подключаемыми .h- файлами, содержащими прототипы библиотечных
функций.
Передача массивов в качестве параметров
При использовании массива в качестве параметра в функцию передается
указатель на его первый элемент, то есть массив всегда передается по адресу.
Но адрес указывает только на начало массива и ничего не говорит о его размере,
поэтому размерность массива надо передавать через отдельный параметр:
int function(int arr[], const int n);
int function(int *arr, const int n);
Оба варианта приведут к одному и тому же результату. И в том, и в другом
случае вы передаете адрес начала массива и количество его элементов.
Пример программы, работающей с функциями
Ввести с клавиатуры два целочисленных массива ‘а’ и ‘в’ и их размерности.
Кол-во элементов в массивах не должно превышать 10. В каждом массиве
вычислить произведение элементов, кратных 3. Первые элементы в каждом
массиве заменить на полученное произведение.
#include<stdio.h>
#define N 10
//(1)
int input(int a[],const int n);
int change(int a[],const int n);
int output(int a[],const int n);
//(2)
int main() {
int a[N],b[N],n1,n2;
printf("input razmer a=");
scanf("%d",&n1);
printf("input razmer b=");
scanf("%d",&n2);
input(a,n1);
change(a,n1);
output(a,n1);
input(b,n2);
//(3)
80
change(b,n2);
output(b,n2);
return 0;
}
int input(int a[],const int n) {
int i;
printf("input array:\n");
for(i=0;i<n;i++) {
printf("a[%d]=",i);
scanf("%d",&a[i]);
}
printf("\n");
return 0;
}
int change(int a[],const int n){
int i,pr=1;
for(i=0;i<n;i++)
if(a[i]%3==0)
pr*=a[i];
a[0]=pr;
}
int output(int a[],const int n) {
int i;
for(i=0;i<n;i++)
printf("a[%d]=%d\n",i,a[i]);
}
//(4)
//(5)
//(6)
Пояснения.
(1)-директивы препроцессора для подключения заголовочного файла
<stdio.h> и определение подстановки в текст программы;
(2) – прототипы функций input(),change(),output();
(3) – определение главной функции main(). Внутри функции main()
расположены вызовы функций input(),change() и output() и
настройка параметров этих функций сначала на работу с массивом а, затем с
массивом в;
(4) – определение функции input(), которое показывает, как будет
работать эта функция, когда мы ее вызовем сначала для ввода элементов
массива а, затем для ввода элементов массива в;
(5) –определение функции change(), которая ищет произведение только
тех элементов массивов а и в соответственно, которые нацело делятся на 3,
и помещает результат этого произведения на место первого элемента массива;
81
(6) – определение функции output(), которая печатает новые массивы.
Выводы
1.Функция – это логически самостоятельная именованная часть программы.
Использование функций упрощает структуру программы.
2. Интерфейс функции определяется ее заголовком.
3. Для вызова функции надо указать ее имя и набор фактических
параметров.
4. В определении, объявлении и вызове функции типы и порядок следования
параметров должны совпадать.
5. Передача параметров в функцию может выполняться по значению и по
адресу.
6. Входные данные функции надо передать по значению или по константной
ссылке, а результаты работы – через возвращаемое значение или при
необходимости вернуть более одной величины – через параметры по ссылке
или указателю.
7. Массивы всегда передаются в функцию по адресу. Количество элементов в
массиве должно передаваться отдельным параметром.
8. В многофайловом проекте надо уметь разбить задачу на подзадачи и
распределить функции по файлам. По сути файлы соответствуют отдельным
модулям. Но в С++ нет конструкций для обозначения модулей, но есть понятие
– единица трансляции. Единица трансляции представляет отдельный файл с
исходным текстом на С++, который получается после обработки
препроцессором. Такое разбиение на отдельные файлы требует последующей
сборки. Так как в С++ отсутствуют конструкции для обозначения модулей, то
нет и конструкций для их сборки в исполняемую программу. Эта задача
решается средствами интегрированной среды, в которой создается проект. В
составе проекта перечисляются все объектные модули. Процесс сборки полной
программы из объектных модулей называется компоновкой. Программа
линкования(компоновки)
входит
в
состав
системы
программирования.(linker- сборщик).При компоновке в программу
собираются и разрабатываемые пользователем модули(файлы) , и стандартные
модули из стандартных объектных библиотек. Разделение программы на модули
требует согласования определений и объявлений в разных единицах
трансляции.
Контрольные вопросы.
1. Как связан принцип: “Разделяй и властвуй” с использованием функций?
2. Что требуется знать, чтобы использовать функцию? Как это можно назвать
одним словом?
3.Что необходимо знать для вызова функции?
4.Как передаются одномерные массивы в функцию?
5. Какие параметры функции называются формальными, а какие –
фактическими (аргументами)?
82
Приложение
Создание нового проекта в интегрированной среде NetBeans 6.8
Integrated development Environment( интегрированная среда
разработки) – это программный продукт, который объединяет текстовый
редактор, компилятор, отладчик, компоновщик. Вам предстоит работать со
средой NetBeans IDE 6.8. Любую программу, даже самую простую, мы
будем оформлять как отдельный проект.
Проект- это набор взаимосвязанных исходных файлов и заголовочных
файлов, компиляция и компоновка которых позволяет создать исполняемую
программу.
После запуска NetBeans вы увидите перед собой экран с верхней полоской
меню (Рис.П.1).
83
Рис. П. 1
Вызываем пункт меню File/New Project. В появившемся окне New
Project выбираем Categories C/C++(подсветка слева).
Справа щелчком подсвечиваем пункт C/C++Application, переходим к
следующей операции щелчком Next (Рис. П. 2).
84
Рис. П. 2
Далее предлагается выбрать имя проекта и его местоположение(Project
Name and Location). Имя, которое вы дадите проекту, получит и папка, в
которой этот проект будет храниться.
В окошечке Set as Main project не забудьте поставить птичку. И
нажмите Finish(Рис. П. 3).
85
Рис. П. 3
Слева в окне с названием Projects появится значок с именем вашего проекта.
Щелкнув по этому значку , вы увидите из каких файлов состоит ваш проект:
Header files
Resource Files
Source Files
Important Files.
Выделите Sourse Files цветом , войдите в меню File/New File и
выберите С++Files и Main C++Files, нажмите кнопку Next (Рис. П.
4).
86
Рис. П .4
Теперь определитесь с именем файла, расширением(cpp) и папкой, в которой
будете хранить файл, и нажмите кнопку Finish (Рис. П. 5).
87
Рис. П. 5
Пример выполнения проекта
Возьмем пример, приведенный в разделе функций и выполним его в виде
проекта.
Постановка задачи.
Ввести с клавиатуры два целочисленных массива ‘а’ и ‘в’ и их размерности.
Количество элементов в массивах не должно превышать 10. В каждом массиве
вычислить произведение элементов, кратных 3. Первые элементы в каждом
массиве заменить на полученное произведение. Ввод элементов массивов a и b
,обработку массивов и вывод результатов оформить в виде функций.
Окончательно задача должна быть выполнена в виде проекта.
В разделе исходных файлов (sourse files) будут находиться файлы с
расширением .cpp. Это главный файл (main.cpp) и предположим, файл с
именем func.cpp. В файле func.cpp разместим все определения функций
input,change и output.
В разделе Header Files разместим файл с именем func.h. В этом
88
файле разместим прототипы наших функций:
func.h
#ifndef_FUNC_H // “страж определен?” if !defined
// _FUNC_H , если не определен, то
// его надо определить
#define_FUNC_H //определение стража
#endif/*_FUNC_H*/
// конец #ifndef
int input(int a[],const int n);
int change(int a[],const int n);
int output(int a[],const int n);
Такая последовательность команд препроцессора называется стражем
включения и предназначена для предотвращения повторного включения
содержимого файла в программу (эти команды препроцессора поместит в файл
сама система).
В разделе Sourse Files разместим файл с именем func.cpp. В этом
файле разместим определения наших функций input(),change() и
output() (все функции, кроме main()).
func.cpp
#include<stdio.h>
#include”func.h”
int input(int a[],const int n) {
int i;
printf("input array:\n");
for(i=0;i<n;i++) {
printf("a[%d]=",i);
scanf("%d",&a[i]);
}
printf("\n");
return 0;
}
int change(int a[],const int n){
int i,pr=1;
for(i=0;i<n;i++)
if(a[i]%3==0)
89
pr*=a[i];
a[0]=pr;
}
int output(int a[],const int n) {
int i;
for(i=0;i<n;i++)
printf("a[%d]=%d\n",i,a[i]);
}
Это можно показать так:
func.cpp
#include <stdio.h>
int input(int a[],const int n) {
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
}
int change(int a[],const int n) {
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
}
int output(int a[],const int n) {
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
}
Функция main() тоже помещена в разделе Sourse Files, но отдельно
от других наших функций.
main.cpp
#include<stdio.h>
#include”func.h”
int main() {
int a[N],b[N],n1,n2;
printf("input razmer a=");
scanf("%d",&n1);
printf("input razmer b=");
scanf("%d",&n2);
input(a,n1);
change(a,n1);
output(a,n1);
90
input(b,n2);
change(b,n2);
output(b,n2);
return 0;
}
Это можно показать так:
main.cpp
#include<stdio.h>
#include”func.h”
int main() {
……. . . . . . .
. . . . . . . .
return 0
}
Обратите внимание, мы подключая с помощью директивы #include свой
пользовательский заголовочный файл (не стандартный) используем кавычки, а
не привычные уголки(“func.h”).
В окне Projects, щелкнув по значку с именем вашего проекта и раскрыв
значки Header Files и Sourse Files вы увидите как разместились
ваши функции в этих разделах:
Header Files
func.h
Sourse files
main.cpp
func.cpp
Создание проекта завершено, и системе можно поручить компиляцию,
компоновку и постановку программы на счет.
Попробуйте самостоятельно нарисовать этапы создания исполняемой
программы конкретно для этой задачи (для этого повторите материал введения
и внимательно рассмотрите в качестве образца рис.1.1.)
Выполняя лабораторные и самостоятельные работы каждую свою программу
вы будете представлять в виде одного отдельного проекта. Для каждого проекта
будет создана отдельная папка.
Надеемся, что если в данный момент изученный вами материал все еще не
кажется совсем легким, то, по крайней мере, многое стало понятным.
Желаем успехов!
91
92
Download