Все записи автора KaDeaT

Занятие 13. Азы работы с файлами и потоками. Случайные числа.


Добрый день друзья.
Не знаю как вас, а меня жутко раздражает одна штука. Вот вам еще не надоело, каждый раз, когда проверяете свою программу вводить заново исходные данные с клавиатуры? Меня этот факт всегда печалил. Хочется быстрее посмотреть, как работает программа, а тут тебе бац, такая засада – снова данные надо вводить. Я думаю пора с этим покончить, тем более для этого у нас уже достаточно знаний и средств.  Так что, сегодняшний мини-урок будет посвящен именно решению этой проблемы.
Не далее, как полтора года назад, я рассказывал вам о стандартных потоках ввода и вывода. Для тех, кто подзабыл, рекомендую повторить материалы занятия про ввод и вывод данных. Теперь еще потренируем свою память. Что вы помните об авгиевых конюшнях?  Ну вспоминайте-вспоминайте. Если никак я напомню.  Был такой персонаж – Геракл. Большой силач, как утверждают некоторые. Так вот, встала перед ним однажды задачка, почистить конюшни, которые 30 лет никто не чистил. История умалчивает, но возможно Геракл, был первым, кто реально мог на себе почувствовать, что значит быть «по уши в дерьме». Говорят здоровяки обычно тупые. Это было явно не про нашего героя. Хитрый Геракл, перегородил   рядом протекающую речку,  направив поток воды в эти самые конюшни. Водичка за день вымыла весь навоз. 
Много воды утекло с тех пор. Сейчас данная история известна как шестой подвиг Геракла.
Кто еще не понял, чего я тут упражняюсь в остроумии, говорю напрямую. Сегодняшнее занятие будет посвящено  перенаправлению стандартных потоков ввода и вывода. Хотя нас, в данной ситуации, больше интересует поток ввода. Перенаправлять потоки мы будем в текстовый файл и из текстового файла соответственно.
Мы еще посвятим файлам отдельное занятие, сейчас же коснемся лишь некоторых основных моментов.
С текстовым файлом в Си можно работать в нескольких режимах:
r– режим, при котором мы можем читать информацию из файла. Понятное дело, что файл должен существовать, иначе откуда мы будем читать-то? 
w – режим,  который создает новый файл для записи в него информации. Если файл уже существовал, то его содержимое удаляется.
a – режим, при котором создается новый файл для записи в него, либо если файл уже существует, то в отличие от режима “w” вся прежняя информации будет сохранена. А новые данные будут записаны в конец этого файла.
r+” – при данном режиме, файл открывается сразу и для чтения и для записи. То есть мы можем как читать из файла, так и записывать в него информацию. Файл должен существовать.
w+” создается новый файл для чтения и записи. Если файл уже существовал, то его содержимое будет удалено.
 a+” – файл открывается или создается для чтения и записи информации в него. Вся информация будет записана в конец файла.

Каждый режим немного отличается от другого.  В различных ситуациях удобно использовать тот или иной режим работы с файлом.
Теперь о потоках.
Когда наша программа запускается, у нас уже открыты два стандартных потока:
stdin– стандартный поток ввода, изначально связан с клавиатурой.
stdout– стандартный поток вывода, изначально связан с монитором.

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

Разберем, что означают её аргументы. Всего их три.
Первый аргумент (красный)  - указывает путь к файлу. Двойные кавычки обязательны.  Про двойной обратный слеш отмечу отдельно. Обратный слеш, это начало управляющей последовательности как известно. Но сейчас нам никакие управляющие последовательности не нужны, а  нужен  просто обратный слеш, как символ. В таких случаях используют двойной обратный слеш.
Второй аргумент (зеленый) – указывает режим работы с файлом. В нашем случае обычный режим чтения. Двойные кавычки обязательны.
Третий аргумент (синий) – имя потока.
Что делает функция freopen.  Данная функция, открывает файл (указанный в первом аргументе) с заданным режимом (второй аргумент) и связывает его с потоком, который указан в третьем аргументе.
Получается в нашем случае, мы открываем для чтения файл 1.txt , хранящийся на диске D, и связываем его со стандартным потоком ввода.
Естественно я не привожу сейчас полный синтаксис данной функции, кому интересно может посмотреть его в любом справочнике. Для новичков этих данных более чем достаточно.
Вы уже осознали масштабы, которые открывает нам эта функция?  Допустим мне нужно ввести в программу 100 целых чисел, сохранить их в массив. Для дальнейшей работы с ними. Если я буду каждый раз запускать программу и вводить заново 100 чисел, у меня на это уйдет больше времени, чем на исправление ошибок. Теперь же мы можем, просто записать их один раз в файл и пользоваться им в дальнейшем. Ниже пример.
Листинг 13.1
#include<stdio.h> 
int main(){ 
      freopen("D:\\input.txt","r",stdin); 
      int arr[30]; 
      for (inti=0;i<30;i++) 
            scanf("%d",&arr[i]); 
      for (inti=1;i<30;i++) 
            arr[0]+=arr[i]; 
      printf("summa elementov massiva %d\n",arr[0]); 
      return 0;
}
На следующем рисунке  показан результат работы программы и содержимое файла input.txt.



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

Ну и это еще не всё. Файлы это все конечно классно, но вот вводить 100 чисел руками, даже один раз затруднительно. А точнее лениво. Было бы классно, чтобы программа сама как-нибудь заполняла такой файл разными числами.  Решение давно существует. Функция rand(). Данная функция возвращает случайное целое число в диапазоне от нуля до RAND_MAX.  RAND_MAX это зарезервированная константа языка Си, в ней содержится максимальное целое число, которое может быть возвращено функцией rand().
Для работы с этой функций, не обходимо подключить заголовочный файл stdlib.h Кстати, этом же файле описана константа RAND_MAX. Вы можете найти этот файл у себя на компьютере, и посмотреть её значение.
Рассмотрим простой пример. Необходимо заполнить массив случайными числами.
Листинг 13.2
#include <stdio.h> 
#include <stdlib.h> 
int main(void){ 
      int arr[10]; 
      for(int i=0; i<10; i++) 
            arr[i]=rand(); 
      for(int i=0; i<10; i++) 
            printf("%d\t",arr[i]);
}

Результат работы это программы:

Теперь рассмотрим, как наложить некоторые ограничения на эту функцию.

1. Пусть нам нужны числа, которые больше 100.  Функция rand() выдает нам числа, начиная с нуля. Т.е. самое маленькое число, которое может получиться, будет нуль. Если мы прибавим к возвращаемому значению rand() число 100, то  получим левую границу нужного нам множества.
В нашей программе мы должны были бы написать
arr[i]=rand() + 100;

2. Зайдем с другой стороны. Пусть мы хотим получить числа от нуля до  100. На помощь нам придет математика. Как известно остаток от деления на некоторое число A,всегда меньше этого числа А.
Например, при делении на 4, могут получиться остатки 0,1, 2 и 3.
Таким образом, мы можем взять от числа возвращаемого функцией rand(0) остаток от деления на 100 и получим числа от 0 до 100, причем  число 100 не включено в промежуток.
arr[i]=rand()%100;

3. Теперь пусть нужно получить число из некоторого промежутка [A,B]. Для определенности пусть это будет промежуток [100;200].
Логично было бы объединить эти условия.
arr[i]=100+rand()%200;

Но в таком виде у нас может получиться число и большее 200. Например, если бы rand()вернула бы число 395. Остаток от деления на 200 395%200=195, да еще + 100 получили бы 295. Не совсем то, что нам нужно. Такая строка даст нам числа из интервала [100;299] или если записать это иначе [A;A+B-1].  Как видим правая граница сдвинута на A-1 вправо. Нам её нужно сдвинуть влево. За правую границу у нас отвечает остаток от деления. Значит вычтем её на A-1.
Получим  rand()%(B-A+1).
Ну а в наших условиях:
arr[i]=100+rand()%101;

С учетом изменений получим следующую программу

Листинг 13.3
#include <stdio.h>

#include <stdlib.h>



int main(void){ 
      int arr[10]; 
     
      for(int i=0; i<10; i++) 
            arr[i]=100+rand()%101; 
      for(int i=0; i<10; i++) 
            printf("%d\t",arr[i]);
}

Результат её работы на следующем рисунке


С целыми числами разобрались. А как быть с вещественными? Как вариант можно поделить возвращаемое значение на RAND_MAX, при этом не забывайте о коварном свойствеоперации деления описанной в четвертом занятии.
float b;

b=(float) rand()/RAND_MAX;

Но при таком подходе мы получим случайные числа от нуля до единицы [0;1).
Если же нам надо получить число из интервала [A;B), то можно воспользоваться следующей формулой:
A+rand()*(B-A)/RAND_MAX;

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

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

Занятие 13. Азы работы с файлами и потоками. Случайные числа.


