4842

Информатика - Основы алгоритмизации и программирование

Конспект

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

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

Русский

2012-11-27

732.5 KB

59 чел.

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

При изложении материала предполагается, что студентами освоена программа I семестра в части принципов работы ЭВМ и представления в памяти ПК чисел, символов и команд. Также считается, что студенты владеют сведениями о программном обеспечении ПК и функционировании операционных систем в объеме программы I семестра.

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

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

Рекомендуемая литература

а) основная литература

  1.  Керниган, Д. Ритчи. Язык программирования Си (пер. с англ.). — М.: Финансы и статистика, 1992.
  2.  Березин Б.И., Березин С.Б. Начальный курс С и С++ Диалог МИФИ 1996
  3.  Дейтел Х., Дейтел П. Как программировать на С++: Пер. с англ. – М.: ЗАО «Издательство БИНОМ», 1998 г. – 1024 с.: ил.
  4.  Луис Дерк. Справочник С и С++. М., Бином, 1997

б) дополнительная литература:

  1.  Паппас К., Мюррей У. Программирование на C и C++. Библиотека студента. BHV 2000.
  2.  Культин Н.Б. С/С++ в задачах и примерах. – Спб.: БХВ-Петербург, 2001.-288 с.: ил.
  3.  Крячков А.В. и др. Программирование на С и С++. Практикум. М.: Радио и связь. 1997.
  4.  Подбельский В.В. Язык С++. М.: Финансы и статистика, 1996.
  5.  Ишкова Э.А. С++. Начала программирования – М.: ЗАО «Издательство БИНОМ», 2000. – 304 с.: ил.

  1.  Этапы решения инженерных задач с помощью ЭВМ.

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

В прошлом семестре вы использовали один из таких пакетов - MS Excel - для исследования влияния начальных условий на поведение реального физического объекта - парашютиста, строили траектории сложного движения точки и диаграммы распределения доходов фирмы в течение года. Другие математические пакеты, такие, как MathCad, Mathematics и другие, вы будете изучать на старших курсах.

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

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

  1.   Физическая постановка задачи. Здесь производится выработка физической модели процесса или явления, т.е. формулировка предположений, отражающих наши представления о нем. В нашем случае при малых скоростях можно предположить, что земля плоская, а также нет сопротивления воздуха.
  2.   Математическая постановка задачи. На этом этапе на основе физической формулировки задачи выбираются переменные, подлежащие определению, записываются ограничения, связи между переменными, в совокупности образующие математическую модель решаемой проблемы. В результате инженерная задача приобретает вид формализованной математической задачи, записанной в виде уравнений.

При нашей физической модели можно записать хорошо известные уравнения движения точки  x=V0 cos t; y= V0 sin t - gt2/2; S =  vdt. При этом интеграл вычисляется от 0 до t так, чтобы y конечное = 0

  1.   Выбор или разработка метода численного решения. Для поставленной математической задачи необходимо выбрать метод ее численного решения, сводящий решение задачи к последовательности арифметических и логических операций. Разработкой и изучением таких методов занимается раздел математики, называемый численным анализом. Примером численного метода в нашем случае является метод прямоугольников для вычисления определенного интеграла. В этом методе интеграл заменяется конечной суммой.
  2.   Разработка алгоритма решения. Это означает, что на основе выбранного метода записывается последовательность действий, приводящих к решению задачи . Разработка алгоритма обычно завершается либо представлением в виде графической схемы, либо записью с помощью символов специального языка проектирования программ, называемого псевдокодом. Используются также другие средства представления логики алгоритма: HIPO-диаграммы,  таблицы решений и др. Цель такого представления состоит в том, чтобы еще до этапа программирования убедиться в правильности логики разрабатываемого алгоритма.
  3.   Разработка интерфейса пользователя. Параллельно с разработкой алгоритма разрабатывается сценарий работы программы, т.е. ее представление на экране при взаимодействии с пользователем.
  4.   Написание программы. На этом этапе алгоритм записывается в виде последовательности предложений на понятном ЭВМ языке.
  5.   Отладка программы. При реализации алгоритма в виде программы и ее вводе с клавиатуры в память ЭВМ могут быть допущены ошибки. Их обнаружение, локализацию и устранение выполняют на этапе отладки программы.

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

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

  1.   Отладка метода и алгоритма. На этом этапе, наиболее трудоемком, производится тестирование программы, т.е. проверяется, правильно ли результаты ее работы отражают предметную область, для моделирования которой программа создана. С этой целью проводятся многочисленные расчеты, результаты которых сравнивают с уже известными экспериментальными данными и теоретическими решениями. При несовпадении результатов приходится изменять алгоритм или метод решения, а возможно и строить другую физическую модель.
  2.   Серийные расчеты. На этом этапе проводятся многочисленные предусмотренные постановкой задачи расчеты по исследованию влияния исходных данных на характеристики процесса. Результаты стараются выводить в удобном для просмотра и анализа виде.
    1.  Алгоритмы решения задач.

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

  1.  Понятие алгоритма, его свойства.

Понятие алгоритма обсуждалось в прошлом семестре. Рассмотрим его более полно.

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

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

Свойства алгоритма

  1.  Дискретность. Это означает, что предопределенный алгоритмом вычислительный процесс должен состоять из простых элементарных операций, которые может выполнить человек или машина.
  2.  Определенность. Алгоритм должен быть однозначным, исключающим произвольность толкования любого из предписаний и заданного порядка исполнения.
  3.  Полнота. Это свойство означает, что должны быть предусмотрены все возможные варианты работы алгоритма при любых допустимых значениях исходных данных.
  4.  Универсальность (массовость). Решение однотипных задач с различными исходными данными можно осуществлять по одному и тому же алгоритму, что дает возможность создавать типовые программы для решения задач при различных вариантах задания значений исходных данных.
  5.  Результативность. Реализация вычислительного процесса, предусмотренного алгоритмом, должна через определенное число шагов привести к выдаче результатов или сообщения о невозможности решения задачи. Это свойство не всегда должно выполняться, поскольку существуют такие циклические процессы, которые не должны прекращаться (например, плавка металла в доменных печах или выработка электроэнергии на электростанциях). Ясно, что алгоритмы таких процессов не имеют окончания за конечное число шагов.

Если алгоритм рассматривать как совокупность предписаний по выполнению действий, то всегда необходимо выделить те объекты, над которыми должны  осуществляться  предписанные действия. Такими объектами являются данные.

  1.  Обозначения элементов алгоритмов (блоки).

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

К средствам описания алгоритмов относятся следующие основные способы их представления:

словесный (записи на естественном языке);

структурно-стилизованный (записи с помощью символов специального языка проектирования программ, называемого псевдокодом);

графический (изображения схем из графических символов);

программный (тексты на языках программирования).

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

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

В схеме алгоритма каждому типу действий (вводу исходных данных, вычислению значений выражений, проверке условий, управлению повторением действий, окончанию обработки и т. п.) соответствует геометрическая фигура, представленная в виде блочного символа (блока), называемого символом действия. Символы действия соединяются линиями переходов, определяющими очередность выполнения действий. Форма символов установлена ГОСТ 19.003—99, а правила составления схем алгоритмов — ГОСТ 19.002—99.

Наиболее часто употребляемые символы действий приведены в Табл.1.

  1.  Базовые управляющие конструкции.

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

Различные варианты этих базовых структур отображены в Табл.2. Там же указаны примеры, словесное описание алгоритма структуры и синтаксис записи ее на языке С. Последнее будет полезно в будущем при переводе алгоритмов в программный код.

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

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

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

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

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


Основные блочные символы.

Таблица 1.

Название

Внешний вид

Пример

Назначение

Линии передачи управления (потока информации)

       

       

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

Соединение линий потока информации

    

    

Служит для соединения нескольких линий в одну. Стрелки ставятся при направлении линии справа налево и снизу вверх.

Начало алгоритма

Внутри указывается назначение или название алгоритма. Вход может быть только один.

Конец алгоритма

Внутри можно указать вычисляемое в алгоритме данное. Выходов из алгоритма может быть несколько.

Блок обработки данных (последовательное действие или процесс)

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

Ветвление (развилка)

Используется для обозначения переходов управления в зависимости от выполнения условия. Если условие истинно, управление передается по стрелке да, в противном случае - по стрелке нет.

Ввод данных (с клавиатуры)

Используется для обозначения операций ввода информации. Пример: ввести значения переменных a и b с клавиатуры.

Вывод результатов на бумагу

Используется для обозначения операций вывода информации. Пример: вывести значения переменных x и y.

Ввод или вывод данных (без указания устройства)

Используется для обозначения операций ввода или вывода информации без указания устройства. Пример: ввести значения переменных a и b.

Предопределенный процесс (вызов модуля)

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

Разрыв линии передачи управления и ее продолжение

 

  

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

Перенос алгоритма на следующую страницу

 

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

Текстовые пояснения

Используется для дополнения схемы алгоритма текстовыми пояснениями.


Базовые алгоритмические структуры.

Таблица 2.

Внешний вид

Пример

Словесное описание алгоритма

Синтаксис записи на С++

Последовательность

         

          

Действия выполняются в порядке их следования в алгоритме. Знак = в действии предписывает вычислить выражение справа от него и занести результат в переменную слева от него.

x=d+sin(g);

y=y+3;

Второе действие предписывает сложить y и 3, а результат записать туда же, т.е. увеличить y на 3.

Ветвление

  1.  Вычисляется условие.
  2.  Если оно истинно, то

выполняется действие1.

Иначе

выполняется действие2.

3.Переход к следующему действию (продолжение алгоритма).

if (условие)

 действие1;

else

 действие2;

Пример:

if (a>b)

 c=c+1;

else

 c=c+2;

  1.  Вычисляется условие.
  2.  Если оно истинно, то

выполняется действие1.

3.Переход к следующему действию (продолжение алгоритма).

if (условие)

 действие1;

Пример:

if (x<0)

 y=x;

Условия проверяются сверху вниз. Как только встречается истина, выполняется соответствующее действие и последовательность проверок прекращается. Последнее действие срабатывает, если все предыдущие условия ложны.

if (условие1)

 действие1;

else if (условие2)

 действие2;

else if (условие3)

 действие3; 

·························

else действие n; 

Повторение (цикл)

Цикл с предусловием.

  1.  Вычисляется условие.
  2.  Если оно истинно, то
  3.  Выполняется действие.
  4.  Переход к пункту 1.

Иначе

выход из цикла (продол   жение алгоритма).

while ( условие )

