решение домашнего задания с предыдущей лекцииУсловие:
Напишите функцию void print_arr(int *arr, unsigned int size), получающую массив и его размер, которая распечатывает массив на экран. Позовите функцию несколько раз. Если вызов четный, то массив распечатывается с начала, при нечетном вызове массив распечатается с конца.
Требования:
1. Запрещено менять описание функции, то есть добавлять/убирать параметры.
2. Запрещено внутри функции print_arr использовать цикл.
3. Счет вызовов начинается с 1
Пример (3 вызова):
Массив:
{0, 1, 2, 3, 4}
результат вызова 1:
4 3 2 1 0
результат вызова 2:
0 1 2 3 4
результат вызова 3:
4 3 2 1 0
решениеКод:
Результат:
лекцияИтак, мы подошли в плотную к тому, что совсем недавно только "маячило на горизонте". "Программист может создавать собственные типы переменных" - сказал как-то я на одной из предыдущих лекциях. Думаю, что это слушалось очень загадочно, интересно и маняще....
Мы начнем по чуть-чуть. Шажок за шажком. Это важная часть не только языка, но и всего программирования в целом, потому что с того самого момента, как языки программирования дали возможность создавать собственные типы - родился совершенно новый взгляд на программирование! Это ни больше ни меньше - революция программирования! Подход, в котором собственный тип ставится "во главу угла" получил название "Объектно-ориентированное программирование" (ООП). Напомню, что пока что мы изучаем более старый подход, получивший название "процедуральное программирование", где "во главу угла" было поставлено понятие "функция".
В этой лекции мы пока не увидим ООП, потому что процедуральное еще не до конца нами исчерпано, но то, что я покажу на этой лекции - положило начало ООП.
Энумераторы (англ. Enum)
Энумераторы - это самый простой и ограниченный способ создать свой тип (typedef я не считаю). Представьте ситуацию, в которой, вам, к примеру, надо работать с днями недели. Допустим, мы создаем 7 стрингов. В первом написано "понедельник", а в последнем написано "воскресение". Тогда, во всех функциях, в которые мы получили день и хотим узнать: какой день недели мы получили, мы пишем 7 блоков if, внутри которых мы сравниваем стринги, что, конечно работает медленно.
Хорошо, говорим мы. Мы хотим упростить работу: мы договоримся, что понедельник это 1, а воскресение - 7. Становится проще работать с такими вещами, плюс мы получаем возможность быстро узнать, сколько дней прошло между понедельником и воскресением. То есть, действие 7 - 1, скажет нам, что прошло 6 дней (что невозможно сделать со стрингами). Но какой минус мы получаем автоматом? Наверное САМЫЙ главный минус - не читабельность такого кода.
Можно сделать следующую штуку: объявить 7 глобальных констант и тогда это выглядит так:
const int MONDAY = 1;
......
const int SUNDAY = 7;
и мы получаем читабельность кода. Но программеры С (впервые энумераторы появились в С) пошли далее, они сказали: давайте дадим возможность в таких случаях объявить собственный тип! Мы получим штуку, которая будет обладать преимуществами обоих подходов (стринги и числа). Мы объявим "перечисления" (значение слова "enum" в английском языке):
enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY};
давайте разберем: в начале мы пишем, что мы создаем энумератор (перечисление), после этого, мы пишем название нашего типа. То, что мы создали - это ТИП переменной, собственный тип! То есть на него распространяются все законы, которые, скажем, распространялись на тип int. Точно так же, как мы могли создать переменную типа int в коде:
int num;
точно так же мы можем создать переменную типа Day в коде:
Day today;
В последствии, мы можем присвоить ему значение:
today = SUNDAY;
Естественно, что тип принимает только значения, перечисленные в момент создания в фигурных скобках. Мы так же можем передать его в функцию:
// описание функции:
void foo ( Day day );
// вызов функции в коде:
foo ( today );
Понятно, что мы также можем его вернуть из функции:
Day foo ( void );
Мы можем создать указатель или ссылку на этот тип:
Day * p = & today;
Day & r = today;
Мы можем создать массив таких типов:
Day week [ 7 ];
Day week [ ] = { MONDAY, FRIDAY, SUNDAY }; // массив из трех элементов
За этими значениями стоят числа, то есть, на самом деле, MONDAY, FRIDAY, SUNDAY и т.д. - это числа. По-умолчанию, они начинаются с нуля, то есть, в том виде, в котором я создал тип Day, значение MONDAY это ноль, TUESDAY - это один и так далее, до 6-ти включительно (SUNDAY это 6) в том порядке, в котором они указаны. Это значит, что если мы пишем:
FRIDAY - MONDAY, то мы подразумеваем 4 - 0
Допустим, сегодня понедельник. Мы спрашиваем в коде: "сколько дней до пятницы?", что нужно сделать? Нужно отнять пятницу от понедельника:
int num_of_days = FRIDAY - MONDAY;
И мы получим ответ: 4 дня.
Но начало с нуля, в днях недели - не интуитивное.... поэтому, мы можем сами назначить числовые значения, при создании типа:
enum Day { MONDAY=1, TUESDAY=2, WEDNESDAY=3, THURSDAY=4, FRIDAY=5, SATURDAY=6, SUNDAY=7};
Но, если наши числовые значения тоже идут по порядку, просто не начинаются с нуля, то можно назначить лишь первое:
enum Day { MONDAY=1, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY};
Мы сказали: первый элемент это 1. Далее, язык сам "раздаст" значения в порядке возрастания, то есть - обе эти записи дают совершенно идентичный результат.
Однако, мы можем написать:
enum Day { MONDAY=1, TUESDAY, WEDNESDAY=33, THURSDAY, FRIDAY, SATURDAY=538, SUNDAY};
и тогда получится следующее:
enum Day { MONDAY=1, TUESDAY=2, WEDNESDAY=33, THURSDAY=34, FRIDAY=35, SATURDAY=538, SUNDAY=539};
Я люблю писать определение энумератора "в столбик":
enum Day {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
};
И тогда это становится более читабельным:
enum Day {
MONDAY = 1,
TUESDAY,
WEDNESDAY = 33,
THURSDAY,
FRIDAY,
SATURDAY = 538, // и тогда тут можно написать комментарий, вот так и что-то объяснить
SUNDAY
};
Сейчас я напишу код примера, чтобы просто наглядно посмотреть, как это выглядит в коде. Пример немного тупой, то есть - в нем нет какой-то логики, это чистая демонстрация возможностей:
результат:
Структуры
Из-за своих ограничений, а именно того, что на самом деле энумераторы могут хранить только числа, этот тип помогает решать очень конкретные задачи, и не дает возможность удобно хранить данные. Когда нужна большая гибкость, то на помощь приходят структуры.
Что же такое структура? То, что я расскажу сейчас, раскроет только небольшую часть возможностей, но я не хочу сваливать весь объем информации сразу, для одной лекции это будет слишком.
Представьте, что мы пишем программу. В нашей программе фигурирует студент. У студента такие данные:
1. номер студенческого билета
2. имя и фамилия
3. оценки за экзамены
Ну, на первое время хватит. Согласитесь, что было бы очень удобно, если бы это было сгруппировано в один тип. Представьте, что у вас есть тип "Студент", который выгодно отличается тем, что такой тип, словно "портфель", туда можно "положить" значения сразу нескольких типов: числа... стринги...
На каждого студента, в том заведении, для которого пишется программа, заведен такой вот виртуальный портфель, в котором все про него! Мы бы могли составлять целые списки студентов: кто поехал на олимпиаду по математике? А кто - кандидат на вылет.... Но о списках мы поговорим позже, а пока создадим тип "Студент" и рассмотрим его возможности.
Давайте начнем всматриваться в то, что я написал. Я объявил новый тип переменной и дал ему имя, написав
struct Student { } ;
То есть структура Студент. Внутри фигурных скобок я напишу типы переменных, которые содержатся в структуре, раздав им понятные имена. Номер студенческого билета ("student_id") - это тип long, так как скорее всего - номер длинный. Имя и фамилию ("first_name", "last_name") я буду содержать в типе char *. Так как я не знаю, какой длины имя и фамилия у конкретного студента, то я аллокирую память с нужным мне размером, а потом скопирую туда эти данные. В ВУЗе, в котором учится студент - всего 5 курсов (отличный вуз). Поэтому, я сделаю следующую вещь: я создам массив целых чисел, размером 5 и для каждого курса выделю место. Например, математика это 0, физика - 1, английский - 2 и так далее. Скорее всего, дополнительно, я создам Enum, в котором это будет прописано.
Я хочу написать коротенькую программу, чтобы наглядно показать, как работать с этим всем. В моем сценарии, программа получит данные и заполнит поля, а потом - распечатает это на экран. Я не буду писать в коде как программа получает эти данные, потому что это мы делать умеем (например с клавиатуры), я лишь освещу работу непосредственно со структурой. Итак код:
Код:
Результат:
Ну, что, разберем?
Я создал переменную Student и назвал ее britney. Это важно понять! Тип переменной: Student, имя: britney, это все равно, как если бы я написал int britney, где int это тип, а britney - имя.
Моя переменная состоит из набора примитивных переменных: long, 2 стринга и массив int. К каждой из них я обращаюсь через точку: имя переменной (структуры) ТОЧКА имя переменной (примитивной). Я показал, как писать и читать из такой переменной. Естественно, что на мою переменную распространяются ВСЕ законы обычных переменных: ее можно передать в функцию, ее можно получить из функции, можно создать указатель или ссылку на нее, можно создать массив таких переменных.... но обо всем этом мы поговорим в следующий раз, а пока - достаточно.
Домашнее задание:
Напишите программу, которая вычисляет студента, которого нужно послать на олимпиаду по физике.
Немного о вузе: в вузе есть всего 3 курса: физика, математика и английский. В Вузе - 100 бальная система оценок. Вуз заботится о своем имени и поэтому, он не посылает на такие мероприятия студентов, у которых средняя оценка по всем курсам ниже 85. На олимпиаду должны поехать студенты, у которых оценка по физике не меньше 90.
Создайте 3 студента с такими оценками:
1. физика 95, математика 90, английский 85.
2. физика 95, математика 60, английский 60.
3. физика 80, математика 90, английский 95.
Раздайте им произвольные имена.
Программа напишет на экран имена всех студентов, которые достойны поездки на олимпиаду по физике.
Примечание: Не обязательно получать данные с "внешнего источника", можно прописать их в коде. Цель задания - работа со структурами, а не с клавиатурой.
Совет: даже больше чем совет, скорее требование: разбейте код на функции, не пишите все в main, это не красиво.
Удачи!