Добрый день друзья.
Не знаю как вас, а меня жутко раздражает одна штука. Вот вам еще не надоело, каждый раз, когда проверяете свою программу вводить заново исходные данные с клавиатуры? Меня этот факт всегда печалил. Хочется быстрее посмотреть, как работает программа, а тут тебе бац, такая засада – снова данные надо вводить. Я думаю пора с этим покончить, тем более для этого у нас уже достаточно знаний и средств.  Так что, сегодняшний мини-урок будет посвящен именно решению этой проблемы.
Не далее, как полтора года назад, я рассказывал вам о стандартных потоках ввода и вывода. Для тех, кто подзабыл, рекомендую повторить материалы занятия про ввод и вывод данных. Теперь еще потренируем свою память. Что вы помните об авгиевых конюшнях?  Ну вспоминайте-вспоминайте. Если никак я напомню.  Был такой персонаж – Геракл. Большой силач, как утверждают некоторые. Так вот, встала перед ним однажды задачка, почистить конюшни, которые 30 лет никто не чистил. История умалчивает, но возможно Геракл, был первым, кто реально мог на себе почувствовать, что значит быть «по уши в дерьме». Говорят здоровяки обычно тупые. Это было явно не про нашего героя. Хитрый Геракл, перегородил   рядом протекающую речку,  направив поток воды в эти самые конюшни. Водичка за день вымыла весь навоз. 
Много воды утекло с тех пор. Сейчас данная история известна как шестой подвиг Геракла.
Кто еще не понял, чего я тут упражняюсь в остроумии, говорю напрямую. Сегодняшнее занятие будет посвящено  перенаправлению стандартных потоков ввода и вывода. Хотя нас, в данной ситуации, больше интересует поток ввода. Перенаправлять потоки мы будем в текстовый файл и из текстового файла соответственно.
Мы еще посвятим файлам отдельное занятие, сейчас же коснемся лишь некоторых основных моментов.
С текстовым файлом в Си можно работать в нескольких режимах:
r– режим, при котором мы можем читать информацию из файла. Понятное дело, что файл должен существовать, иначе откуда мы будем читать-то? 
w – режим,  который создает новый файл для записи в него информации. Если файл уже существовал, то его содержимое удаляется.
a – режим, при котором создается новый файл для записи в него, либо если файл уже существует, то в отличие от режима “w” вся прежняя информации будет сохранена. А новые данные будут записаны в конец этого файла.
r+” – при данном режиме, файл открывается сразу и для чтения и для записи. То есть мы можем как читать из файла, так и записывать в него информацию. Файл должен существовать.
w+” создается новый файл для чтения и записи. Если файл уже существовал, то его содержимое будет удалено.
 a+” – файл открывается или создается для чтения и записи информации в него. Вся информация будет записана в конец файла.

Каждый режим немного отличается от другого.  В различных ситуациях удобно использовать тот или иной режим работы с файлом.
Теперь о потоках.
Когда наша программа запускается, у нас уже открыты два стандартных потока:
stdin– стандартный поток ввода, изначально связан с клавиатурой.
stdout– стандартный поток вывода, изначально связан с монитором.

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

Разберем, что означают её аргументы. Всего их три.
Первый аргумент (красный)  - указывает путь к файлу. Двойные кавычки обязательны.  Про двойной обратный слеш отмечу отдельно. Обратный слеш, это начало управляющей последовательности как известно. Но сейчас нам никакие управляющие последовательности не нужны, а  нужен  просто обратный слеш, как символ. В таких случаях используют двойной обратный слеш.
Второй аргумент (зеленый) – указывает режим работы с файлом. В нашем случае обычный режим чтения. Двойные кавычки обязательны.
Третий аргумент (синий) – имя потока.
Что делает функция freopen.  Данная функция, открывает файл (указанный в первом аргументе) с заданным режимом (второй аргумент) и связывает его с потоком, который указан в третьем аргументе.
Получается в нашем случае, мы открываем для чтения файл 1.txt , хранящийся на диске D, и связываем его со стандартным потоком ввода.
Естественно я не привожу сейчас полный синтаксис данной функции, кому интересно может посмотреть его в любом справочнике. Для новичков этих данных более чем достаточно.
Вы уже осознали масштабы, которые открывает нам эта функция?  Допустим мне нужно ввести в программу 100 целых чисел, сохранить их в массив. Для дальнейшей работы с ними. Если я буду каждый раз запускать программу и вводить заново 100 чисел, у меня на это уйдет больше времени, чем на исправление ошибок. Теперь же мы можем, просто записать их один раз в файл и пользоваться им в дальнейшем. Ниже пример.
Листинг 13.1
#include<stdio.h> 
int main(){ 
      freopen("D:\input.txt","r",stdin); 
      int arr[30]; 
      for (inti=0;i<30;i++) 
            scanf("%d",&arr[i]); 
      for (inti=1;i<30;i++) 
            arr[0]+=arr[i]; 
      printf("summa elementov massiva %dn",arr[0]); 
      return 0;
}
На следующем рисунке  показан результат работы программы и содержимое файла input.txt.



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

Ну и это еще не всё. Файлы это все конечно классно, но вот вводить 100 чисел руками, даже один раз затруднительно. А точнее лениво. Было бы классно, чтобы программа сама как-нибудь заполняла такой файл разными числами.  Решение давно существует. Функция rand(). Данная функция возвращает случайное целое число в диапазоне от нуля до RAND_MAX.  RAND_MAX это зарезервированная константа языка Си, в ней содержится максимальное целое число, которое может быть возвращено функцией rand().
Для работы с этой функций, не обходимо подключить заголовочный файл stdlib.h Кстати, этом же файле описана константа RAND_MAX. Вы можете найти этот файл у себя на компьютере, и посмотреть её значение.
Рассмотрим простой пример. Необходимо заполнить массив случайными числами.
Листинг 13.2
#include <stdio.h> 
#include <stdlib.h> 
int main(void){ 
      int arr[10]; 
      for(int i=0; i<10; i++) 
            arr[i]=rand(); 
      for(int i=0; i<10; i++) 
            printf("%dt",arr[i]);
}

Результат работы это программы:

Теперь рассмотрим, как наложить некоторые ограничения на эту функцию.

1. Пусть нам нужны числа, которые больше 100.  Функция rand() выдает нам числа, начиная с нуля. Т.е. самое маленькое число, которое может получиться, будет нуль. Если мы прибавим к возвращаемому значению rand() число 100, то  получим левую границу нужного нам множества.
В нашей программе мы должны были бы написать
arr[i]=rand() + 100;

2. Зайдем с другой стороны. Пусть мы хотим получить числа от нуля до  100. На помощь нам придет математика. Как известно остаток от деления на некоторое число A,всегда меньше этого числа А.
Например, при делении на 4, могут получиться остатки 0,1, 2 и 3.
Таким образом, мы можем взять от числа возвращаемого функцией rand(0) остаток от деления на 100 и получим числа от 0 до 100, причем  число 100 не включено в промежуток.
arr[i]=rand()%100;

3. Теперь пусть нужно получить число из некоторого промежутка [A,B]. Для определенности пусть это будет промежуток [100;200].
Логично было бы объединить эти условия.
arr[i]=100+rand()%200;

Но в таком виде у нас может получиться число и большее 200. Например, если бы rand()вернула бы число 395. Остаток от деления на 200 395%200=195, да еще + 100 получили бы 295. Не совсем то, что нам нужно. Такая строка даст нам числа из интервала [100;299] или если записать это иначе [A;A+B-1].  Как видим правая граница сдвинута на A-1 вправо. Нам её нужно сдвинуть влево. За правую границу у нас отвечает остаток от деления. Значит вычтем её на A-1.
Получим  rand()%(B-A+1).
Ну а в наших условиях:
arr[i]=100+rand()%101;

С учетом изменений получим следующую программу

Листинг 13.3
#include <stdio.h>

#include <stdlib.h>



int main(void){ 
      int arr[10]; 
     
      for(int i=0; i<10; i++) 
            arr[i]=100+rand()%101; 
      for(int i=0; i<10; i++) 
            printf("%dt",arr[i]);
}

Результат её работы на следующем рисунке


С целыми числами разобрались. А как быть с вещественными? Как вариант можно поделить возвращаемое значение на RAND_MAX, при этом не забывайте о коварном свойствеоперации деления описанной в четвертом занятии.
float b;

b=(float) rand()/RAND_MAX;

Но при таком подходе мы получим случайные числа от нуля до единицы [0;1).
Если же нам надо получить число из интервала [A;B), то можно воспользоваться следующей формулой:
A+rand()*(B-A)/RAND_MAX;

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

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

Создание пустого проекта в среде Visual Studio 2010.

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

Создание пустого проекта в Microsoft Visual Studio 2010.

Запускаем среду программирования. Перед нами открывается начальная страница.
Начальная страница Visual Studio 2010
 Теперь нам нужно создать пустой проект. Для этого переходим на вкладку Файл и выбираем пункт Создать-> Проект. Либо, можно использовать "горячие клавиши" Ctrl+Shift+N.