действие; 

Пример:

while ( a<5 )

 a=a+2;

Цикл с постусловием.

  1.  Выполняется действие.
  2.  Вычисляется условие.
  3.  Если оно истинно, то

 Переход к пункту 1.

Иначе

выход из цикла (продол   жение алгоритма).

do

 действие

while (условие);

Пример:

do

 x=x-1;

while ( x>0 );

Цикл с параметром (пошаговый).

  1.  Вычисляется начальное значение счетчика.
  2.  Вычисляется условие (продолжения цикла);
  3.  Если оно истинно, то
  4.  Выполняется действие;
  5.  Вычисляется новое значение счетчика;
  6.  Переход к пункту 2.

Иначе - выход из цикла (продолжение алгоритма).

for( начальное значение счетчика;

условие продолжения цикла;

изменение счетчика) 

повторяемое действие

Пример:

s=0;

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

 s=s+i;


  1.  Некоторые стандартные приемы алгоритмизации.

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

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

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

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

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

1. Поменять местами содержимое двух переменных (три ведра).

Назовем эти переменные a и b (пусть их значения равны 15 и -3) и изобразим их на схеме. Чтобы поменять их содержимое местами, добавим еще одну (буферную) переменную c, в которой временно запомним значение переменной a (1-е действие). Затем занесем в a значение переменной b (2-е действие), а затем в b - c (3-е действие). После того, как продумали таким образом алгоритм, изобразим его в виде блок-схемы.

              

                   

  1.  Определить, имеют ли две переменные один знак.

Чтобы узнать одного ли знака две переменные, достаточно их перемножить. Если результат положительный, то переменные одного знака.

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

  1.  Проверить делимость числа на 3.

Если число делится нацело, то остаток от деления будет равен нулю. Чтобы узнать этот остаток, нужно запомнить в какой-либо переменной (например, b) результат деления, а в переменной c - его целую часть (целую часть числа принято обозначать квадратными скобками). Тогда разность b и c и является целой частью результата деления.

  1.  Алгоритм обработки массива.

При решении задач с массивами чисел или символов чаще всего используется один и тот же алгоритм единообразной поочередной обработки каждого элемента массива в отдельности. Для этого обычно применяется циклический алгоритм с параметром, рассмотренный справа. Массив a из n элементов будем обозначать {a}n, а элемент массива с номером i - ai.

Сначала вводится количество n элементов массива a  и сам массив (блок 1). Затем проводятся подготовительные операции (блок 2), содержание которых зависит от решаемой задачи. Обычно здесь задаются начальные значения вспомогательным переменным. Блоки 3, 4 и 6 являются обязательными, именно они реализуют цикл и позволяют поочередно обработать каждый элемент массива. Блок 3 задает номер начального элемента обрабатываемого массива, блок 4 позволяет закончить обработку и выйти из цикла после превышения номером текущего элемента значения n, а блок 6 позволяет после обработки текущего элемента перейти к следующему, увеличив номер элемента на 1.

При обработке i-го элемента массива в некоторых случаях возможен досрочный выход из цикла, тогда в блок-схеме появляется блок 9, на который передается управление прямо от блока 5.

После окончания обработки производят вывод исходного массива для контроля правильности его ввода (блок 7), а затем анализируют и печатают результаты обработки (блок 8).

  1.  Найти сумму элементов массива.

В качестве примера рассмотрим нахождение суммы элементов массива. В блоке 2 введем переменную s, в которой будем копить искомую сумму. Чтобы найти действие, которое

s=0

s=0+a1 

s=0+a1 +a2 

s=0+a1 +a 2+ a3

s=0

s=s+a1 

s=s+a2 

s=s+a3

надо повторить для каждого элемента массива (блок 5), рассмотрим, что находится в s при прибавлении к сумме каждого следующего элемента. Из таблицы справа видно, что в s каждый раз заносится то, что там было после предыдущего шага (подчеркнуто), плюс текущий элемент, т.е. s=s+ai . В блоке анализа результатов (блок 8) просто напечатаем значение s.

  1.  Найти максимальный элемент массива.

Рассмотрим нахождение максимального элемента массива. Для определения повторяющегося для каждого элемента действия представим массив в виде набора ячеек, содержащих указанные справа значения. Ниже укажем переменную amax, в которой будет находиться значение макси-

мального из уже рассмотренных элементов.

Вначале занесем туда первый элемент, как наибольший из рассмотренных (блок 2). На каждой итерации (блок 5) будем заносить туда текущий элемент ai, если он больше предыдущего максимального (при этом старое значение стирается), если же нет, то оставлять там то, что было. После окончания обработки массива в amax останется самый большой элемент. В блоке 8 анализа результатов  просто напечатаем значение amax .

  1.  Использование флага наступления события.

В некоторых задачах требуется обработать не все элементы массива, а только соответствующие некоторому условию. В этом случае после обработки массива иногда неизвестно, были найдены такие элементы или нет. Чтобы узнать это, используют прием алгоритмизации, называемый флагом. Перед обработкой массива (в блоке 2) в некоторую переменную (назовем ее flag) заносят число 0. В процессе обработки элементов (блок 5) в эту переменную заносят число 1, если обрабатываемый элемент удовлетворяет заданному условию. Тогда после окончания обработки достаточно проверить значение этой переменной - если там остался ноль, то подходящих

элементов не нашлось.

В качестве примера рассмотрим поиск первого четного элемента массива. Перед циклом в блоке 2 занесем в переменную flag число 0. При обработке i-го элемента массива (блок 5) в случае его четности занесем в flag число 1 и сделаем досрочный выход из цикла, передав управление на блок 9, в противном случае продолжим цикл, перейдя на блок 6.

После окончания обработки (блок 8) проверим значение переменной flag. Если оно не 0, значит четные элементы были, поэтому выводим ai.

Построить таблицу значений функции на отрезке и найти ее максимальное значение.

  1.  Системы программирования.
    1.  Состав.

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

В состав системы программирования обычно входят:

описание применяемого языка программирования;

текстовый редактор, позволяющий ввести текст программы и записать его в файл на диске;

программы-трансляторы, переводящие исходный текст программы в машинный код;

развитую библиотеку стандартных подпрограмм;

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

  1.  Язык программирования, алфавит, синтаксис, семантика.

Программа – набор команд на понятном ЭВМ языке, реализующих заданный алгоритм.

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

Синтаксис – это набор правил написания языковых конструкций.

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

  1.  Реализация языка.

Реализация языка (транслятор) - это программа, которая переводит (преобразует) записи на языке высокого уровня в последовательность машинных команд.

Имеются два основных вида средств реализации языка: компиляторы и интерпретаторы.

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

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

В принципе любой язык программирования может быть как интерпретируемым, так и компилируемым, но в большинстве случаев у каждого языка есть свой предпочтительный способ реализации. Языки Фортран, Си, Паскаль в основном компилируют, языки Бейсик и Лисп широко используют оба способа.

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

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

  1.  Характеристики языков программирования.

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

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

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

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

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

Основные отличия алгоритмических языков от машинных языков со стоят в следующем:

  1.   алгоритмический язык обладает гораздо большими выразительными возможностями, т.е. его алфавит значительно шире алфавита машинного языка, что существенно повышает наглядность текста программы;
  2.   набор операций, допустимых для использования, не зависит от набора машинных операций, а выбирается из соображений удобства формулирования алгоритмов решения задач определенного класса;
  3.   формат предложений достаточно гибок и удобен для использования, что позволяет с помощью одного предложения задать достаточно содержательный этап обработки данных;
  4.   требуемые операции задаются в удобном для человека виде, например с помощью общепринятых математических обозначений;
  5.   для задания операндов операций используемым в алгоритме данным присваиваются индивидуальные имена, выбираемые программистом, и ссылка на операнды производится главным образом путем их имен;
  6.   в языке может быть предусмотрен значительно более широкий набор типов данных по сравнению с набором машинных типов данных.

Таким образом, алгоритмический язык в значительной мере является машинно-независимым.

Языки программирования характеризуются двумя основными параметрами:

  1.  Мощностью, которая определяется разнообразием задач, решение которых может быть достигнуто с помощью этого языка.
  2.  Уровнем, который определяется легкостью решения сложных задач с помощью этого языка. Чем проще реализуются в синтаксисе языка сложные операции и понятия, тем меньше объем получаемых на этом языке программ.

Можно сказать, что чем мощнее язык, тем ниже его уровень. Самым мощным языком является машинный код.

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

Специализированные языки предназначены для решения задач в какой-либо одной области. Например, Кобол - для решения экономических задач, а Лисп - для обработки данных списковой структуры.

Указанные выше признаки - мощность и уровень определяют следующие свойства языка:

  1.  Надежность - обеспечивает низкий уровень ошибок при написании программ, позволяет большинство ошибок выявить на этапе трансляции (Паскаль).
  2.  Простота - легкость понимания семантических конструкций языка (Паскаль, Бейсик).
  3.  Гибкость - легкость выражения необходимых действий (Си).
  4.  Полнота - способность обеспечивать решение класса задач предметной области.
  5.  Мобильность - независимость от аппаратных средств (Си).
  6.  Эффективность - эффективные коды реализации (Фортран, Си).
    1.  Целевое назначение систем программирования.

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

  1.  Машинно-ориентированные.

Машинно-ориентированные системы программирования имеют входной язык, зависящий от особенностей построения определенной машины или семейства машин. Наиболее типичными представителями таких систем программирования являются системы символического кодирования и ассемблеры.

Системы символического кодирования.

Система символического кодирования (автокод) - одна из первых систем, созданных для автоматизации программирования с использованием входных языков по принципу "один к одному" (например, широко распространенный в конце 1970 годов автокод Чайковского). Этот принцип предполагает, что одному символическому оператору соответствует одна машинная команда или константа. Смысл применения подобной системы состоит в использовании символических обозначений вместо машинных кодов, в применении автоматического распределения памяти и присвоении действительных адресов.

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

Ассемблеры.

В настоящее время широкое применение из машинно-ориентированных языков нашел язык ассемблера. Как правило, в языке ассемблера существует четыре типа операторов:

1) мнемоническая команда - соответствует одной машинной команде, в ней вместо машинных кодов операций используются мнемонические обозначения;

2) псевдокоманда - служит для передачи информации программе-транслятору и не порождает команд на машинном языке;

3) макрокоманда - соответствует нескольким машинным командам;

4) условная команда ассемблера - используется для управления процессом трансляции.