Создаем проект.
В появившемся окошке выбираем Visual C++, а справа выбираем значок "Пустой проект" (в английской версии это Empty project ). Ниже необходимо придумать название проекту. На следующей картинке проект называется "hello world!" Тут же можно выбрать в какую папку сохранить ваш проект.

Окно основных свойств создаваемого проекта.
Теперь необходимо добавить в наш проект файл с кодом программы.
Есть несколько способов.
1. Представлен на картинке ниже. (используем обозреватель решений)
2. Используя меню "Проект"  Проект->Добавить новый элемент -> Создать элемент
3. Горячие клавиши Ctrl+Shift+A. 
 В открывшемся окне необходимо выбрать значок "Файл .cpp" а чуть ниже написать имя нашему новому файлу. На следующем рисунке новый файл имеет имя example. 
Добавление файла в проект
Нажимаем "Добавить". 


Страница редактора.
Вуаля! Все готово. Теперь можем писать код.


Кстати, те кто пользуется Visual Studio 2008, вы можете аналогично создавать свои проекты. Не мудря с консольным приложением.  Так получается немного быстрее. 

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

Создание пустого проекта в среде Visual Studio 2010.

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

Создание пустого проекта в Microsoft Visual Studio 2010.

Запускаем среду программирования. Перед нами открывается начальная страница.
Начальная страница Visual Studio 2010
 Теперь нам нужно создать пустой проект. Для этого переходим на вкладку Файл и выбираем пункт Создать-> Проект. Либо, можно использовать "горячие клавиши" Ctrl+Shift+N.
Создаем проект.
В появившемся окошке выбираем Visual C++, а справа выбираем значок "Пустой проект" (в английской версии это Empty project ). Ниже необходимо придумать название проекту. На следующей картинке проект называется "hello world!" Тут же можно выбрать в какую папку сохранить ваш проект.

Окно основных свойств создаваемого проекта.
Теперь необходимо добавить в наш проект файл с кодом программы.
Есть несколько способов.
1. Представлен на картинке ниже. (используем обозреватель решений)
2. Используя меню "Проект"  Проект->Добавить новый элемент -> Создать элемент
3. Горячие клавиши Ctrl+Shift+A. 
 В открывшемся окне необходимо выбрать значок "Файл .cpp" а чуть ниже написать имя нашему новому файлу. На следующем рисунке новый файл имеет имя example. 
Добавление файла в проект
Нажимаем "Добавить". 


Страница редактора.
Вуаля! Все готово. Теперь можем писать код.


Кстати, те кто пользуется Visual Studio 2008, вы можете аналогично создавать свои проекты. Не мудря с консольным приложением.  Так получается немного быстрее. 

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

Занятие 12. Простейшие алгоритмы для работы с одномерными массивами. Программирование для начинающих.


Добрый день друзья. Продолжаем изучать одномерные массивы. Сегодня у нас важная тема. Изучим основные алгоритмы работы с массивами: поиск в массиве и его сортировка.
Почему я говорю, что этот урок важный? Просто обработка массивов, наряду с работой со строками,  это одна из самых типичных задач в программировании.  Ни для кого не секрет, компьютеры появились для решения вычислительных задач математики.  А вычислительные задачи, обычно связаны с большим объемом однотипных данных, которые где хранятся? Ага, правильно. Массивы.
Итак, не будем тянуть быка за хвост, ухватимся сразу за рога. Начнем с самого простого.


Поиск минимального элемента в массиве.

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

Рис.1 Одномерный массив
А теперь стоп.  Задумайтесь на секундочку, как вы это сделали? Скорее всего, вы пробежались по столбику глазами, и выявили там самое меньшее число.  Аналогичную процедуру должна выполнить и наша программа.
Первым делом следует подумать, где будет храниться минимальное значение. Для него необходима отдельная переменная. Назовем её min. Сохраним в неё значение первого элемента массива, чтобы нам было с чем сравнивать. Дальше нужно пройтись по массиву. Это не сложно, организуем для этого цикл. В работе с массивами в большинстве случаев используется цикл for. Так как там сразу же есть счетчик, который меняется  каждой итерации. Теперь основная часть алгоритма. Нам нужно последовательно сравнивать элементы массива с минимальным, и если окажется так, что элемент массива меньше минимального, то следует сохранить это значение вместо него.  Вот, в принципе, и весь алгоритм.
Теперь запишем это все в виде программы.

Листинг  12.1
#include <stdio.h> 
int main(){
      int min, arr[11]={5,14,7,4,11,2,6,12,8,7,3};
      min = arr[0];
      for (int i=1; i<11; i++){
            if (arr[i]<min)
                  min = arr[i];
      }
      printf("%dn", min);
      return(0); 
}

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

В дополнение к словесному описанию проиллюстрируем картинкой работу нашей программы.

Рис. 2 Иллюстрация поиска минимума в одномерном массиве.

Еще раз посмотрите приведенный выше код, вам должно быть в нем все понятно. Аналогично можно найти максимальный элемент в массиве.

Сортировка массива.

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

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

Давайте рассмотрим иллюстрирующую этот шаг алгоритма картинку.

Рис. 3. Пример первого шага сортировки одномерного массива методом обмена.

Как видите,  самый тяжелый элемент – тонет, то есть опускается на самый низ. А более легкие элементы, как бы всплывают, как пузырьки в воде. Отсюда, в принципе, и название алгоритма. Как видите, после одного прохода алгоритма, на последнее место. Получается, после одного шага алгоритма один элемент уже точно занимает свое место, и при следующем проходе, нам уже нет необходимости сравнивать предпоследний и последний элемент, так как последний элемент уже на своем месте и ничего менять не придется, так зачем лишнее действие? Правильно, незачем. А мы и не будем.  
Повторяя описанную процедуру n-1 раз (здесь n – размерность массива) получим полностью отсортированный массив.

На следующем рисунке проиллюстрирована работа алгоритма, на примере небольшого массива.

Рис. 4. Иллюстрация сортировки массива методом "пузырька".
Приведем код реализации алгоритма сортировки пузырьком, на языке Си. Обратите внимание на поясняющие комментарии.

Листинг 12.2
#include <stdio.h> 
int main(){
      int n=6, // размерность массива 
            arr[6]={5,14,7,4,11,2}; // массив для сортировки 
      for (int i=n-1;i>0;i--)
            for (intj=0;j<i;j++)
                  if(arr[j]>arr[j+1]){ // если порядок нарушен 
                       //меняем j и j+1 элементы массива местами 
                        int temp=arr[j];
                        arr[j]=arr[j+1];
                        arr[j+1]=tmp;
                  }
      //выводим массив на экран 
                  for (int i=0;i<n; i++)
                        printf("%dn", arr[i]);
      return(0); 
}

Помните, мы говорили, что на каждом шаге нам нужно сокращать длину основного цикла? Т.е. сначала проходим все элементы до n-1, на следующем шаге до n-2 и т.д.  В приведенном выше алгоритме это реализовано внешним циклом. Обратите внимание, что мы впервые используем направление цикла «сверху вниз». Т.е. движемся не от единицы к n, а наоборот, от n-1 к 1.

На этом сегодняшнее занятие окончено.

Задание для практической работы.

  • Задан целочисленный массив a. Написать программу, которая ищет максимальный элемент в массиве и выводит на экран его номер. Обратите внимание не значение, а номер элемента в массиве.
  • Задан целочисленный массив  arr[1000], в котором существует минимум 10 различных значений. Найти 10 по величине элемент массива.
  • Задан целочисленный массив arr[40] . Найти количество элементов массива,  которые по модулю меньше произвольного наперед заданного числа.  Вывести количество таких элементов в массиве. 
  • Составьте функцию, которая по заданному массиву определяет, есть ли в массиве хотя бы один отрицательный элемент.
  • В уроке был приведен кусок кода, в котором с помощью дополнительной переменной меняются местами два элемента массива.  Подумайте, как можно обменять значения двух переменных, не используя дополнительной переменной.  Т.е. например, int a=3, b=5; должно стать a=5, b=3; Помните, что использовать дополнительные переменные нельзя.

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

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

Занятие 12. Простейшие алгоритмы для работы с одномерными массивами. Программирование для начинающих.


Добрый день друзья. Продолжаем изучать одномерные массивы. Сегодня у нас важная тема. Изучим основные алгоритмы работы с массивами: поиск в массиве и его сортировка.
Почему я говорю, что этот урок важный? Просто обработка массивов, наряду с работой со строками,  это одна из самых типичных задач в программировании.  Ни для кого не секрет, компьютеры появились для решения вычислительных задач математики.  А вычислительные задачи, обычно связаны с большим объемом однотипных данных, которые где хранятся? Ага, правильно. Массивы.
Итак, не будем тянуть быка за хвост, ухватимся сразу за рога. Начнем с самого простого.


Поиск минимального элемента в массиве.

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