Под ассемблером понимают транслятор, выполняющий перевод программы, записанной на языке ассемблера, на машинный язык.

  1.  Машинно-независимые.

Машинно-независимые системы программирования строятся на основе процедурно-ориентированных и проблемно-ориентированных языков.

  1.  Процедурно-ориентированные.

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

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

  1.  Проблемно-ориентированные.

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

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

Среди проблемно-ориентированных систем все большее значение приобретают непроцедурные (описательные) способы, ориентированные на «нетрадиционные» применения вычислительной техники: использование естественного языка, построение банков данных и баз знаний, создание экспертных систем и т. п., которые принято относить к проблематике искусственного интеллекта.

Наиболее известными из непроцедурных языков являются SQL-подобные языки, а также язык ПРОЛОГ. Программа, написанная на них, не содержит формул, предписаний, что сделать для получения результата. Программа констатирует, какой результат желателен, однако не указывает как этого достичь, Иными, словами, программа описывает не, процедуру решения задачи, а логическую модель предметной области - некоторые факты относительно свойств предметной области и отношений между этими свойствами, а также правила вывода новых свойств и отношений из уже заданных.

  1.  Этапы обработки программы машиной.

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

  1.  Написание программы. Здесь происходит достаточно формальный процесс перевода алгоритма с языка, использованного при его разработке, в текст программы на алгоритмическом языке высокого уровня (у нас это Си). При написании программы учитывается разработанные ранее сценарий ее работы и интерфейс пользователя.
  2.  Создание программы на диске. На этом этапе с помощью программы редактор текстов, обычно входящую в систему программирования, текст программы записывается на жесткий диск. Результатом этапа редактирования является текстовый файл программы на языке Си с именем, которое дает программист, и расширением .c или .cpp. Этот файл имеет название исходный модуль. Программа может быть записана и в нескольких исходных модулях, т.е. состоять из нескольких файлов.
  3.  Компиляция программы. На этапе компиляции каждый исходный модуль переводится с языка высокого уровня в машинный код. Это действие производится с помощью программы компилятор, обязательно входящей в систему программирования. Результатом работы компилятора является файл на диске, имеющий то же имя, что и исходный модуль, а расширение .obj. Этот файл носит название объектный модуль. Побочным результатом компиляции в случае наличия синтаксических ошибок в программе являются сообщения компилятора о них на экране дисплея. В этом случае объектный модуль не формируется.
  4.  Сборка (компоновка, редактирование связей). На этом этапе все объектные модули с помощью программы компоновщик (или редактор связей) компонуются (собираются) в одну исполняемую программу на машкоде. Результатом работы компоновщика является файл, который называется загрузочный модуль. Его имя совпадает с именем первого из объектных модулей, участвующих в сборке, а расширение - .exe. Вместе с объектными модулями компонуются и используемые в программе стандартные функции из специальных библиотек, входящих в систему программирования. При этом формируются так называемые перекрестные ссылки (связи), т.е. адреса расположения в памяти используемых функций.
  5.  Загрузка. С помощью программы загрузчик (или интерпретатор команд операционной системы, обычно это файл command.com), программа загружается в оперативную память и управление передается на ее первую команду (принцип Фон-Неймана). Обычно в начале работы программы вводятся исходные данные либо из файла, либо пользователем с клавиатуры. В процессе выполнения программа выводит результаты на экран, принтер или в файл на диске.

Табл.3

Смысл объекта

Вид представления

Место хранения

Язык

Имя хранения

Название объекта

Алгоритм

Блок-схема

в тетради

Язык -графический

Имя алгоритма

блок-схема задачи

Программа

текст

в тетради

на языке Си

Имя программы

текст программы

Программа

Файл

на диске

на языке машкод

имя.exe

загрузочный модуль

Программа

Файл

на диске

на языке машкод

имя.obj

объектный модуль

Программа

Файл

на диске

на языке Си

имя.cpp

исходный модуль

Результаты

Числа и текст

на экране

или в файле

На русском языке

Исходные данные

Числа и текст

Ввод с клавиатуры

или из файла

На русском языке

Стандарт-е функции

Объектный модуль

на диске

на языке машкод

.lib, .obj

Библ.станд.функций


  1.  Основные понятия языка Си++.
    1.  История создания.

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

Самым первым в ряду предшественников Си следует считать язык Алгол-60, разработанный в 1960 г. Международным комитетом. В Алголе впервые большое внимание было уделено модульной структуре программы. На базе Алгола в 1963 году возник язык CPL (Conbined Programing Language - комбинированный язык программирования) - совместная разработка Кембриджского и Лондонского университетов.

Язык BCPL, созданный Мартином Ричардом в 1967 г., был получен выделением из CPL его основных свойств. Ещё большее упрощение представлял собой язык В (1970 г Кен Томпсон, Bell Laboratories). Но экономия средств языков BCPL и В привела к тому, что они стали ограниченными, пригодными только для узкого круга задач.

Язык Си был создан в 1972 г Денисом Ритчи (Bell Laboratories) как инструментальное средство операционной системы UNIX. Достижением при разработке языка Си было восстановление потерянной общности за счёт удачной системы типов данных, но без потери простоты, что и было целью разработки языка CPL.

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

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

  1.  
    1.  Алфавит, структура программы.

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

  1.  Алфавит.

Предложения любого языка пишутся на основе некоторого алфавита, т.е. набора символов, используемых для записи инструкций языка, которые компилятор переводит в машинный код. В С++ к алфавиту относятся все буквы латинского алфавита, цифры от 0 до 9, символы кириллицы и специальные символы, а именно все, которые есть на клавиатуре ЭВМ, в частности: . , ; : ' " ? / * + - _ ! @ & { } ( ) [ ] и др. Необходимо отметить, что компилятор языка С++ отличает большие и маленькие буквы латинского алфавита.

  1.  Структура программы.

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

Комментарии в C++ предназначены для удобства чтения программы и содержат пояснения к тексту инструкций. Они игнорируются транслятором и обозначаются в программе следующими символами:

// в любом месте строки, действителен до конца строки

/* в начале комментария и */ в конце, действителен в любом месте текста

Операторы языка делятся на исполняемые и неисполняемые. Неисполняемые содержат дополнительные инструкции компилятору для перевода текста в машинный код. Эти инструкции отсутствуют в exe-модуле. Исполняемые операторы непосредственно переводятся в машинный код.

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

Основными видами операторов, определяющих структуру программы, являются:

директивы препроцессора;

объявления функций;

описания переменных - (для хранения данных);

описания (определения) функций - (описывают вычисления);

Сразу напишем пример программы.


Пример 1. Программа, вычисляющая площадь треугольника:

#include <iostream.h>  // Директива препроцессора

float pl(float a, float b); //Объявление (прототип) функции pl()

       // Описание функции main

void main()                            // Заголовок описания функции main

{

 float katet1,katet2; // описание переменных

 cout << "Введите катеты (в см): ";            // Инструкции, 

 cin >> a,b;                          // описывающие 

 cout << "Площадь треугольника ="          // действия

      << Pl(katet1,katet2) << "см*см\n";

}

                     // Описание функции pl()

/**** Расчет площади прямоугольного треугольника *********** 

****по его катетам a и b . Возвращает площадь ********/

float pl(float a, float b)// Заголовок описания функции pl()

{

Тело

функции

pl()

 float s; // объявление переменной s

 s=a*b/2;      // Инструкции,  

 return s;     // описывающие

}               // действия

Рассмотрим указанные выше типы операторов.

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

Описания (или определения, definition) функций следуют одно за другим в произвольном порядке и имеют вид (синтаксис)

Тип имя([список параметров])              -заголовок

{

Тело

функции

 описания переменных  (переменные хранят значения)                                      

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

                                              описывающие действия)                           

}

(квадратные скобки в наших правилах записи операторов, т.е. в синтаксисе, будут означать необязательные параметры).

Имя (идентификатор) - последовательность латинских букв и цифр, начинающихся с буквы, символ "_" - тоже буква. Строчные и прописные буквы различаются. Длина имени - не больше 32 символов.

Имена функциям дает программист, однако одна из них должна иметь имя main(), поскольку именно с нее всегда начинается выполнение программы. В примере описаны 2 функции - main() и pl(). Выполнение начинается с функции main(), из которой вызывается функция pl().

Тип указывает на тип вычисляемого (возвращаемого) функцией значения. Если функция ничего не возвращает, то в качестве типа пишется слово void.

Любая функция должна быть описана до обращения к ней (до вызова), чтобы компилятор мог понять, правильно ли это обращение. Если описание указано после обращения, в начале программы (до функции main() ) должен быть указано объявление этой функции (прототип, declaration) в виде ее заголовка с ";" в конце.

Для стандартных функций прототипы уже имеются в специальных header-файлах, которые поставляются вместе с другими файлами пакета Си++ и имеют расширение ".h". Чтобы не указывать самостоятельно прототипы используемых стандартных функций, можно с помощью директивы препроцессора #include включить этот файл в текст программы. Например, прототипы стандартных потоков ввода и вывода cin и cout находятся в файле iostream.h . При выполнении директивы

#include <iostream.h>

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

  1.  Типы данных Си. Константы. Переменные. Описания переменных.

Данные в Си могут быть одного из четырех базовых типов (они приведены в порядке возрастания старшинства):

символьные - char;

целые - int;

вещественные - float;

вещественные двойной точности - double.

Тип данного определяет:

сколько места в памяти ПК занимает это данное и в каком виде там хранится;

диапазон возможных значений;

операции, в которых это данное может использоваться.

Типы данных могут дополняться квалификаторами short и long (для типа int), а также unsigned и signed (для типов char и int). В частности, для работы с кириллицей в данных символьного типа необходимо использовать тип unsigned char.

Константа в Си - это область памяти, в которой хранится неизменяемое значение. Тип этого значения определяется написанием константы.

Тип
константы

Пример

Область
значений

Целая

-2, 48

-32768 - 32767

Вещественная

2.5, 2.5е3, 5.

От ~10-37 до ~1038

Символьная

'A', 's'

от -128 до +127

Строковая

"Привет"

Символ "е" в записи вещественной константы (экспоненциальный вид) означает: взять число слева от буквы "е" и умножить на 10 в степени, указанной справа от "е" (2.5е2 означает число 250).

Символьная константа - это число, представленное в виде буквы в апострофах. Значением константы является десятичный код буквы, например, значением константы 'A' является число 65.

Для написания символов, не имеющих графического представления, используются так называемые Esc-последовательности - комбинации из двух символов, первый из которых "\":

Символ

Назначение