Рис.1 Одномерный массив
А теперь стоп.  Задумайтесь на секундочку, как вы это сделали? Скорее всего, вы пробежались по столбику глазами, и выявили там самое меньшее число.  Аналогичную процедуру должна выполнить и наша программа.
Первым делом следует подумать, где будет храниться минимальное значение. Для него необходима отдельная переменная. Назовем её min. Сохраним в неё значение первого элемента массива, чтобы нам было с чем сравнивать. Дальше нужно пройтись по массиву. Это не сложно, организуем для этого цикл. В работе с массивами в большинстве случаев используется цикл for. Так как там сразу же есть счетчик, который меняется  каждой итерации. Теперь основная часть алгоритма. Нам нужно последовательно сравнивать элементы массива с минимальным, и если окажется так, что элемент массива меньше минимального, то следует сохранить это значение вместо него.  Вот, в принципе, и весь алгоритм.
Теперь запишем это все в виде программы.

Листинг  12.1
#include <stdio.h> 
int main(){
      int min, arr[11]={5,14,7,4,11,2,6,12,8,7,3};
      min = arr[0];
      for (int i=1; i<11; i++){
            if (arr[i]<min)
                  min = arr[i];
      }
      printf("%d\n", min);
      return(0); 
}

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

В дополнение к словесному описанию проиллюстрируем картинкой работу нашей программы.

Рис. 2 Иллюстрация поиска минимума в одномерном массиве.

Еще раз посмотрите приведенный выше код, вам должно быть в нем все понятно. Аналогично можно найти максимальный элемент в массиве.

Сортировка массива.

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

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

Давайте рассмотрим иллюстрирующую этот шаг алгоритма картинку.

Рис. 3. Пример первого шага сортировки одномерного массива методом обмена.

Как видите,  самый тяжелый элемент – тонет, то есть опускается на самый низ. А более легкие элементы, как бы всплывают, как пузырьки в воде. Отсюда, в принципе, и название алгоритма. Как видите, после одного прохода алгоритма, на последнее место. Получается, после одного шага алгоритма один элемент уже точно занимает свое место, и при следующем проходе, нам уже нет необходимости сравнивать предпоследний и последний элемент, так как последний элемент уже на своем месте и ничего менять не придется, так зачем лишнее действие? Правильно, незачем. А мы и не будем.  
Повторяя описанную процедуру n-1 раз (здесь n – размерность массива) получим полностью отсортированный массив.

На следующем рисунке проиллюстрирована работа алгоритма, на примере небольшого массива.

Рис. 4. Иллюстрация сортировки массива методом "пузырька".
Приведем код реализации алгоритма сортировки пузырьком, на языке Си. Обратите внимание на поясняющие комментарии.

Листинг 12.2
#include <stdio.h> 
int main(){
      int n=6, // размерность массива 
            arr[6]={5,14,7,4,11,2}; // массив для сортировки 
      for (int i=n-1;i>0;i--)
            for (intj=0;j<i;j++)
                  if(arr[j]>arr[j+1]){ // если порядок нарушен 
                       //меняем j и j+1 элементы массива местами 
                        int temp=arr[j];
                        arr[j]=arr[j+1];
                        arr[j+1]=tmp;
                  }
      //выводим массив на экран 
                  for (int i=0;i<n; i++)
                        printf("%d\n", arr[i]);
      return(0); 
}

Помните, мы говорили, что на каждом шаге нам нужно сокращать длину основного цикла? Т.е. сначала проходим все элементы до n-1, на следующем шаге до n-2 и т.д.  В приведенном выше алгоритме это реализовано внешним циклом. Обратите внимание, что мы впервые используем направление цикла «сверху вниз». Т.е. движемся не от единицы к n, а наоборот, от n-1 к 1.

На этом сегодняшнее занятие окончено.

Задание для практической работы.

  • Задан целочисленный массив a. Написать программу, которая ищет максимальный элемент в массиве и выводит на экран его номер. Обратите внимание не значение, а номер элемента в массиве.
  • Задан целочисленный массив  arr[1000], в котором существует минимум 10 различных значений. Найти 10 по величине элемент массива.
  • Задан целочисленный массив arr[40] . Найти количество элементов массива,  которые по модулю меньше произвольного наперед заданного числа.  Вывести количество таких элементов в массиве. 
  • Составьте функцию, которая по заданному массиву определяет, есть ли в массиве хотя бы один отрицательный элемент.
  • В уроке был приведен кусок кода, в котором с помощью дополнительной переменной меняются местами два элемента массива.  Подумайте, как можно обменять значения двух переменных, не используя дополнительной переменной.  Т.е. например, int a=3, b=5; должно стать a=5, b=3; Помните, что использовать дополнительные переменные нельзя.

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

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

Занятие 11. Одномерные массивы. Программирование для начинающих.

Прочитайте улучшенную версию этого урока "Одномерные массивы".

В новой версии:

  • Ещё более доступное объяснение
  • Дополнительные материалы
  • 9 задач на программирование с автоматической проверкой решения

Добрый день. Сегодня поговорим о массивах. Не будем тянуть быка за хвост, а сразу же возьмем его за рога. Вот представьте, вы хотите написать программу, которая будет вычислять среднее арифметическое ваших (или вашего чада) оценок за четверть/семестр.  И при этом вы хотите не только посчитать среднее, а еще и по какому предмету, у вас максимальный средний балл, сколько пятерок получено за все предметы вместе взятые, и сколько неудов поставлено за весь период и много еще чего. Ясное дело все эти оценки надо где-то хранить. Можно конечно завести пару сотен переменных, придумать им всем разные названия, да еще и так, чтобы отличать в каких именно хранятся оценки по математике, а в которых по русскому. А как средний балл потом как считать? Складывать между собой 200 разных переменных и  результат сложения делить на 200? Ну общая идея конечно такова, но в такой реализации её может исполнить только истинный мазохист. Но есть и хорошая новость для нас с вами. Для хранения больших объемов однотипной информации можно использовать массивы.

Как себе представить массив? Да легко. Можно считать, что массив это такая таблица, точнее один столбик (или одна строчка) некоторой таблицы. Возвращаясь к нашей задаче с оценками можно, это выглядело бы так.
Рис.1 Пример целочисленного массива оценок.

Кстати, запомните, в массиве могут храниться данные только одного типа. Т.е. нельзя в одном массиве хранить данные типа int и типа float. В примере выше, все данные в массиве целые числа. Это и понятно, так как оценок 3.7, 4.2, 2.73 не предусмотрено.

Теперь разберемся с тем, как объявить массив, как с ним работать и чем он может нам помочь.

Для начала научимся объявлять массив. Ничего нового тут нет, Америку сейчас я не открою.
Итак, внимание на экран.

Рис 2. Объявление массива

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

Пришло время рассказать еще об одной особенности массива. Каждый элемент массива имеет свой номер. И причем нумерация элементов начинается с нуля. Поэтому многие программисты, вероятно чувствуя свою элитарность начинают счет с нуля. Ну вы понимаете... =)))
Рис 3. Массив оценок

Кстати, это является не только поводом для шуток, но и для трудностей. Одной из самых распространенных ошибок, при работе с массивами является выход за пределы массива. Это когда у нас есть массив из 8 элементов, а мы пытаемся обратиться к элементу с номером 53. Как видите, размерность у нас 8, а последний номер элемента 7. Вроде бы ничего сложно, помни, что у тебя на один номер меньше, чем объявлял и всё. Но нет ошибались, ошибаются и будут ошибаться.

Теперь посмотрим как занести данные в массив, и как их оттуда извлечь.
Например, мы хотим сохранить данные в первый элемент массива ( помним, что это первый элемент это, элемент с цифрой 0).

Листинг 11.1
ochenka [0] = 4; // занести в нулевой элемент  значение 4
printf("%d ", ochenka[0]); // вывести значение хранящееся в                                  // нулевом элементе

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

А теперь рассмотрим одну из задач, которые встречаются в ЕГЭ. Как будет выглядеть массив после выполнения следующей программы. Только не надо её переписывать в среду разработки, прокрутите программу ручками. Это называется компиляция в уме. Нарисуйте массив, и последовательно выполняйте операции, шаг за шагом.

Листинг 11.2

#include <stdio.h>

int main(){
      int A[10], k=0;
       for (int i=0; i<=9; i++)
             A[i]=9-i;
       for (int i=0; i<=4; i++){
             k = A[i];
             A[i] = A[9-i];
             A[9-i] = k;
       } 
      for (int i=0; i<=9; i++)
            printf("%d ", A[i]);
return(0); 
}

Правильный ответ написан ниже, белым цветом.Выделите строку и сразу его увидите. Настоятельно вас прошу, проделать это упражнение самостоятельно, без помощи компьютера.
0 1 2 3 4 5 6 7 8 9

Вернемся на секундочку к нашему примеру, который мы описали в начале урока. Давайте таки найдем среднее арифметическое из оценок и посчитаем количество двоек например.

Листинг 11.3


#include <stdio.h>