'\n'

переход на следующую строку (Enter)

'\t'

табуляция (tab)

'\a'

звонок

'\0'

специальный символ с кодом 0 (NULL)

Строковая константа - это последовательность символов, окаймленная кавычками ", например, "Привет". Компилятор добавляет в конец строки символ '\0', чтобы по нему можно было в дальнейшем определить конец строки. Поэтому 'A' и "A" в Си отличаются: первое - это код символа А, а второе - последовательность двух символов: 'A' и '\0'.

Для удобства чтения и изменения текста программы константы можно именовать с помощью директивы препроцессора #define, помещаемой обычно в начало каждого программного модуля, например:

#define MAX_SIZE 120     - определение границ массива

#define ENTER 13         - код клавиши

При обработке программы препроцессор заменит строки с этими директивами пустыми строками (сохраняя нумерацию строк), а в тексте везде заменит имена таких констант на их значения.

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

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

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

Все переменные в программе должны быть описаны. Описание переменной имеет вид (синтаксис)

тип имя переменной[,имя переменной ...] , например

int i,n;

float a,b,c;

В синтаксисе "..." показывает, что содержимое скобок может повторяться несколько раз.

Описание указывает компилятору, сколько места отвести в памяти для этой переменной, а также какие действия с этой переменной можно производить, и ставится либо

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

в начале блока, который начинается с "{" и заканчивается "}". В этом случае область видимости - только внутри блока, а время жизни - до выхода из блока (см. пример).

При описании переменной в нее можно поместить начальное значение (т.е. инициализировать):

int n=25;

char key='+',ch=ENTER;  // именованная константа ENTER, равная 13.

  1.  Выражения в языке Си.

Главной частью языка Си являются операторы, они управляют процессом выполнения программы.

Операторы состоят из ключевых слов и выражений. Выражения, в свою очередь, состоят из операндов, знаков операций и скобок, задающих порядок вычисления. Операнд выражения - это константа, переменная или обращение к функции. Примеры выражений: a+b/2 ,
c-sin(a+1) > 3.0 .

Выражения можно условно разделить на арифметические и логические.

  1.  Арифметические операции и выражения.

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

  1.  Действия в скобках (), начиная от самых внутренних;
  2.  Вычисление функций;
  3.  Унарный минус (-), это значит, что он применяется к одному операнду, например, -3;
  4.  бинарные *, / и % (деление по модулю, т.е. остаток от целочисленного деления, - только для целых аргументов) Выполняются слева направо в порядке следования. Например, 12%5 дает 2, а 4%2 дает 0;
  5.  бинарные “-“ и “+”. Выполняются слева направо в порядке следования.

Правила записи арифметических выражений:

  1.  Все знаки операций проставляются. Перенос на следующую строку производится без дополнительных знаков операций;
  2.  Если оба операнда в бинарных операциях одного типа, то результат будет того же типа. Деление целых даёт целое, которое получается отбрасыванием дробной части, например, 5/2 дает 2.
  3.  Если операнды разных типов, то перед выполнением действия "младший" тип приводится к более "старшему". Например, 5./2 дает 2.5, поскольку целое 2 преобразуется сначала в вещественное 2.0 .

Пример:

(1.+n)/(1.-1./(2*(1.+n)))

  1.  Логические операции и выражения.

Логические выражения обычно имеют вид

арифм.выражение1 операция отношения арифм.выражение2

Операции отношения могут быть следующими :

Смысл

Обозначение

Больше

>

Больше или равно

>=

Меньше

<

Меньше или равно

<=

Равно

==

Не равно

!=

При выполнении операции отношения сравниваются значения двух выражений. Если результат сравнения - истина, то результатом логического выражения будет число 1, иначе - 0. Например, 5>3 дает 1, а 5<3 дает 0.

Поскольку результатом логического выражения в Си является число (0 или 1), то нет разницы с точки зрения результата между арифметическим и логическим выражениями. Будем их называть просто выражениями. Условием будем называть любое выражение. Если его результат равен 0, то условие ложно, если не равен 0, то истинно.

Более сложные выражения получаются соединением двух простых знаками логических операций:

&&   логическое “и” (AND) - конъюнкция:

Выражение условие1 && условие2 равно 1, если оба условия истинны, если же хоть одно из них ложно, то результат равен 0. Например, 2+3 && 5>3 равно 1, а 2+3 && 5= =3 равно 0.

|| - логическое “или” (OR) - дизъюнкция:

Выражение условие1 || условие2 равно 1, если хоть одно из условий истинно, если же оба ложны, то результат равен 0. Например, 2+3 || 5=3 равно 1, а 0 && 5= =3 равно 0.

! - логическое “не” (NOT) - отрицание;

Выражение !условие равно 1, если условие ложно, и равно 0 в противном случае. Например, !(5= =3) равно 1, а !(5>3) равно 0.

  1.  Некоторые другие операции в языке Си.

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

Операция присваивания.

Операция присваивания имеет вид (синтаксис):

переменная = выражение

Семантика: сначала вычисляется выражение, затем результат заносится в переменную, при этом тип результата преобразуется к типу переменной (конечно, если это возможно, например, тип float нельзя перевести в int, если число > 32767). Результатом операции и его типом являются значение и тип переменной.

Если в выражении несколько операций "=", то они выполняются справа налево. Приоритет этой операции ниже приоритета всех остальных операций, кроме операции "запятая", которая рассмотрена ниже.

Пример 1:

float a=2.5,b; int c; char d;

Результатом выражения

Выражение

Значение b

Значение с

Значение d

Знач-е выр-я

c=a+1

3

3

b=c=a+1

3.0

3

3.0

c=b=a+1

3.5

3

3

d=c='A'+4

69

'E'

'E'

(c=a+1)+2>5

3

0

В последнем примере необходимость скобок связана с тем, что приоритет у операции "+" выше, чем у "=".

Условная операция.

Условная операция имеет 3 операнда (т.е. является тернарной) и выглядит так (синтаксис)

условие ? выражение1 : выражение2

Семантика: если условие истинно, то результатом операции является значение выражения1, если ложно, то значение выражения2.

Пример 2.

Значением выражения x>y ? x : y будет большее из x и y. Значением выражения x<0 ? -x : x является модуль x.

Операция последовательного вычисления (запятая).

Операция последовательного вычисления имеет вид (синтаксис):

Выражение1,выражение2

Семантика: при выполнении этой операции сначала вычисляется выражение1, затем выражение2. Результатом операции является значение выражения2.

Приоритет у этой операции самый низкий из всех. Используется там, где по синтаксису допускается только одно выражение. Например, значением выражения  x>y ? (y=0, x) : (x=0,y) будет большее из x и y, а меньшее обнулится .

Специфические формы операции присваивания.

Для записи операций присваивания вида

переменная = переменная  арифм.операция  выражение,

например, m=m+2*(b-1), в языке Си предусмотрены специальные формы их записи, которые легче пишутся и быстрее выполняются:

переменная арифм.операция = выражение,

Например, вместо вышеуказанного выражения можно записать m+=2*(b-1), вместо m=m/2 можно записать m/=2 и т.п. Приоритет у этих операций такой же, как у обычного присваивания.

При необходимости увеличения (уменьшения) значения переменной на 1 предусмотрена еще одна удобная форма операции присваивания, называемая декрементом (инкрементом) и имеющая вид ++ (--), например, i++ означает i=i+1, а i-- означает i=i-1.

Такой вид записи обычно используется внутри выражений. При этом символы ++ (--) можно ставить как перед именем переменной (++n, префиксная форма операции), так и после него (n--, постфиксная форма). Отличие в этих формах состоит в том, что изменение переменной происходит в первом случае до использования ее значения в выражении, а во втором случае - после.

Пример 3. Пусть k=4. Операции m=k++ занесут сначала число 4 (т.е. значение k) в m , а затем увеличат k на 1. Наоборот, операции m=++k сначала увеличат k на 1, а затем результат (число 5) занесут в m.

Приоритет выполнения операций.

Приведем сводную таблицу приоритетов рассмотренных операций языка Си.

Операции

Выполнение

Слева направо     Справа налево

( )

!   ++   --  унарный  -

*   /   %

+   -

<   <=  >=   >

==    !=

&&    ||

Условная операция (  ?  :  )

=   +=    =   *=   /=   %=

Операция ','

  1.  Стандартные функции.

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

  1.  Правила записи.

Обращение к функции имеет вид (синтаксис):

имя(список аргументов)

При вызове функции надо выполнять следующие правила:

  1.  После имени функции должна идти открывающая скобка, после списка аргументов - закрывающая. Скобки позволяют транслятору определить, где начинается и где заканчивается список аргументов;
  2.  Аргументы перечисляются через “,” ;
  3.  Аргументы могут быть как  константами, так и выражениями, которые в свою очередь могут содержать другие функции. Задаваемые аргументы должны иметь допустимые для данного аргумента типы и значения. Соответствующий контроль не производится.

Пример 4.  y=sin(x)+pow(cos(2.*x),3.0)

  1.  Основные математические функции.

Перечислим основные из математических функций.

Синтаксис

Возвращаемое значение

Пример

  1.  sqrt(x)

, x - вещественный.

sqrt(9.0) =3.0

  1.  fabs(x)

|x| для вещественных x

fabs(-3.0) = 3.0

  1.  abs(x)

|x| для целых x

abs(-3) = 3

  1.  pow(x,y)

xy, x,y - вещественные. При x<0 и y не целом - ошибка

pow(2.,3.) = 8

pow(-2.,.5) - ошибка

  1.  M_PI

Число

M_PI/2 1.57

  1.  sin(x)

синус x, x - вещественный

sin(M_PI) = 0.0

  1.  cos(x)

косинус x, x - вещественный

  1.  tan(x)

тангенс x, x - вещественный

  1.  atan(x)

арктангенс x, x - вещественный

  1.  exp(x)

ex, x - вещественный

exp(1.0) 2.7

  1.  log(x)

Натуральный логарифм x,
x - вещественный

log(1.0) = 0.0

  1.  log10(x)

Десятичный логарифм x,
x - вещественный

log10(10.) = 1.0

  1.  Простейшие операторы языка Си.

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

Рассмотрим простейшие из них.

  1.  Оператор-выражение.

Оператор-выражение имеет вид (синтаксис):

выражение;

Например, x = y + 3.0; my_funct(x); i=i+1;

                 max= x>y ? (y=0, x) : (x=0,y);

  1.  Операторы для ввода и вывода информации.