int main(){
      int ochenka[8];
      for (int i=0; i<=7; i++){
            printf ("Vvedite %d ochenku:", i+1);
            scanf("%d", &ochenka[i]);
      }
int k=0; //переменная счетчик для двоек 
float s_a=0; //переменная для хранения среднего арифметического 
      for (int i=0; i<=7; i++){
            if (ochenka[i] == 2)
                  k++;
            s_a+=ochenka[i];
      }
printf("Kolichestvo dvoek = %d srednii ball = %3.2f\n", k, s_a/8);
return(0);
}


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



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

Резюме урока:
  •  научились основным действия  при работе с массивами: объявление массива из нескольких элементов, присвоение конкретному элементу некоторого значение.
  •  попрактиковались выполнять программу в уме (на листочке)

 Задание для практической работы:

  • Выполнить задание, которое описано в уроке. Разобраться в нем, особенно если у вас не сразу получился правильный ответ.
  • Задан целочисленный массив из N элементов. N<=30. Пользователь задает некоторое целое число. Необходимо посчитать, количество элементов массива  меньших по модулю этого числа
  • Написать программу, которой на вход подается три массива оценок: сначала по русскому языку, потом по математике и наконец по физике. Причем, количество их заранее не известно, но не превышает 20 штук, по любому из предметов.  По разным предметам может быть введено различное количество оценок. Ввод оценок производится пользователем с клавиатуры, причем сначала он вводит одну из цифр 1, 2, 3 которая определяет,  по какому предмету эта оценка. (1 -русский язык, 2 - математика и 3 - физика)
Ввод данных заканчивает, когда пользователь введет цифру 0.
Например:
1 5
1 4
2 3
1 5
3 4
0
Такой набор исходных данных означает, что ученик получил
по русскому языку 5,4,5
по математике 3
по физике 4
Программа, должна вывести на экран все оценки ученика по каждому из предметов в формате:
rus: 5 4 5
math: 3
fiz: 4
Посчитать средний балл по каждому из предметов. Посчитать количество двоек по каждому из предметов, если их нет, то вывести 0. Посчитать средний балл по всем оценкам. Посчитать количество пятерок, которые получены по всем предметам.


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

Занятие 11. Одномерные массивы. Программирование для начинающих.

Прочитайте улучшенную версию этого урока "Одномерные массивы".

В новой версии:

  • Ещё более доступное объяснение
  • Дополнительные материалы
  • 9 задач на программирование с автоматической проверкой решения

Добрый день. Сегодня поговорим о массивах. Не будем тянуть быка за хвост, а сразу же возьмем его за рога. Вот представьте, вы хотите написать программу, которая будет вычислять среднее арифметическое ваших (или вашего чада) оценок за четверть/семестр.  И при этом вы хотите не только посчитать среднее, а еще и по какому предмету, у вас максимальный средний балл, сколько пятерок получено за все предметы вместе взятые, и сколько неудов поставлено за весь период и много еще чего. Ясное дело все эти оценки надо где-то хранить. Можно конечно завести пару сотен переменных, придумать им всем разные названия, да еще и так, чтобы отличать в каких именно хранятся оценки по математике, а в которых по русскому. А как средний балл потом как считать? Складывать между собой 200 разных переменных и  результат сложения делить на 200? Ну общая идея конечно такова, но в такой реализации её может исполнить только истинный мазохист. Но есть и хорошая новость для нас с вами. Для хранения больших объемов однотипной информации можно использовать массивы.

Как себе представить массив? Да легко. Можно считать, что массив это такая таблица, точнее один столбик (или одна строчка) некоторой таблицы. Возвращаясь к нашей задаче с оценками можно, это выглядело бы так.
Рис.1 Пример целочисленного массива оценок.

Кстати, запомните, в массиве могут храниться данные только одного типа. Т.е. нельзя в одном массиве хранить данные типа int и типа float. В примере выше, все данные в массиве целые числа. Это и понятно, так как оценок 3.7, 4.2, 2.73 не предусмотрено.

Теперь разберемся с тем, как объявить массив, как с ним работать и чем он может нам помочь.

Для начала научимся объявлять массив. Ничего нового тут нет, Америку сейчас я не открою.
Итак, внимание на экран.

Рис 2. Объявление массива

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

Пришло время рассказать еще об одной особенности массива. Каждый элемент массива имеет свой номер. И причем нумерация элементов начинается с нуля. Поэтому многие программисты, вероятно чувствуя свою элитарность начинают счет с нуля. Ну вы понимаете... =)))
Рис 3. Массив оценок

Кстати, это является не только поводом для шуток, но и для трудностей. Одной из самых распространенных ошибок, при работе с массивами является выход за пределы массива. Это когда у нас есть массив из 8 элементов, а мы пытаемся обратиться к элементу с номером 53. Как видите, размерность у нас 8, а последний номер элемента 7. Вроде бы ничего сложно, помни, что у тебя на один номер меньше, чем объявлял и всё. Но нет ошибались, ошибаются и будут ошибаться.

Теперь посмотрим как занести данные в массив, и как их оттуда извлечь.
Например, мы хотим сохранить данные в первый элемент массива ( помним, что это первый элемент это, элемент с цифрой 0).

Листинг 11.1
ochenka [0] = 4; // занести в нулевой элемент  значение 4
printf("%d ", ochenka[0]); // вывести значение хранящееся в                                  // нулевом элементе

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

А теперь рассмотрим одну из задач, которые встречаются в ЕГЭ. Как будет выглядеть массив после выполнения следующей программы. Только не надо её переписывать в среду разработки, прокрутите программу ручками. Это называется компиляция в уме. Нарисуйте массив, и последовательно выполняйте операции, шаг за шагом.

Листинг 11.2

#include <stdio.h>

int main(){
      int A[10], k=0;
       for (int i=0; i<=9; i++)
             A[i]=9-i;
       for (int i=0; i<=4; i++){
             k = A[i];
             A[i] = A[9-i];
             A[9-i] = k;
       } 
      for (int i=0; i<=9; i++)
            printf("%d ", A[i]);
return(0); 
}

Правильный ответ написан ниже, белым цветом.Выделите строку и сразу его увидите. Настоятельно вас прошу, проделать это упражнение самостоятельно, без помощи компьютера.
0 1 2 3 4 5 6 7 8 9

Вернемся на секундочку к нашему примеру, который мы описали в начале урока. Давайте таки найдем среднее арифметическое из оценок и посчитаем количество двоек например.

Листинг 11.3


#include <stdio.h>

int main(){
      int ochenka[8];
      for (int i=0; i<=7; i++){
            printf ("Vvedite %d ochenku:", i+1);
            scanf("%d", &ochenka[i]);
      }
int k=0; //переменная счетчик для двоек 
float s_a=0; //переменная для хранения среднего арифметического 
      for (int i=0; i<=7; i++){
            if (ochenka[i] == 2)
                  k++;
            s_a+=ochenka[i];
      }
printf("Kolichestvo dvoek = %d srednii ball = %3.2f\n", k, s_a/8);
return(0);
}


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



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

Резюме урока:
  •  научились основным действия  при работе с массивами: объявление массива из нескольких элементов, присвоение конкретному элементу некоторого значение.
  •  попрактиковались выполнять программу в уме (на листочке)

 Задание для практической работы:

  • Выполнить задание, которое описано в уроке. Разобраться в нем, особенно если у вас не сразу получился правильный ответ.
  • Задан целочисленный массив из N элементов. N<=30. Пользователь задает некоторое целое число. Необходимо посчитать, количество элементов массива  меньших по модулю этого числа
  • Написать программу, которой на вход подается три массива оценок: сначала по русскому языку, потом по математике и наконец по физике. Причем, количество их заранее не известно, но не превышает 20 штук, по любому из предметов.  По разным предметам может быть введено различное количество оценок. Ввод оценок производится пользователем с клавиатуры, причем сначала он вводит одну из цифр 1, 2, 3 которая определяет,  по какому предмету эта оценка. (1 -русский язык, 2 - математика и 3 - физика)
Ввод данных заканчивает, когда пользователь введет цифру 0.
Например:
1 5
1 4
2 3
1 5
3 4
0
Такой набор исходных данных означает, что ученик получил
по русскому языку 5,4,5
по математике 3
по физике 4
Программа, должна вывести на экран все оценки ученика по каждому из предметов в формате:
rus: 5 4 5
math: 3
fiz: 4
Посчитать средний балл по каждому из предметов. Посчитать количество двоек по каждому из предметов, если их нет, то вывести 0. Посчитать средний балл по всем оценкам. Посчитать количество пятерок, которые получены по всем предметам.


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

Занятие 10. Функции определяемые пользователем.Уроки программирования для чайников. Язык Си.

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

Прочитайте улучшенную версию этого урока "Простые пользовательские функции".

В новой версии:

  • Ещё более доступное объяснение
  • Дополнительные материалы
  • 10 задач на программирование с автоматической проверкой решения

В домашнем задании к уроку №5 я просил вас, написать программу которая решает уравнение ах=b в целых числах относительно х. Что многие успешно сделали. Разберем теперь другую сторону вопроса. Пусть нам надо проверить одну такую программу. Вот её код.

Листинг 10.1

#include <stdio.h>
int main(void)
{
int a,b;
scanf("%d %d",&a,&b);

if((a==0)&&(b!=0)) printf("no solution\n");
else {
      if((a==0)&&(b==0)) printf("many solution\n");
      else {
            if (b%a!=0) printf("no solution\n");
            else
                  printf("%d\n",b/a);
            }
      }
}

return(0);
}

Теперь подумаем как вообще должно решаться такое уравнение и подберем к нему набор тестов.
ах=b следовательно х=b/a. Так как решение в целых числах, то при делении b на a не должно получаться остатка. 
Получаем несколько вариантов решения.
1) а=0 и b=0  х любое число.т.е.  программа должна вывести many solution.
2) a=0 и b!=0 решений нет. Программа должна ответить no solution
3) b делится на а без остатка, то это и b/a решение.
4) b делится на а с остатком. Программа должна вывести no solution

Теперь подберем проверочные значения для каждого случая.
1. а=0, b=0
2. a=0, b=3
3. a=3, b=9
4. a=3, b=7

Теперь нам надо проверить нашу программу на всех этих данных и если в ответе получается 
1.many solution
2. no solution
3.3
4.no solution
то программа написана верно.

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

1 способ.
Мы можем запустить нашу программу несколько раз и подставить в неё эти данные, но это слишком долго. Отметаем этот способ.

2 способ.
Можем переписать программу добавив в неё цикл на 4 шага и каждый раз вводить значения и смотреть ответы.  Программа будет выглядеть следующим образом.

Листинг 10.2
#include <stdio.h>
int main(void)
{

      for(inti=0;i<4;i++) {
            int a,b;
            scanf("%d %d",&a,&b);

            if((a==0)&&(b!=0)) printf("no solution\n");
            else {
                  if((a==0)&&(b==0)) printf("many solution\n");
                  else {
                        if (b%a!=0) printf("no solution\n");
                        else
                             printf("%d\n",b/a);
                        }
                  }
            }
      }
return(0);
}


Вот так будет выглядеть окно программы.

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

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

4 способ.
Хорошо было бы написать так программу, чтобы мы туда один раз скопировали текст своей программы, запустили её и она нам сразу выдала бы, правильно работает программа или нет. 

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

Как задать функцию.
Написать свою функцию очень просто, и вы уже много раз это делали. Любая функция состоит из двух частей: заголовка и тела функции. Покажем это на примере функции main. 
ЗАГОЛОВОК ФУНКЦИИ.
Заголовок функции состоит из трех частей. Сначала нужно указать тип значения которое возвращает функция. В нашем случае это int. То есть когда функция main закончит свою работу она должна вернуть в программу которая её вызвала какое-то целое значение. Если нам не нужно чтобы программа возвращала какое-то значение, то пишем тип void. 
Если бы функция main не должна была бы ничего возвращать, то её заголовок выглядел бы так. 
Листинг 10.3
void main(void)

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

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

Листинг 10.4
int zadanie_1(void) //функция с именем zadanie_1, которая не принимает ни одного параметра, и после окончания работы возвращает значение типа int
void func(int,int)  //функция с именем func, которая принимает два параметра каждый из которых целого типа, и после окончания работы не возвращает ничего.
float f_3(int,int,float ) //функция с именем f_3, которая принимает три аргумента, причем первые два типа int, а третий типа float, и возвращает значение типа float.


ТЕЛО ФУНКЦИИ.
После заголовка функции в фигурных скобках пишется тело функции. 
Здесь описано как должна эта функции работать. Если функция должна возвращать какое-то значение то в теле функции обязательно должна быть инструкция return. В нашем примере, так как функция main должна возвращать значение типа int, после того как программа вывела на экран Hello World, написана строка return (0); Т.е функция возвращает 0, в программу которая её вызвала.


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

Листинг 10.5
void fun1(int a, int b)
{
if((a==0)&&(b!=0)) printf("no solution\n");
else {
      if((a==0)&&(b==0)) printf("many solution\n");
      else {
            if (b%a!=0) printf("no solution\n");
            else { 
                  printf("%d\n",b/a);
            }
      }
}

}


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

Теперь когда мы разобрались с объявлением функции, надо научится её использовать. Любая функция прежде чем быть использованной, должна быть объявлена. Функции объявляются после подключения библиотек, перед объявление функции main.
Напишем программу, в которой объявим нашу функцию и используем её.

Листинг 10.6
#include <stdio.h>

void fun1(int a, int b){
      if((a==0)&&(b!=0)) printf("no solution\n");
      else {
            if((a==0)&&(b==0)) printf("many solution\n");
            else {
                  if (b%a!=0) printf("no solution\n");
            else {
                  printf("%d\n",b/a);
            }
            }
      }
}

int main (void) {
      int x,y;
      scanf("%d %d",&x,&y);
      fun1(x,y);
      fun1(3,9);

return(0);
}

Разберем код нашей программы и заодно посмотрим как она работает.
В первой строке подключаем stdio.h.
Далее объявляем функцию c именем fun1 которая принимает два аргумента целого типа, причем мы сразу дали им имена a и b. Потом в фигурных скобках описано тело функции. Т.е как должна работать наша функция.  Так как функции имеет тип возвращаемого значения void, значит она ничего не возвращает, поэтому нет оператора return в нашей функции.
После тела функции fun1, объявляем функцию main. Объявляем две переменные типа int. Считываем что в них значения которые введет пользователь. Далее вызываем функцию fun1 и передаем ей в качестве аргументов переменные х и у. После этого опять вызываем функцию, но уже передаем ей в качестве аргументов числа 3 и 9.

Результат работы этой программы, при вводе данных 2 и 5 представлен на рисунке ниже.

Разберемся как работает вызов функции. Начнем с более легкого, что происходит когда мы пишем 
fun1(3,9);
После того как программа встречает эту строчку она начинает искать в выше функции main объявление функции с именем fun1 которая принимает два параметра. Параметры которые передаются в функцию в программе, называются фактическими параметрами. Если она не найдет такую функцию, то программа не скомпилируется, ну и следовательно не выполнится. Если такая функция есть, то программа передает управление в эту функцию.
Первым делом она создает две переменные целого типа с именами a и b. Которые указаны в заголовке. И присваивает им значения a=3, b=9.Далее выполняется тело функции. После того, как функция закончит работу, управление снова передается в основную программу. А переменные а и b удаляются. И выполняется строчка следующая за вызовом функции. 

ВАЖНО!!!
Обратите внимание, что после того, как функция закончила свою работу, переменные a и b удаляются. Они доступны только внутри функции в которой они объявлены. 

Теперь разберем случай с передачу в функцию переменных. 
fun1(x,y);
После того как программа встречает эту строчку она начинает искать в выше функции main объявление функции с именем fun1 которая принимает два параметра. Если она не найдет такую функцию, то программа не скомпилируется, ну и следовательно не выполнится. Если такая функция есть, то программа передает управление в эту функцию.
Первым делом она создает две переменные целого типа с именами a и b. Которые указаны в заголовке. В эти значения копируются значения переменных, которые были переданы в функцию в качестве параметров. Далее выполняется тело функции. После того, как функция закончит работу, управление снова передается в основную программу. А переменные а и b удаляются. И выполняется строчка следующая за вызовом функции

ВАЖНО!!!
Обратите внимание, что в функцию передаются не сами переменные х и у, а только копии их значений. Т.е. любые действия производимые потом внутри функции на них никак не влияют.

Еще один важный момент. Область видимости переменных. Как уже говорилось выше переменные a и b доступны только внутри функции, в которой они объявлены. Их область видимости тело функции fun1. За пределами этой функции эти переменные не существуют. 
Из этого следует интересный факт. Например, если мы перепишем код нашей программы вот так.
Листинг 10.7
#include <stdio.h>

void fun1(int a, int b){
      if((a==0)&&(b!=0)) printf("no solution\n");
      else {
            if((a==0)&&(b==0)) printf("many solution\n");
            else {
                  if (b%a!=0) printf("no solution\n");
            else {
                  printf("%d\n",b/a);
            }
            }
      }
}

int main (void) {
      int a,b;
      scanf("%d %d",&a,&b);
      fun1(a,b);

return(0);
}


Переменные a и b которые объявлены в основной программе, и переменные a и b которые объявлены в функции fun1 это совершенно разные переменные. И ничего общего между собой не имеют.
Вот небольшой пример.Подумайте что выведет на экран эта программа а потом проверьте себя выполнив её на компьютере.
Листинг 10.8
#include <stdio.h>

void fun1(int a){
      a=8;
}

int main (void) {
      int a;
      a=3;
      fun1(a);
      printf("%d\n",a);

return(0);
}


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

Листинг 10.9
#include <stdio.h>

void fun1(int,int);

int main (void) {
      int x,y;
      scanf("%d %d",&x,&y);
      fun1(x,y);
      fun1(3,9);

return(0);
}

void fun1(int a, int b){
      if((a==0)&&(b!=0)) printf("no solution\n");
      else {
            if((a==0)&&(b==0)) printf("many solution\n");
            else {
                  if (b%a!=0) printf("no solution\n");
            else {
                  printf("%d\n",b/a);
            }
            }
      }
}

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