Важным примером операторов-выражений являются операторы, использующие функции для ввода и вывода информации.

  1.  Оператор бесформатного вывода на экран.

Бесформатный вывод значений выражений на экран имеет вид (синтаксис):

cout << выражение1 [<< выражение2...];

Здесь cout означает так называемый стандартный поток вывода (иначе - вывод на монитор), а лексема “<<” означает “вставить в”, если только слева от неё стоит cout.

Семантика: указанный оператор выводит на экран поочередно значения выражений1, 2 и т.д., перечисленных справа от cout через знаки "<<".

Пример 5. Операторы

a=1;

cout << "\nМы изучаем Си " << a+1 << "-ю неделю.";

выведут на экран сообщение:    Мы изучаем Си 2-ю неделю.

Здесь выводятся значения трех выражений. Значением строковой константы является текст, заключенный в двойные кавычки ". Символы '\n' означают, что в этом месте вывод продолжается с начала следующей строки экрана. Значением выражения a+1 является число 2.

  1.  Оператор бесформатного ввода с клавиатуры.

Бесформатный ввод данных с клавиатуры в переменные имеет вид (синтаксис) :

cin >> переменная1 [<< переменная2...] ;

Здесь cin означает так называемый стандартный поток ввода, иначе ввод с клавиатуры, а лексема  “>>” означает “принять с клавиатуры“, если только слева от неё стоит cin.

Семантика: операторы в программе выполняются последовательно один за другим. Когда программа доходит до оператора ввода, она останавливается и ждет, когда пользователь введет с клавиатуры через пробел столько значений, сколько переменных указано в списке после cin.

Пример 6. Операторы

cout << "\nВведите два числа: ";

cin >> a >> b;

cout << "\nСумма " << a << " и " << b << " равна " << a+b;

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

  1.  Функция форматного вывода на экран.

Для форматного вывода на экран используется функция

printf(строка формата, список вывода) , где

список вывода - перечисленные через ',' выражения, значения которых хотим вывести на экран.

строка формата - символьная строка, указывающая:

  1.  формат отображения этих значений на экране;
  2.  

сопровождающий их текст.

Например,                 

printf("\nЭтап %d длится %f секунд.",i,(t-3.)/2.);

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

Тип данного

Спецификация формата

Целый

%d или %nd (n - число)

Вещественный

%f или %n.mf (n,m - числа)

Символьный

%c

Строковый

%s

Если i=2 и t=60.0, то при выполнении оператора, указанного в примере, на экране будет напечатано с новой строки:

Этап 2 длится 28.500000 секунд.

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

Имеется возможность управлять формой выводимых на экран целых и вещественных чисел. Числа n и m в спецификациях формата задают соответственно общее количество позиций на экране (включая знак '-' и десятичную точку) и количество разрядов, выводимое после десятичной точки. Например, оператор

printf("\nЭтап %3d длится %6.2f секунд.",i,(t-3.)/2.);

выведет значение переменной i в 3-х позициях, а значение выражения (t-3.)/2. в 6 позициях с двумя разрядами после десятичной точки:

Этап 2 длится 28.50 секунд. (символами показаны пробелы, которые выводятся, если цифр в числе меньше, чем позиций, указанных в соответствующей спецификации).

Если заданных позиций не хватает для вывода числа, место для вывода отводится автоматически.

  1.  Функция ввода символа с клавиатуры.

Функция getch() приостанавливает выполнение программы до нажатия пользователем любой клавиши и возвращает введенный символ. В примере на экран выводится нажатая пользователем клавиша:

char sim;

sim=getch();

printf("Нажата клавиша %c.",sim);

  1.  Составной оператор.

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

{                 Например:

{

 оператор1

 a = 1;

 оператор2

 b += sin(a);

 ····················

 c *= b;

}

}

Такой оператор называется составным или блоком. После символа "}" символ ";" указывать не надо. 

  1.  Операторы управления.

Операторы в программе выполняются последовательно один за другим. Операторы управления предназначены для изменения последовательности выполнения операторов. Блок-схемы этих операторов приведены в разделе 1.2.3.

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

  1.  Условные операторы.
    1.  Оператор if-else.

Условный оператор if-else предназначен для изменения последовательности выполнения операторов программы в зависимости от выполнения условия (т.е. для принятия решений) и имеет вид (синтаксис):

    if (условие)

Например:

if (x>y)

      оператор1

 {max=x; y=0;}

   [else

else

      оператор2]

 {max=y; x=0;}

Оператор if выполняется следующим образом (семантика):

  1.  Вычисляется условие.
  2.  Если оно истинно (0), то

выполняется оператор1.

Иначе

Если есть else-часть

выполняется оператор2.

Иначе

переход к следующему после if оператору

В примере в переменную max заносится большее из x и y, а меньшее обнуляется (см.2.4.3.3). Скобки {}, образующие блоки, здесь существенны, поскольку по синтаксису после if и else должен стоять только один оператор.

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

Пример 7. Сравним для иллюстрации два оператора if:

if (a>b)

if (a>b)

 if(c<0)

 { if(c<0)

   x += 3;

     x += 3;

 else

 }

   x += 2;

else

 x += 2;

В первом операторе else относится к внутреннему if, а во втором - к внешнему. Совет: всегда заключайте внутренний if в скобки.

  1.  Оператор else-if. 

Для многоступенчатого принятия решений используют конструкцию else-if (синтаксис):

if (условие1)

 оператор1

else if (условие2)

 оператор2

else if (условие3)

 оператор3 

·························

[else оператор] 

Семантика: условия операторов “if проверяются сверху вниз. Как только встречается истина, выполняется соответствующий оператор и последовательность проверок прекращается. Последняя else-часть срабатывает, если все предыдущие условия ложны. Эта часть может отсутствовать.

Пример 8. Пусть reit - рейтинг студента после сессии. Предлагаемый оператор показывает, кто он такой.

if (reit >= 45)

 cout << "Вы - отличник!\n";

else if (reit > 34)

 cout << "Вы неплохо учитесь!\n";

else if (reit > 25)

 { if (reit > 29)

     cout << "Вам нужно подтянуться!\n";

   else 

     cout << "Такие инженеры нам не нужны!\n";

 }

else 

 cout << "Вас давно ждут в призывном пункте!\n ";

  1.  Оператор-переключатель. Оператор break.

Оператор-переключатель (оператор множественного выбора) предназначен для выбора одного из многих путей выполнения программы. Имеет вид (синтаксис):

switch(выражение)

{

 case константы1:         // метки1

   операторы1

 case константы2:         // метки2

   операторы2

····················

 [default     

   операторы]

}

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

Операторы, стоящие за default, выполняются, если значение выражения не совпало ни с одной из констант. Если default отсутствует, то не выполняется ни один оператор.

Пример 9.

Операторы

Выведут на экран

i=2;

switch(i)

{

 case 1: cout << "Привет 1\n";

 case 2: cout << " Привет 2\n";

 case 5: cout << " Привет 5\n";

 default cout << "Просто привет \n";

}

Привет 2

Привет 5

Просто привет

Каждая ветвь case может быть помечена одной или несколькими константами (или константными выражениями). Все константы должны отличаться друг от друга. В каждой ветви может быть несколько операторов, может их не быть совсем.

Если нужно выйти из переключателя, не выполняя все последующие ветви case, можно использовать оператор break (выход из switch) или return (выход из функции).

Пример 10. В указанном фрагменте программы после нажатия пользователем одной из клавиш ее код занесется в переменную c и в случае нажатия клавиш 'А', 'а', 'Б' или 'б' будет выдано соответствующее сообщение.

c=getch();

switch(c)

{

 case 'A': 'a':  cout << "Нажата буква А\n"; break;

 case 'Б':

 case 'б':        cout << "Нажата буква Б\n"; break;

}

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

  1.  Операторы для организации цикла.

Эти операторы предназначены для повторения какого-либо оператора несколько раз.

  1.  Оператор цикла с предусловием (while).

Оператор цикла с предусловием имеет вид (синтаксис):

while ( условие )

    оператор               // тело цикла

Семантика: выполняется следующим образом:

  1.  Вычисляется условие.
  2.  Если оно истинно (0), то
  3.  Выполняется оператор.
  4.  Переход к пункту 1.