Теперь когда мы стали почти что профессионалами в написании функций вернемся опять к нашему исходному примеру. Мы дошли до того, что оформили решение в виде функции на вход которой подается значения а и b. Листинг 10.9.

Теперь перепишем нашу функцию, так, чтобы она возвращала результат своей работы в виде целого числа. Условимся, что 
30001 это ответ many solution.
30002 это ответ no solution.

Цифры выбраны не случайно. Область решения у нас от -30000 до 30000, значит эти числа использовать нельзя. иначе реальный ответ может совпасть с одним из них. Например если many solution обозначить как 3. То тогда при а=3 b=9 ответ функция вернет 3, но мы условились это считать за many solution. И не понятно будет какой ответ.

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

Листинг10.10
#include <stdio.h>

int fun1(int,int);

int main (void) {
     
        printf("%d\n", fun1(0,0));
        printf("%d\n", fun1(0,3));
        printf("%d\n", fun1(3,9));
        printf("%d\n", fun1(3,7));

return(0);
}

int fun1(int a, int b){
      if((a==0)&&(b!=0)) return(30002); //printf("no solution\n");
      else {
            if((a==0)&&(b==0))return(30001); //printf("many solution\n");
            else {
                  if (b%a!=0)return(30002); //printf("no solution\n");
            else {
                  return (b/a); //printf("%d\n",b/a);
            }
            }
      }
}

Так как функция возвращает значение типа int, её можно использовать везде где допустимо использование типа int. В нашем примере мы записали её в оператор printf. Хотя могли бы сначала присвоить её значение какой-нибудь переменной, а потом выводить в printf её значение. 
Например так,
Листинг 10.11
#include <stdio.h>

int fun1(int,int);

int main (void) {
      int x;
        x=fun1(0,0);
        printf("%d\n",x);
        x=fun1(0,3);
        printf("%d\n",x);
        x=fun1(3,9);
        printf("%d\n",x);
        x=fun1(3,7);
        printf("%d\n",x);

return(0);
}

int fun1(int a, int b){
      if((a==0)&&(b!=0)) return(30002); //printf("no solution\n");
      else {
            if((a==0)&&(b==0))return(30001); //printf("many solution\n");
            else {
                  if (b%a!=0)return(30002); //printf("no solution\n");
            else {
                  return (b/a); //printf("%d\n",b/a);
            }
            }
      }
}


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

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

Листинг 10.12
void prov(void){
      if ((fun1(0,0)== 30001)&&(fun1(0,3)==30002)&&(fun1(3,9)==3)&&(fun1(3,7)==30002))
            printf("pravilno\n");
      else
            printf("nepravilno\n");
}


И добавим её в нашу программу.

Листинг 10.13
#include <stdio.h>

int fun1(int,int);
void prov(void);

int main (void) {
     prov();
return(0);
}

void prov(void){
      if ((fun1(0,0)== 30001)&&(fun1(0,3)==30002)&&(fun1(3,9)==3)&&(fun1(3,7)==30002))
            printf("pravilno\n");
      else
            printf("nepravilno\n");
}

int fun1(int a, int b){
      if((a==0)&&(b!=0)) return(30002); //printf("no solution\n");
      else {
            if((a==0)&&(b==0))return(30001); //printf("many solution\n");
            else {
                  if (b%a!=0)return(30002); //printf("no solution\n");
            else {
                  return (b/a); //printf("%d\n",b/a);
            }
            }
      }
}


Если вы запустите эту программу то, она выведет pravilno. Значит наша программа которую мы проверяли работает правильно. Теперь, для проверки чужой программы, нам останется скопировать часть её кода в функцию fun1 и немного подредактировать его. 

На этом урок закончим. А то он и так поучился очень-очень большим.Разбирайтесь. 

Резюме урока:
  • узнали что такое подпрограммы и чем отличаются функции от процедур
  • научились объявлять и описывать свои функции
  • узнали что такое фактические и формальные параметры
  • получили представление об области видимости переменных

Задание для практической работы.

Отдельного задания не будет. С этого дня оформляйте решения ваших заданий в виде отдельных функций.


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

Занятие 10. Функции определяемые пользователем.Уроки программирования для чайников. Язык Си.

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

Прочитайте улучшенную версию этого урока "Простые пользовательские функции".

В новой версии:

  • Ещё более доступное объяснение
  • Дополнительные материалы
  • 10 задач на программирование с автоматической проверкой решения

В домашнем задании к уроку №5 я просил вас, написать программу которая решает уравнение ах=b в целых числах относительно х. Что многие успешно сделали. Разберем теперь другую сторону вопроса. Пусть нам надо проверить одну такую программу. Вот её код.

Листинг 10.1

#include <stdio.h>
int main(void)
{
int a,b;
scanf("%d %d",&a,&b);

if((a==0)&&(b!=0)) printf("no solution\n");
else {
      if((a==0)&&(b==0)) printf("many solution\n");
      else {
            if (b%a!=0) printf("no solution\n");
            else
                  printf("%d\n",b/a);
            }
      }
}

return(0);
}

Теперь подумаем как вообще должно решаться такое уравнение и подберем к нему набор тестов.
ах=b следовательно х=b/a. Так как решение в целых числах, то при делении b на a не должно получаться остатка. 
Получаем несколько вариантов решения.
1) а=0 и b=0  х любое число.т.е.  программа должна вывести many solution.
2) a=0 и b!=0 решений нет. Программа должна ответить no solution
3) b делится на а без остатка, то это и b/a решение.
4) b делится на а с остатком. Программа должна вывести no solution

Теперь подберем проверочные значения для каждого случая.
1. а=0, b=0
2. a=0, b=3
3. a=3, b=9
4. a=3, b=7

Теперь нам надо проверить нашу программу на всех этих данных и если в ответе получается 
1.many solution
2. no solution
3.3
4.no solution
то программа написана верно.

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

1 способ.
Мы можем запустить нашу программу несколько раз и подставить в неё эти данные, но это слишком долго. Отметаем этот способ.

2 способ.
Можем переписать программу добавив в неё цикл на 4 шага и каждый раз вводить значения и смотреть ответы.  Программа будет выглядеть следующим образом.

Листинг 10.2
#include <stdio.h>
int main(void)
{

      for(inti=0;i<4;i++) {
            int a,b;
            scanf("%d %d",&a,&b);

            if((a==0)&&(b!=0)) printf("no solution\n");
            else {
                  if((a==0)&&(b==0)) printf("many solution\n");
                  else {
                        if (b%a!=0) printf("no solution\n");
                        else
                             printf("%d\n",b/a);
                        }
                  }
            }
      }
return(0);
}


Вот так будет выглядеть окно программы.

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

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

4 способ.
Хорошо было бы написать так программу, чтобы мы туда один раз скопировали текст своей программы, запустили её и она нам сразу выдала бы, правильно работает программа или нет. 

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

Как задать функцию.
Написать свою функцию очень просто, и вы уже много раз это делали. Любая функция состоит из двух частей: заголовка и тела функции. Покажем это на примере функции main. 
ЗАГОЛОВОК ФУНКЦИИ.
Заголовок функции состоит из трех частей. Сначала нужно указать тип значения которое возвращает функция. В нашем случае это int. То есть когда функция main закончит свою работу она должна вернуть в программу которая её вызвала какое-то целое значение. Если нам не нужно чтобы программа возвращала какое-то значение, то пишем тип void. 
Если бы функция main не должна была бы ничего возвращать, то её заголовок выглядел бы так. 
Листинг 10.3
void main(void)

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

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

Листинг 10.4
int zadanie_1(void) //функция с именем zadanie_1, которая не принимает ни одного параметра, и после окончания работы возвращает значение типа int
void func(int,int)  //функция с именем func, которая принимает два параметра каждый из которых целого типа, и после окончания работы не возвращает ничего.
float f_3(int,int,float ) //функция с именем f_3, которая принимает три аргумента, причем первые два типа int, а третий типа float, и возвращает значение типа float.


ТЕЛО ФУНКЦИИ.
После заголовка функции в фигурных скобках пишется тело функции. 
Здесь описано как должна эта функции работать. Если функция должна возвращать какое-то значение то в теле функции обязательно должна быть инструкция return. В нашем примере, так как функция main должна возвращать значение типа int, после того как программа вывела на экран Hello World, написана строка return (0); Т.е функция возвращает 0, в программу которая её вызвала.


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

Листинг 10.5
void fun1(int a, int b)
{
if((a==0)&&(b!=0)) printf("no solution\n");
else {
      if((a==0)&&(b==0)) printf("many solution\n");
      else {
            if (b%a!=0) printf("no solution\n");
            else { 
                  printf("%d\n",b/a);
            }
      }
}

}


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

Теперь когда мы разобрались с объявлением функции, надо научится её использовать. Любая функция прежде чем быть использованной, должна быть объявлена. Функции объявляются после подключения библиотек, перед объявление функции main.
Напишем программу, в которой объявим нашу функцию и используем её.

Листинг 10.6
#include <stdio.h>

void fun1(int a, int b){
      if((a==0)&&(b!=0)) printf("no solution\n");
      else {
            if((a==0)&&(b==0)) printf("many solution\n");
            else {
                  if (b%a!=0) printf("no solution\n");
            else {
                  printf("%d\n",b/a);
            }
            }
      }
}

int main (void) {
      int x,y;
      scanf("%d %d",&x,&y);
      fun1(x,y);
      fun1(3,9);

return(0);
}

Разберем код нашей программы и заодно посмотрим как она работает.
В первой строке подключаем stdio.h.
Далее объявляем функцию c именем fun1 которая принимает два аргумента целого типа, причем мы сразу дали им имена a и b. Потом в фигурных скобках описано тело функции. Т.е как должна работать наша функция.  Так как функции имеет тип возвращаемого значения void, значит она ничего не возвращает, поэтому нет оператора return в нашей функции.
После тела функции fun1, объявляем функцию main. Объявляем две переменные типа int. Считываем что в них значения которые введет пользователь. Далее вызываем функцию fun1 и передаем ей в качестве аргументов переменные х и у. После этого опять вызываем функцию, но уже передаем ей в качестве аргументов числа 3 и 9.

Результат работы этой программы, при вводе данных 2 и 5 представлен на рисунке ниже.

Разберемся как работает вызов функции. Начнем с более легкого, что происходит когда мы пишем 
fun1(3,9);
После того как программа встречает эту строчку она начинает искать в выше функции main объявление функции с именем fun1 которая принимает два параметра. Параметры которые передаются в функцию в программе, называются фактическими параметрами. Если она не найдет такую функцию, то программа не скомпилируется, ну и следовательно не выполнится. Если такая функция есть, то программа передает управление в эту функцию.
Первым делом она создает две переменные целого типа с именами a и b. Которые указаны в заголовке. И присваивает им значения a=3, b=9.Далее выполняется тело функции. После того, как функция закончит работу, управление снова передается в основную программу. А переменные а и b удаляются. И выполняется строчка следующая за вызовом функции. 

ВАЖНО!!!
Обратите внимание, что после того, как функция закончила свою работу, переменные a и b удаляются. Они доступны только внутри функции в которой они объявлены. 

Теперь разберем случай с передачу в функцию переменных. 
fun1(x,y);
После того как программа встречает эту строчку она начинает искать в выше функции main объявление функции с именем fun1 которая принимает два параметра. Если она не найдет такую функцию, то программа не скомпилируется, ну и следовательно не выполнится. Если такая функция есть, то программа передает управление в эту функцию.
Первым делом она создает две переменные целого типа с именами a и b. Которые указаны в заголовке. В эти значения копируются значения переменных, которые были переданы в функцию в качестве параметров. Далее выполняется тело функции. После того, как функция закончит работу, управление снова передается в основную программу. А переменные а и b удаляются. И выполняется строчка следующая за вызовом функции

ВАЖНО!!!
Обратите внимание, что в функцию передаются не сами переменные х и у, а только копии их значений. Т.е. любые действия производимые потом внутри функции на них никак не влияют.

Еще один важный момент. Область видимости переменных. Как уже говорилось выше переменные a и b доступны только внутри функции, в которой они объявлены. Их область видимости тело функции fun1. За пределами этой функции эти переменные не существуют. 
Из этого следует интересный факт. Например, если мы перепишем код нашей программы вот так.
Листинг 10.7
#include <stdio.h>

void fun1(int a, int b){
      if((a==0)&&(b!=0)) printf("no solution\n");
      else {
            if((a==0)&&(b==0)) printf("many solution\n");
            else {
                  if (b%a!=0) printf("no solution\n");
            else {
                  printf("%d\n",b/a);
            }
            }
      }
}

int main (void) {
      int a,b;
      scanf("%d %d",&a,&b);
      fun1(a,b);

return(0);
}


Переменные a и b которые объявлены в основной программе, и переменные a и b которые объявлены в функции fun1 это совершенно разные переменные. И ничего общего между собой не имеют.
Вот небольшой пример.Подумайте что выведет на экран эта программа а потом проверьте себя выполнив её на компьютере.
Листинг 10.8
#include <stdio.h>

void fun1(int a){
      a=8;
}

int main (void) {
      int a;
      a=3;
      fun1(a);
      printf("%d\n",a);

return(0);
}


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

Листинг 10.9
#include <stdio.h>

void fun1(int,int);

int main (void) {
      int x,y;
      scanf("%d %d",&x,&y);
      fun1(x,y);
      fun1(3,9);

return(0);
}

void fun1(int a, int b){
      if((a==0)&&(b!=0)) printf("no solution\n");
      else {
            if((a==0)&&(b==0)) printf("many solution\n");
            else {
                  if (b%a!=0) printf("no solution\n");
            else {
                  printf("%d\n",b/a);
            }
            }
      }
}

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

Теперь когда мы стали почти что профессионалами в написании функций вернемся опять к нашему исходному примеру. Мы дошли до того, что оформили решение в виде функции на вход которой подается значения а и b. Листинг 10.9.

Теперь перепишем нашу функцию, так, чтобы она возвращала результат своей работы в виде целого числа. Условимся, что 
30001 это ответ many solution.
30002 это ответ no solution.

Цифры выбраны не случайно. Область решения у нас от -30000 до 30000, значит эти числа использовать нельзя. иначе реальный ответ может совпасть с одним из них. Например если many solution обозначить как 3. То тогда при а=3 b=9 ответ функция вернет 3, но мы условились это считать за many solution. И не понятно будет какой ответ.

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

Листинг10.10
#include <stdio.h>

int fun1(int,int);

int main (void) {
     
        printf("%d\n", fun1(0,0));
        printf("%d\n", fun1(0,3));
        printf("%d\n", fun1(3,9));
        printf("%d\n", fun1(3,7));

return(0);
}

int fun1(int a, int b){
      if((a==0)&&(b!=0)) return(30002); //printf("no solution\n");
      else {
            if((a==0)&&(b==0))return(30001); //printf("many solution\n");
            else {
                  if (b%a!=0)return(30002); //printf("no solution\n");
            else {
                  return (b/a); //printf("%d\n",b/a);
            }
            }
      }
}

Так как функция возвращает значение типа int, её можно использовать везде где допустимо использование типа int. В нашем примере мы записали её в оператор printf. Хотя могли бы сначала присвоить её значение какой-нибудь переменной, а потом выводить в printf её значение. 
Например так,
Листинг 10.11
#include <stdio.h>

int fun1(int,int);

int main (void) {
      int x;
        x=fun1(0,0);
        printf("%d\n",x);
        x=fun1(0,3);
        printf("%d\n",x);
        x=fun1(3,9);
        printf("%d\n",x);
        x=fun1(3,7);
        printf("%d\n",x);

return(0);
}

int fun1(int a, int b){
      if((a==0)&&(b!=0)) return(30002); //printf("no solution\n");
      else {
            if((a==0)&&(b==0))return(30001); //printf("many solution\n");
            else {
                  if (b%a!=0)return(30002); //printf("no solution\n");
            else {
                  return (b/a); //printf("%d\n",b/a);
            }
            }
      }
}


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

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

Листинг 10.12
void prov(void){
      if ((fun1(0,0)== 30001)&&(fun1(0,3)==30002)&&(fun1(3,9)==3)&&(fun1(3,7)==30002))
            printf("pravilno\n");
      else
            printf("nepravilno\n");
}


И добавим её в нашу программу.

Листинг 10.13
#include <stdio.h>

int fun1(int,int);
void prov(void);

int main (void) {
     prov();
return(0);
}

void prov(void){
      if ((fun1(0,0)== 30001)&&(fun1(0,3)==30002)&&(fun1(3,9)==3)&&(fun1(3,7)==30002))
            printf("pravilno\n");
      else
            printf("nepravilno\n");
}

int fun1(int a, int b){
      if((a==0)&&(b!=0)) return(30002); //printf("no solution\n");
      else {
            if((a==0)&&(b==0))return(30001); //printf("many solution\n");
            else {
                  if (b%a!=0)return(30002); //printf("no solution\n");
            else {
                  return (b/a); //printf("%d\n",b/a);
            }
            }
      }
}


Если вы запустите эту программу то, она выведет pravilno. Значит наша программа которую мы проверяли работает правильно. Теперь, для проверки чужой программы, нам останется скопировать часть её кода в функцию fun1 и немного подредактировать его. 

На этом урок закончим. А то он и так поучился очень-очень большим.Разбирайтесь. 

Резюме урока:
  • узнали что такое подпрограммы и чем отличаются функции от процедур
  • научились объявлять и описывать свои функции
  • узнали что такое фактические и формальные параметры
  • получили представление об области видимости переменных

Задание для практической работы.

Отдельного задания не будет. С этого дня оформляйте решения ваших заданий в виде отдельных функций.


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