Иначе - выход из цикла (переход к следующему после while оператору.

Пример 11. Напечатаем список цифр от одного до 9 через побел.

i=0;

while (i<10)

 { printf(“%d  “,i);

   i++;       // То же, что и i=i+1

 }

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

  1.  Оператор цикла с постусловием (do).

Оператор цикла с постусловием используется редко и имеет вид (синтаксис):

do

  оператор               // тело цикла

while ( условие );

Семантика: выполняется следующим образом:

  1.  Выполняется оператор.
  2.  Вычисляется условное выражение.
  3.  Если оно истинно (0), то

Переход к пункту 1.

Иначе - выход из цикла (переход к следующему после do оператору.

Пример 12. Напечатаем список чисел от 10 до 1 в столбик.

i=10;

do

 printf(“%d\n“,i--);

while (i>0);

В отличие от while тело цикла выполнится хотя бы один раз.

  1.  Оператор цикла с параметром (for) или пошаговый.

Оператор for имеет вид (синтаксис):

for(

выражение1;

условие ;

выражение2 )

оператор

Смысл

for(

начальное значение счетчика

условие продолжения цикла

изменение счетчика

повторяемый оператор (тело цикла)

Любое из выражений в скобках может отсутствовать, однако их разделители “;” обязательны.

Семантика: оператор for выполняется следующим образом:

  1.  Вычисляется выражение1 (начальное значение счетчика), если оно есть;
  2.  Вычисляется условие (условие продолжения цикла);
  3.  Если оно истинно (0) или его вообще нет, то
  4.  Выполняется оператор;
  5.  Вычисляется выражение2 (новое значение счетчика);
  6.  Переход к пункту 2.

Иначе - выход из цикла (переход к следующему после for оператору.

Пример 13. Вычислим сумму и произведение чисел от 1 до 8.

p=1; s=0;

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

 { s=s+i;

   p=p*i;

 }

То же можно написать короче в ущерб читабельности, воспользовавшись операциями “,” , присваивания и постдекремента:

for(p=1, s=0, i=1; i<=8; s+=i, p*=i++);

В этом примере тело цикла отсутствует, поскольку при вычислении выражения2 изменяются и s, и p, а затем и i.

Пример 14. Напишем оператор задержки продолжения программы до нажатия клавиши “g”.

for(ch=‘A’; ch != ‘g’; ) ch=getch();

В данном примере отсутствует выражение2, поскольку значение переменной ch изменяется в теле цикла.

  1.  Вложенные циклы.

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

Пример 15. Напишем программу печати таблицы умножения.

for(i=1; i<10; i++)  // Для каждой строки i таблицы

 { for(j=1; j<10; j++) // Для каждого столбца j таблицы

                                                         // выводим j*i=произведение j на i

     printf(“%d*%d=%2d  “,j,i,j*i);

                       // В конце строки (после цикла по j)

   cout << '\n';       //   переход на новую строку

 }

Попытайтесь исправить эту программу так, чтобы обойтись тремя операторами (без cout ). Подсказка: использовать условную операцию (  ?  :  ).

  1.  Операторы break и continue.

Иногда требуется прекратить выполнение цикла не по результату проверки условия, а другим способом. Это можно сделать оператором break, который  вызывает немедленный выход из самого внутреннего из объемлющих его циклов for, while или do, а также оператора switch.

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

summ=0;

while (1)

 { cin >> a;

   if (a >= 0)

     summ += a;

   else

     break;

 }

Оператор continue тоже прерывает выполнение операторов тела цикла, однако не заканчивает цикл, а вызывает следующий шаг итерации. Для for это изменение счетчика, а для while или do - проверка условия.

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

summ=0;

while (1)

 { cin >> a;

   if (a > 0)        // положительное

     summ += a;

   else if (a < 0)   // отрицательное

     continue;

   else              // введено число 0

     break;

 }

  1.  Оператор безусловного перехода.

Оператор безусловного перехода имеет вид

goto метка;

Он передает управление на оператор, помеченный указанной меткой. Метка - это любой идентификатор, после которого стоит символ ':'. На метку можно перейти из любого места функции, где метка указана. Помечено может быть любое место внутри блока, тела цикла, операторов if (кроме слова else) и switch.

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

Пример 18. Напишем фрагмент подобной конструкции.

for(···)     // Внешний цикл

 { for(···)  // Внутренний цикл

     { ·········// тело внутреннего цикла

       if (···)  // если нужно выйти из всех циклов

         goto label;  // выход на верхний уровень

     }

 }

·········

label: ·········     // обработка досрочного выхода

  1.  Использование массивов. Указатели.
    1.  Массивы.
      1.  Понятие массива. Описание массива.

Массив в языке Си - это непрерывный набор однотипных переменных, имеющих одинаковое имя. Элемент массива обозначается его номером (индексом), указанным в [ ] после имени. Нумерация индексов начинается с 0.

Например, a[0] обозначает 0-й элемент массива a, mas_line[5] - пятый элемент массива mas_line, а d[i] - элемент массива d с номером, который равен значению переменной i. При i=3 это третий элемент, а при i=1 -первый. Преимущество использования массивов как раз и заключается в том, что к разным переменным можно обращаться одной и той же записью типа d[i], меняя только значение переменной, обозначающей индекс.

До своего использования массив должен быть описан. Описание имеет вид (синтаксис):

тип имя[константа];

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

int a[10]; char my_string[80];

Семантика: описание нужно транслятору, чтобы отвести в памяти непрерывный участок для всех элементов массива. Длина этого участка вычисляется произведением размера типа данных на количество элементов массива. Например, размер памяти, отведенный под массив a, равен 2 байта * 10 элементов - всего 20 байт, а под массив my_string - 1*80=80 байт.

При обращении к элементу массива в процессе выполнения программы выход значения индекса за его максимальное значение, указанное при описании массива, не проверяется. Поэтому необходимо следить за этим самостоятельно, чтобы избежать трудно выявляемых ошибок. Индекс может изменяться от 0 до значения (константа - 1).

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

 

 

 

 

- 0-й элемент исходного массива

 

 

 

 

- 1-й элемент исходного массива

 

 

 

 

- 2-й элемент исходного массива

Поэтому при описании многомерных массивов количество элементов по каждому новому измерению дописывается справа от предыдущего измерения в [ ]:

тип имя[константа1] [константа2]...[константаN];

Например, если вышеуказанная матрица из 3-х строк и 4-х столбцов имеет вещественные элементы, то ее описание может выглядеть так:

float matr[3][4];

На первом месте всегда указывается количество строк, на втором - столбцов. В одномерной памяти ПК транслятор располагает двумерный массив построчно - сначала элементы первой строки, затем второй и т.д.:

4 байта

 

 

 

 

 

 

 

 

 

 

 

 

Строка 1

Строка 2

Строка 3

Каждый элемент такого массива определяется его двумя индексами, например, matr[0][1] означает второй элемент первой строки матрицы (напомним, что нумерация начинается с 0, поэтому 0 - первая строка, а 1 - второй столбец).

Количество индексов элемента массива определяет его размерность, максимальные значения индексов - его размер. Например, указанный массив имеет размерность 2, а размер - (34).

  1.  Инициализация массивов.

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

  1.  Инициализация массивов при их объявлении.

Инициализация одномерного массива при объявлении выглядит так:

тип имя[константа] = {список инициализаторов};

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

int a[5]={7,-3,8,-2,4};

Если количество инициализаторов меньше размера массива, оставшиеся элементы обнуляются. Например, объявление

float b[3]={2.};  заполнит массив b числами 2.,0.,0.

Если есть список инициализаторов, то количество элементов массива при объявлении можно не указывать, транслятор сам отведет ему место в соответствии со списком инициализаторов. Например, объявление int c[]={2,0,5}; создаст массив c из трех чисел.

При инициализации двумерного массива значения его элементов нужно указывать в порядке их расположения в памяти, т.е. по строкам. Например, объявление int d[3][2]={1,3,5,7}; создаст матрицу

1

3

,

которая в памяти выглядит так:

5

7

1

3

5

7

0

0

0

0

Если нужно изменить порядок заполнения (т.е. указывать не все элементы строк), то каждую строку нужно указывать в { }.Например, объявление int d[3][2]={{1},{3,5},{7}}; создаст матрицу

1

0

,

которая в памяти выглядит так:

3

5

1

0

3

5

7

0

7

0

Размер для первого индекса (т.е. количество строк) можно не указывать, транслятор сам определит его в соответствии со списком инициализаторов. Например, объявление int d[][2]={1,3,5,7}; создаст массив (22).

  1.  Инициализация массивов вводом с клавиатуры.

Заполнение массивов значениями с клавиатуры производится поэлементно.

  1.  Ввод одномерного массива из n элементов через пробел:

cout << "\nВведите " << n << " чисел через пробел\n";

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

 cin >> a[i];

  1.  Ввод двумерного массива размером (nm) построчно:

printf("\nВведите матрицу (%d%d) построчно\n",n,m);

for (i=0; i<n; i++)       // Для каждой строки

 for (j=0; j<m; j++)   // ввод m чисел через пробел

   cin >> d[i][j];

  1.  Вывод массивов.

Вывод массивов на экран также производится поэлементно.

  1.  Вывод одномерного массива из n элементов через пробел:

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

 cout >> a[i] >> " ";

  1.  Вывод двумерного массива размером (nm) построчно:

for (i=0; i<n; i++)       // Для каждой строки

 { for (j=0; j<m; j++)   // вывод m чисел через 2 пробела

     printf("%5.1f  ",a[i][j]); // число в 5 позициях

   cout << '\n';          // переход на след.строку экрана

 }

  1.  Работа с массивами.

Приведем в качестве примера работы с массивами фрагменты программы нахождения суммы и максимального элемента массива a из n чисел (блок-схемы соответствующих алгоритмов были приведены в 1.2.4.  

#define MAX_SIZE 100    //Определение именованной константы

float a[MAX_SIZE];

float s,max;

int i,n;

············    // Ввод n и массива a из n чисел, n должно быть ············    // не больше MAX_SIZE

s=0;            // Сумма элементов массива a

max=a[1];       // Максимальный элемент массива 

for (i=1;i<n;i++) // Для каждого элемента 

 { s+=a[i];        // Увеличение суммы на a[i]

   if (a[i]>max)   // Если элемент больше максимального, то

     max=a[i];     // он становится максимальным

 }

············    // Вывод n,  массива a, s и max

  1.  Указатели.
    1.  Адреса и указатели.

Напомним, что память ЭВМ представляет собой последовательность пронумерованных ячеек, каждая из которых имеет свой адрес. Там хранятся как команды программы, так и данные, ею используемые. Данные типа char в языке Си занимают один байт, данные целого типа - обычно два байта, а вещественного - 4 байта.

Если рассмотреть операцию a=5, то для компилятора это означает: занести число 5 по адресу переменной с именем a, т.е. в ячейку с адресом 2008 (см.таблицу).

Имя

ptr

a

d

Адрес

2000

2004

2008

2012

Значение

2012

5

32

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

тип_объекта  *имя_указателя ;

Это означает, что в этом указателе будет храниться адрес какого-либо объекта (например, переменной) указанного типа. Например,

int *ptr, d;

означает, что в указателе ptr будет храниться адрес переменной целого типа, а в переменной d - значение целого типа.

Занести в указатель адрес можно, используя специальную операцию адресации:  &имя_переменной , например, ptr=&d означает: занести в указатель ptr адрес переменной d. Говорят, что ptr указывает (или ссылается) на d.

Чтобы изменить значение переменной, используя ее указатель (т.е. значение по указанному адресу), нужно вместо имени этой переменной в левой части оператора присваивания использовать имя указателя на эту переменную со знаком * перед ним. Например, операторы *ptr=32; и d=32; равнозначны, если ptr ссылается на d.

Операцию * называют операцией раскрытия ссылки, она означает переменную, на которую ссылается указатель. Выражение *указатель может использоваться вместо имени соответствующей переменной в любых выражениях Си (в нашем случае *ptr вместо d). Унарные операции & и * имеют тот же приоритет, что и унарный минус (см. таблицу в 2.4.3.5).

  1.  Массивы и указатели.

Пусть описаны целые массив a, переменная s и указатель на целое p.

int *p,a[5]={7,-3,8,-2,4},s;

В языке Си имя массива (например, a) означает на самом деле адрес его нулевого элемента, т.е. адрес ячейки a[0]. Поэтому операторы

p=&a[0]; и p=a;

эквивалентны и предписывают занести в p адрес нулевого элемента массива a (т.е. 2004). Оператор s=*p; копирует в s содержимое a[0].

Имя

p

a[0]

a[1]

a[2]

a[3]

a[4]

s

Адрес

2000

2004

2006

2008

2010

2012

2014

Значение

2004

7

-3

8

-2

4

7

Если p указывает на нулевой элемент массива a, то по определению p+1 указывает на следующий элемент, т.е. на a[1], а значением выражения *(p+1) является значение этого элемента, т.е. -3. Аналогично p+i (также, как и a+i) указывает на i -й элемент массива a, следовательно значения выражений a[i] , p[i], *(a+i) и *(p+i) равны.

Отличием в использовании имени массива и указателя на массив является то, что указатель - это переменная и в нее можно занести в дальнейшем какой-либо другой адрес, например, записать p++ (тогда в p будет находиться адрес 2006. С именем массива такое сделать нельзя, т.к. адрес массива определен компилятором раз и навсегда.

  1.  Строки и указатели.

Наибольшее распространение указатели получили в Си при работе со строками. Строковые константы, т.е. объекты вида "текст" (см.2.3), представляются в Си в виде массива символов, который заканчивается символом '\0'. Количество элементов этого массива на единицу больше количества символов между двойными кавычками. Например, "Си" представляется массивом из 3 символов 'С', 'и' и '\0'.

Место в памяти для строковой (как и для других типов) константы отводится при трансляции. Доступ же к ней осуществляется через указатель на нулевой элемент соответствующего массива. У этого массива нет имени. Операторы

char *p_error;

p_error="Ошибка!";

заносят в указатель p_error адрес нулевого элемента символьного массива (символа 'О'). Это же можно сделать и так:

char *p_error=" Ошибка!";  

Описание же следующего вида

char m_error[]="Ошибка!";  создает символьный массив m_error из 8 элементов. Отдельные его символы можно будет изменить, но m_error всегда ссылается на одно и то же место памяти Значение же p_error в будущем можно будет изменить, записав туда адрес другой константы, например, p_error="Ошибочные данные!";.

Для ввода строки с клавиатуры предусмотрена функция gets(имя_массива), которая заносит введенные с клавиатуры символы в указанный символьный массив. Размер массива должен быть таким, чтобы вместить все введенные символы. Если же в качестве аргумента функции указан указатель на char, то он также должен указывать на область памяти, достаточную, чтобы вместить все введенные символы.

При вводе строки после нажатия пользователем клавиши Enter символы заносятся в массив, за последним введенным символом автоматически добавляется символ ‘\0’. Затем выполнение программы продолжается.

Чаще всего строки используются в качестве аргументов функций, как, например, в функции fprintf() или в потоках вывода cout.

Рассмотрим некоторые приемы работы со строками в Си. Основным является то, что длина строки заранее неизвестна. Признаком ее окончания является символ '\0'.

Пример 19. Напишем программу, которая позволяет ввести с клавиатуры пароль и сравнить его с образцом.

int i,equal=0;        // Признак совпадения строк пока равен 0

unsigned char mas[81], *parol="Мой пароль";

gets(mas);              // Ввели пароль (<=80 символов)

for (i=0; mas[i] == *(parol+i);i++)

 if (mas[i] == '\0' )  // Строки совпали полностью

   {equal=1;break;}    // Признак совпадения = 1

if (!equal)

 printf("Пароль %s не годится\n",mas);

else

 cout << "Пароль" << mas << "годится\n";

На вводимое значение пароля мы отвели 80 символов, указатель parol ссылается на массив с его верным значением. После ввода пароля он посимвольно сравнивается с правильным значением до нахождения отличающихся символов. Если же дошли до конца строки (символа ‘\0’), не встретив отличий, то выходим из цикла, установив признак совпадения строк в 1.

Пример 20. Напишем программу, которая позволяет определить количество букв и слов в введенном предложении.

Ограничим длину предложения 300 символами, признаком окончания слова будем считать пробел, а признаком буквы – значение кода символа в пределах от 65 (латинская А) до 239 (русская я). Поскольку будем использовать коды кириллицы (т.е. > 127), то необходимо описывать символы как unsigned char.

int i=0;                   // Номер текущего символа

int k_sl=1,k_sim=0;        // Количество слов и символов

unsigned char mas[300],ch; // Предложение и текущий символ

gets(mas);

while ( (ch = *(mas+i++)) != '\0' )

 if (ch==' ')               // Текущий символ - пробел -

   k_sl++;                  // увеличили кол-во слов

 else if (ch>64 && ch<240)  // Текущий символ - буква -

   k_sim++;                 // увеличили кол-во букв

printf("Слов - %d, ,букв - %d",k_sl,k_sim);

Просматриваем по порядку все введенные символы, записывая их в ch, пока не дойдем до конца строки '\0'. Выражение (ch = *(mas+i++)) != '\0' означает: взять то, что находится по адресу mas+i, и занести в ch. Затем сравнить это значение с '\0' и результат сравнения считать значением всего выражения. Затем увеличить i на 1.

Внешние скобки здесь обязательны, иначе в ch занесется результат сравнения *(mas+i) и '\0', поскольку логическая операция != обладает более высоким приоритетом, чем операция присваивания =.

Отметим, что в операторе while вместо mas+i++ использовать сдвиг значения mas путем mas++ нельзя, т.к. mas - имя массива, а не указатель. Можно сделать указатель на mas :

insigned *p;

p=mas; // и тогда правомерна конструкция

while ( (ch = *(p++)) != '\0' )

  1.  Функции пользователя в языке Си.
    1.  Назначение функций. Описание функции.

Функции в Си используются для:

структурирования программы (т.е. для подразделения задачи на более мелкие);

многократного выполнения одних и тех же действий с различными исходными данными.

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

Чтобы к функции можно было обратиться, она должна быть описана в программе.

Описание указывает:

какие действия необходимо выполнить при передаче управления функции (тело функции);

переменные, в которые будут помещены исходные данные для выполнения  этих действий (формальные параметры).

Описание функции имеет вид:

Тип имя([список формальных параметров])    -заголовок

Тело

функции

{

   описания локальных переменных

   исполняемые операторы

}

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

тип имя_переменной

Пример 21.

float max(float x,float y)

{ float z;                // описание локальной переменной

 if (x>y        

Исполняемые

операторы

   z=x;         

 else           

   z=y;         

 return z;

}

или

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

return [выражение]

Оператор return досрочно передает управление на точку вызова и, если есть выражение, то возвращает туда значение этого выражения, которое должно быть указанного в заголовке типа.

  1.  Обращение к функции. Прототип функции. Область видимости и время жизни переменных.

Обращение к функции (вызов) имеет вид:

имя(список фактических аргументов)

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

a=max(4.8, b+18.);

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

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

В нашем примере при вызове функции max значения фактических аргументов (число 4.8 и значение b+18.) заносятся в соответствующие им по порядку формальные параметры, для которых отводится место в памяти (соответственно в x и y). Затем образуется локальная переменная z, в которую далее заносится большее из x и y. По достижении оператора return значение z передается на точку вызова функции и заносится в переменную a. Место в памяти, выделенное под x, y и z, освобождается.

Эту же функцию можно описать короче без использования локальной переменной z с помощью условной операции:

float max(float x,float y)

{return (x>y) ? x : y;}

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

a=max( max(b, c), max(d, e) );

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

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

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

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

Если имя локальной переменной совпадает с именем глобальной (внешней), то до выхода из блока действительна локальная, после выхода - восстанавливается действие внешней переменной.

  1.  Правила соответствия формальных параметров и фактических аргументов. 

Фактические аргументы и формальные параметры должны соответствовать друг другу по

порядку следования;

типу;

количеству.

Примечание: соответствия по имени не требуется!

  1.  Передача данных по значению и по адресу. Функция scanf().

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

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

Пример 22. Напишем функцию обмена значениями двух переменных.

void change_var1(float *var1, float *var2)

{ float c;

 c = *var1;

 *var1 = *var2;

 *var2 = c;

}

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

f = 2.8; d = 12.0;

change_var1(&d, &f);

Основная программа

Функция change_var1

Имя

d

f

var1

var2

c

Адрес

1000

1004

2000

2004

2012

Значение

2.8

12.0

1000

1004

2.8

При вызове функции change_var1 адреса фактических аргументов (переменных d и f) заносятся в указатели var1 и var2, для которых отводится место в памяти. Затем образуется локальная переменная c, которая используется в качестве буферной переменной, чтобы поменять местами значения по адресам, находящимся в var1 и var2.

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

scanf(строка формата, список ввода) , где

список ввода - перечисленные через ',' адреса переменных, значения которых хотим ввести с клавиатуры через пробел или <Enter>.

строка формата - символьная строка, указывающая форматы, в соответствии с которыми будут интерпретироваться вводимые с клавиатуры данные;

Например, для чтения с клавиатуры даты вида 12 марта 1999 в целые переменные day, year и строку month_name обращение к функции scanf будет выглядеть так:

scanf("%d%s%d",&day, month_name, &year); 

Знак & перед month_name не требуется, поскольку имя массива является адресом.

В Си++ разработан еще один способ передачи данных по ссылке. Если в заголовке описания функции перед формальным параметром указать операцию адресации &, то адрес фактического аргумента при обращении к функции станет адресом соответствующего формального параметра (другими словами, внутри функции будет использоваться та же переменная, но с другим именем - именем формального параметра).

Пример 23. Рассмотрим, как будет выглядеть функция обмена значениями двух переменных (см. Пример 22) в этом случае.

void change_var2(float &var1, float &var2)

{ float c;

 c = var1;

 var1 = var2;

 var2 = c;

}

Обращение к такой функции может выглядеть так:

f = 2.8; d = 12.0;

change_var2(d, f);

Основная программа

Функция change_var

Имя

d

f

var1

var2

c

Адрес

1000

1004

1000

1004

2012

Значение

2.8

12.0

2.8

12.0

2.8

При вызове функции change_var2 адреса фактических аргументов (переменных d и f) становятся адресами переменных var1 и var2, для которых место в памяти не отводится. Таким образом, var1 и var2 являются просто другими именами переменных d и f из вызывающей функции. Затем образуется локальная переменная c, которая используется в качестве буферной переменной, чтобы поменять местами значения var1 и var2, т.е. d и f.

  1.  Использование массивов в качестве аргументов функции.

Поскольку имя массива означает адрес его 0-го элемента, то массив всегда передается по ссылке. Оформим в виде функции вычисление суммы элементов массива, описанное в разделе 4.1.4.

float summ(int n, float *mas);

{ int i;

 float s=0;

 for (i=1;i<n;i++) // Для каждого элемента 

   s+=mas[i];        // Увеличение суммы на mas[i]

 return s;

}

Сравним теперь средние арифметические двух массивов, введенных с клавиатуры, используя функцию summ. Здесь vvod_mas1() - функция для ввода одномерного массива, которую нужно написать самостоятельно.

#define MAX_SIZE 100

void main(void)

{

 float a[MAX_SIZE],b[MAX_SIZE]; // описание массивов

 float sa, sb;                  // их средние значения

 int na, nb; // количества введенных элементов этих массивов

 vvod_mas1(&na,a) // Ввод количества элементов na и массива a 

 vvod_mas1(&nb,b) // Ввод количества элементов nb и массива b 

 sa=summ(na,a)/na; // среднее арифметическое массива a

 sb=summ(nb,b)/nb; // среднее арифметическое массива b

·········           // обработка sa и sb

}

Если в функцию надо передать двумерный массив, то в качестве соответствующего формального параметра надо использовать указатель на одномерный массив с размером, равным длине строки передаваемого массива, например, int (*mas)[4]. Вместо такого указателя можно использовать и просто двумерный массив, указав только количество его столбцов - int mas[ ][4].

Такое описание необходимо транслятору, чтобы знать, в каком месте одномерной памяти начинается новая строка двумерного массива, и уметь находить элемент с заданными индексами:

2 байта

 

 

 

 

 

 

 

 

 

 

 

 

Строка 1

Строка 2

Строка 3

Пример 24. Напишем, как будет выглядеть заголовок описания функции vvod_mas2() ввода с клавиатуры двумерного массива, количество столбцов для которого было отведено в памяти в соответствии с именованной константой K_STOLB. Функция должна также заполнять значения количеств строк и столбцов вводимого массива по адресам, находящимся в указателях n_str и n_stlb, вводя их с клавиатуры:

void vvod_mas2(int *n_str, int *n_stlb, int mas[][K_STOLB])

или (см. Пример 23)

void vvod_mas2(int &n_str, int &n_stlb, int (*mas)[K_STOLB])

Пример 25. Оформим в виде функции password() ввод с клавиатуры пароля и сравнение его с образцом (см. Пример 19). В функцию передается образец пароля, а возвращается истина, если введенный пароль верен.

int password(char *parol)

{ int i

 unsigned char mas[81];

 gets(mas);              // Ввели пароль

 for (i=0; mas[i] == *(parol+i);i++) ;

   if (mas[i] == '\0' )  // Строки совпали полностью

     return 1;       

 return 0;

}

Обращение к функции может выглядеть так:

if password("Мой пароль")

 cout << "Пароль верный, доступ открыт";

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

void wrd_let(int *k_sl, int *k_sim, char *mas)

{ int i=0;                 // Номер текущего символа

 unsigned char ch;        // Текущий символ

 

 *k_sl=1,*k_sim=0;        // Количество слов и символов

 gets(mas);

 while ( (ch = *mas++) != '\0' )

   if (ch==' ')              // Текущий символ - пробел -

     *k_sl++;                // увеличили кол-во слов

   else if (ch>64 && ch<240) // Текущий символ - буква -

     *k_sim++;               // увеличили кол-во букв

}

После такого обращения к функции:

wrd_let(&slova, &bukvy, "Мы изучаем Си!");

переменные slova и bukvy примут значения соответственно 3 и 11.

  1.  Работа с внешними файлами в Си.
    1.  Файловый указатель. Открытие файла.

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

Для работы с файлом надо описать указатель на файл. Описание имеет вид:

FILE *имя_переменной

Здесь FILE - имя специальной структуры для работы с файлами. Она описана в файле stdio.h . Например:

FILE *fin, *fout;

Чтобы файл был доступен для чтения или записи, его нужно открыть. Файловый указатель связывается с конкретным файлом в момент его открытия функцией fopen():

указатель = fopen(строка с именем файла, режим открытия)

Режим открытия указывает, с какой целью открыт файл:

Режим

Цель открытия (режим)

"r"

чтение из файла

"w"

создание нового файла и запись туда

"a"

добавление в файл

"r+"

чтение и запись

"w+"

создание нового файла для чтения и записи

Например,

fin = fopen("test_in.txt","r");

fout = fopen("test_out.txt","w");

Первый файл test_in.txt открыт для чтения из него информации. Второй - test_out.txt - будет создан заново, даже если он уже существует, и открыт для записи.

При любой ошибке открытия fopen() возвращает значение NULL, описанное в stdio.h .

  1.  Чтение данных из текстового файла.

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

Ввод строки из файла производится функцией

fgets(имя строки, максимальное количество символов, указатель файла)

Эта функция записывает в строку из файла, связанного с указателем, следующие по порядку символы до встречи символа '\n' или до истечения максимального количества символов-1. fgets оставляет в конце строки символ '\n' и добавляет туда символ '\0'.

Форматный ввод из файла производится функцией

fscanf(указатель файла, строка формата, список ввода),

которая идентична функции scanf() (см.5.4) с той лишь разницей, что первым ее параметром является указатель файла.

  1.  Запись данных в текстовый файл. Закрытие файла.

Форматный вывод в файл производится функцией

fprintf(указатель файла, строка формата, список вывода) 

Эта функция также идентична функции printf() форматного вывода на экран (см. 2.5.2.3).

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

fclose(указатель файла)

Пример 27. Рассмотрим в качестве примера ввод двумерного массива из файла ish_dann.txt. Данные в нем расположены, например, так:

2                              (число строк)

3                              (число столбцов)

120  113.5  -18.3     (массив)

-113 140 112

if(( fin=fopen("ish_dann.txt","r"))==NULL)

 {cout << "файл ish_dann.txt не открылся"; getch();}

else

{ fscanf(fin,"%d %d",&n,&m) ;

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

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

     fscanf(fin,"%f",&a[i][j]) ;

 fclose(fin);

}


 

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

31062. Цитомегаловирусный сиалоаденит 14.33 KB
  При локализованной форме чаще поражаются околоушные железы где вирус фиксируется в ацинарных и протоковых клетках и может существовать в виде латентной инфекции. Макроскопически слюнные железы увеличены. В исходе локализованной формы цитомегаловирусного сиалоаденита как правило в той или иной степени развивается склероз слюнной железы.
31063. Атоиммунные поражения при ревматических болезнях, активном хроническом вирусном гепатите и струме Хашимото, синдроме Шегрена и синдроме Микулича (сухой синдром) 15.9 KB
  Особую группу заболеваний слюнных желез составляют аутоиммунные поражения при ревматических болезнях активном хроническом вирусном гепатите и струме Хашимото синдроме Шегрена и синдроме Микулича сухой синдром. Для сухого синдрома Шегрена характерно поражение всех слюнных желез что сопровождается ксеростомией сухость слизистой оболочки рта слезных желез с развитием ксерофтальмии и суставов с формированием полиартрита. Для синдрома Микулича характерно поражение больших и малых слюнных желез и слезных желез которые увеличиваются и...
31064. Кисты слюнных желез 14.87 KB
  Кисты слюнных желез. Кисты чаще развиваются в малых слюнных железах. Различают ретенционные и слизистые кисты. Микроскопически стенка кисты представлена соединительной тканью выстлана уплощенным эпителием.
31065. Реактивные опухолеподобные поражения слюнных желез 14.91 KB
  К реактивным опухолеподобным поражениям слюнных желез относят: доброкачественное лимфоэпителиальное поражение сиалоз онкоцитоз некротизирующую сиалометаплазию. Эти поражения проявляются увеличением в размерах слюнных желез чаще околоушных и имеют сходную клиническую картину с опухолями. Функциональная роль онкоцитов в слюнных железах а также в некоторых других органах щитовидная и околощитовидная железы окончательно не определена.
31066. АДЕНОМЫ 21.28 KB
  Значительное присутствие разбросанных миоэпителиальных клеток анастомозирующих между собой можно отметить только среди миксоидноro компонента опухоли не имеющего никакого отношения к строме. В этих двух разновидностях плеоморфной аденомы можно выделить три типа эпителиальных клеток. Первая группа клеток мелкими клетками с гиперхромными ядрами. Второй тип клеток светлыми клетками овальной или округлой формы они образуют мелкие и крупные комплексы или альвеолярные структуры.
31067. КАРЦИНОМЫ (раки) 28.86 KB
  Эпителиально-миоэпителиальная карцинома ЗЛОКАЧЕСТВЕННЫЕ ЭПИТЕЛИАЛЬНЫЕ ОПУХОЛИ СЛЮННЫХ ЖЕЛЕЗ КАРЦИНОМЫ ИЛИ РАКИ Мукоэпидермоидный рак. Чаще всего в ткани опухоли хорошо заметны множественные кисты. И при этом малоподвижные опухоли плотной консистенции обычно оказываются низкодифференцированными при микроскопическом исследовании. Встречаются также многоузелковые опухоли узлы обычно спаяны между собой и имеют плотную консистенцию.
31068. Кисты кожи головы, лица и шеи 29.5 KB
  Кисты лица и шеи: 1 кератиновые кисты к которым относят волосяную кисту и эпидермальную; 2 дермоидная киста; 3 врожденные кисты и свищи лица и шеи. Кератиновые кисты: а полость волосяной кисты содержит аморфную массу белосероватого цвета внутренняя выстилка представлена рядами чешуйчатоподобных клеток б полость эпидермальной кисты заполнена слоями кератина внутренняя выстилка представлена многослойным плоским эпителием. Наиболее частая локализация кератиновых кист кожа лица шеи волосистой части головы; кисты появляются в период...
31069. Варианты лимфаденитв 19.22 KB
  Лимфогранулематоз Хлджкина– злокачественная опухоль лимфоидной ткани в которой малочисленные опухолевые клетки характерного строения располагаются среди преобладающего реактивного клеточного окружения. Опухолевые клетки при нодулярном типе лимфоидного преобладания экспрессируют панВклеточные антигены в то время как клетки классического лимфогранулематоза утрачивают экспрессию Вклеточных антигенов. Клетки БерезовскогоШтернбергаРид типичного строения – крупные 2030 мкм с дву или многодольчатым ядром или дву или многоядерного...
31070. Одонтогенный сепсис 30.01 KB
  Изначально причиной одонтогенного сепсиса чаще всего являются осложнения кариеса: апикальный периодонтит периостит остеомиелит челюстей и флегмоны мягких тканей орофациальной области. Для реализации сепсиса необходима неадекватная гиперергическая реакция макроорганизма на возбудителя и несостоятельность его антибактериальной защиты. При сепсисе утрачена способность макроорганизма локализовать инфекцию.