Скачать презентацию Тема Управляющие конструкции языка С продолжение Лекция 27 Скачать презентацию Тема Управляющие конструкции языка С продолжение Лекция 27

Лекция-06-07.ppt

  • Количество слайдов: 26

Тема «Управляющие конструкции языка С» (продолжение) Лекция 27. 09. 11 г. 1 Тема «Управляющие конструкции языка С» (продолжение) Лекция 27. 09. 11 г. 1

Обзор вопросов прошлой лекции ü ü ü ü ü Операции с присваиванием Тернарная условная Обзор вопросов прошлой лекции ü ü ü ü ü Операции с присваиванием Тернарная условная операция Приоритет и ассоциирование операций Преобразование (приведение) типов Тема «Управляющие конструкции языка С» Простые операторы и блоки Операторы простого выбора Вложенные операторы выбора Оператор множественного выбора switch Операторы повторения (цикла): с предусловием, оператор for, с постусловием Лекция 27. 09. 11 г. 2

Оператор break Иногда возникает необходимость прервать выполнение тела цикла и «досрочно» выйти за пределы Оператор break Иногда возникает необходимость прервать выполнение тела цикла и «досрочно» выйти за пределы цикла. Это можно сделать, разместив в теле цикла оператор break. Это оператор вызывает «безусловный переход» в точку программы, расположенную непосредственно за циклом. Оператор break может использоваться в любом операторе цикла, а также в операторе множественного выбора switch. Пример: программа удаляет «незначащие» символы в конце строки s. #include #include int main() { int n; char s[] = "qwerty tt nn"; printf("before: n"); printf("number of characters(%s) = %dn", s, strlen(s)); for (n = strlen(s)-1; n >= 0; n--) if (s[n] != ' ' && s[n] != 't' && s[n] != 'n') break; s[n+1] = ''; printf("after: n"); printf("number of characters(%s) = %dn", s, strlen(s)); system("PAUSE"); return 0; } Лекция 27. 09. 11 г. 3

Оператор continue Этот оператор похож на break, но, в отличие от break, досрочно прекращает Оператор continue Этот оператор похож на break, но, в отличие от break, досрочно прекращает выполнение текущей итерации цикла, а не всего оператора цикла. Для циклов while и do это означает переход к проверке условия, а для цикла for – вычисление выражения 3, а уже затем переход к проверке условия. Пример: подсчитать количество положительных элементов массива и найти их среднее арифметическое. #include #include int main() { int x[] = {-1, 4, 0, -3, -7, 5, 11, -2}, i, n; double s; for(i = 0, n = 0, s = 0. 0; i < sizeof(x) / sizeof(x[0]); i++) { if(x[i] <= 0) continue; s += x[i]; n++; } if(n) s /= n; printf("number of positive elements = %dn", n); printf("mean value = %fn", s); system("PAUSE"); return 0; } Лекция 27. 09. 11 г. 4

Оператор перехода goto и метки Оператор goto метка вызывает безусловный переход к оператору (в Оператор перехода goto и метки Оператор goto метка вызывает безусловный переход к оператору (в той же самой функции) перед которым записана метка (обычное имя, заканчивающееся символом : - двоеточие). Пример: проверить, имеют ли два массива хотя бы один общий элемент. #include #include int main() { int a[] = {-1, 4, 0, -3, -7, 5, 11, -2}, i; int b[] = {7, -12, 8, 9, 11, -2}, j; int n = sizeof(a) / sizeof(a[0]); int m = sizeof(b) / sizeof(b[0]); for (i = 0; i < n; i++) for (j = 0; j < m; j++) if (a[i] == b[j]) goto found; printf("no common elementsn"); goto final; found: printf("common elements: a[%d] == b[%d]n", i, j); final: system("PAUSE"); return 0; } Лекция 27. 09. 11 г. 5

Оператор перехода (продолжение) Оператор goto является «нежелательным» оператором, т. к. «запутывает» логическую структуру программы, Оператор перехода (продолжение) Оператор goto является «нежелательным» оператором, т. к. «запутывает» логическую структуру программы, однако бывают ситуации, когда программа без goto получается более громоздкой, чем с goto. Пример: предыдущая программа без goto. #include #include int main() { int a[] = {-1, 4, 0, -3, -7, 5, 11, -2}, i; int b[] = {7, -12, 8, 9, 11, -2}, j, found = 0; int n = sizeof(a) / sizeof(a[0]); int m = sizeof(b) / sizeof(b[0]); for (i = 0; i < n && !found; i++) for (j = 0; j < m && !found; j++) if (a[i] == b[j]) found = 1; if (found) printf("common elements: a[%d] == b[%d]n", i, j); else printf("no common elementsn"); system("PAUSE"); return 0; } Лекция 27. 09. 11 г. 6

Тема «Функции и структура программы» Лекция 27. 09. 11 г. 7 Тема «Функции и структура программы» Лекция 27. 09. 11 г. 7

Функции и модульность программы Ø Модульность в языках программирования — принцип, согласно которому программное Функции и модульность программы Ø Модульность в языках программирования — принцип, согласно которому программное средство - ПС (программа, библиотека, web-приложение и др. ) разделяется на отдельные сущности, называемые модулями. Модульность позволяет упростить задачи проектирования ПС и распределения процесса разработки ПС между группами разработчиков, а также позволяет реализовать методологию повторного использования кода. Ø При разбиении ПС на модули для каждого модуля указывается реализуемая им функциональность, а также связи с другими модулями. Роль модулей могут играть структуры данных, библиотеки функций, классы, сервисы и др. программные единицы, реализующие некоторую функциональность и предоставляющие интерфейс к ней. Ø В языке С модульность поддерживается функциями, препроцессоными командами, многофайловой структурой программы и заголовочными файлами. Лекция 27. 09. 11 г. 8

Функции и модульность программы Функции разбивают большие вычислительные задачи на более мелкие и позволяют Функции и модульность программы Функции разбивают большие вычислительные задачи на более мелкие и позволяют инкапсулировать ( «упрятать» в оболочку) детали реализации некоторой функциональности, предоставив пользователям ( «клиентам» ) формат обращения к этой функциональности (интерфейс). Это делает программу в целом более ясной и облегчает внесение в нее изменений. Пример: функция, преобразующая символьное изображение числа, записанное в строке, в само число. #include реализация int atoi_ (char s[]) { int i, n, sign; for (i = 0; isspace(s[i]); i++) ; sign = (s[i] == '-') ? -1 : 1; if (s[i] == '+' || s[i] == '-') i++; for (n = 0; isdigit(s[i]); i++) n = 10 * n + (s[i] - '0'); return sign * n; } интерфейс #include #include int atoi_ (char[]); int main() { int n = atoi_(" -347 ab"); printf("n = %dn", n); system("PAUSE"); return 0; } Лекция 27. 09. 11 г. 9

Определение функции Для того, чтобы использовать функцию, ее необходимо определить: описать её интерфейс (т. Определение функции Для того, чтобы использовать функцию, ее необходимо определить: описать её интерфейс (т. е. объяснить, как функцией можно воспользоваться) и привести программный код, раскрывающий, как функция работает (т. е. записать реализацию функции на языке программирования). Определение любой функции имеет следующую форму: тип_возвращ_знач имя_функции(список_объявлений_арг) { объявления и операторы } Различные части этого определения могут отсутствовать, но обязательными являются: имя_функции, пара круглых скобок и пара фигурных скобок, т. е. «минимальная» функция определяется так: fun(){} Это – «пустышка» , которая не принимает никаких аргументов и ничего не делает (имеет пустое «тело» ). Подобные функции могут использоваться в качестве «заглушек» при разработке программ. v Если при объявлении функции не указан тип возвращаемого значения, то по умолчанию подразумевается тип int. Лекция 27. 09. 11 г. 10

Функции и программы Ø Любая программа является набором определений o типов, o переменных и Функции и программы Ø Любая программа является набором определений o типов, o переменных и o функций. Ø Функции обмениваются данными посредством передачи аргументов и возвращения значений, а также через внешние переменные. Ø Функции могут следовать друг за другом в файле исходного кода в любом порядке, и текст программы можно разбивать на любое количество файлов, но при этом запрещается разбивать текст функции между файлами. Ø Функция не может быть определена внутри другой функции Ø В результате своей работы функция может возвратить в вызывающую ее функцию результат – некоторое значение, тип которого объявлен перед именем функции. Для этого в теле функции должен присутствовать хотя бы один оператор возврата вида: return выражение; Вызывающая функция может игнорировать (т. е. не использовать) возвращаемое значение. Ø Существует еще одна форма оператора возврата: return; В этом случае в вызывающую функцию ничего не передается, а при определении функции в качестве типа возвращаемого значения указывается void. Ø Тело функции может не содержать оператора возврата return ; при этом возврат из функции происходит при достижении конца блока (закрывающей скобки } ). Лекция 27. 09. 11 г. 11

Функции и программы Ø Все аргументы в функцию передаются по значению, т. е. функция Функции и программы Ø Все аргументы в функцию передаются по значению, т. е. функция получает значения своих аргументов в виде временных переменных, а не оригиналов. Следовательно, все действия функции со своими аргументами никак не отражаются на переменных, переданных функции при вызове. Исправить это положение можно с помощью указателей. Пример: функция, производящая «обмен значениями» двух переменных. #include #include void swap(int a, int b) { int c = a; a = b; b = c; } int main() { int x = 5, y = 7; printf("x = %d, y = %dn", x, y); swap(x, y); printf("x = %d, y = %dn", x, y); system("PAUSE"); return 0; } #include #include void swap(int *a, int *b) { int c = *a; *a = *b; *b = c; } int main() { int x = 5, y = 7; printf("x = %d, y = %dn", x, y); swap(&x, &y); printf("x = %d, y = %dn", x, y); system("PAUSE"); return 0; } Лекция 27. 09. 11 г. 12

Функция main Программа может использовать любое количество функций при одном условии: в любой программе Функция main Программа может использовать любое количество функций при одном условии: в любой программе обязательно должна присутствовать в точности одна функция с именем main ( «главная» функция), т. к. запуск программы на выполнение операционной системой производится всегда через эту функцию (т. е. main начинает выполняться первой). Функция main всегда имеет тип возвращаемого значения int, через это значение ОС уведомляется об успешности или не успешности завершения программы (т. н. «код завершения» ). А т. к. int подразумевается «по умолчанию» , то тип возвращаемого значения для main часто не указывают. Существует две формы main: ü без аргументов – main() и ü с аргументами – main(int argc, char *argv[]) Вторая форма main позволяет при вызове программы из командной строки передать в нее произвольное количество строковых аргументов (об этом позже). Лекция 27. 09. 11 г. 13

Пример программы с модульной структурой Разработать программу решения линейных диофантовых уравнений с двумя неизвестными: Пример программы с модульной структурой Разработать программу решения линейных диофантовых уравнений с двумя неизвестными: ax + by = c , где - a, b, c, x, y – целые числа ; a и b - не нули. К подобному уравнению сводится решение известной олимпиадной задачи: «Предположительно известно, что некоторый ценный предмет (золотой самородок) весит 20 граммов. Требуется убедиться в этом, взвесив его на обычных рычажных весах, но в нашем распоряжении имеются только гири двух типов – на 2 и на 7 граммов, по 5 штук каждого типа. Можно ли в этих условиях произвести взвешивание? Сколько гирь одного и другого типа нужно взять? Сколько существует вариантов взвешивания? » Очевидно, что для получения ответа нужно, как минимум, решить уравнение: 2 x + 7 y = 20 Точнее говоря, найти множество решений … Лекция 27. 09. 11 г. 14

Алгоритм решения диофантова уравнения Алгоритм D. Даны три целых числа: a, b, c (a Алгоритм решения диофантова уравнения Алгоритм D. Даны три целых числа: a, b, c (a и b - не нули). Требуется найти два целых числа x и y таких, что ax + by = c. D 1. [Нахождение наибольшего общего делителя a и b. ] Пользуясь алгоритмом Евклида, найти g - наибольший общий делитель a и b. D 2. [Проверка существования решения. ] Если с % g ≠ 0, то решения не существует и выполнение алгоритма прекращается. DЗ. [Решение вспомогательного уравнения au + bv = g. ] Пользуясь расширенным алгоритмом Евклида, найти u и v. D 4. [Получение результата. ] Результатом решения исходного уравнения будут множества значений: x = u*(c/g) – (b/g)*t, y = v*(c/g) + (a/g)*t, где t=0, ± 1, ± 2, …♦ Алгоритм Евклида был рассмотрен ранее. Рассмотрим расширенный алгоритм Евклида (алгоритм EE). Лекция 27. 09. 11 г. 15

Расширенный алгоритм Евклида Алгоритм EE. Даны два целых числа: a, b (a и b Расширенный алгоритм Евклида Алгоритм EE. Даны два целых числа: a, b (a и b - не нули). Требуется найти два целых числа x и y таких, что ax + by = g (где g - наибольший общий делитель a и b). EE 1. [Установка начальных значений. ] Положить x 1=0, x 2=1, y 1=1, y 2=0. EE 2. [Цикл с предусловием. ] Если b = 0, то решение найдено и перейти к EE 4. EEЗ. [Тело цикла. ] Вычислить: q=a/b, r=a-q*b, a=b, b=r, x=x 2 -q*x 1, y=y 2 -q*y 1, x 2=x 1, x 1=x, y 2=y 1, y 1=y. Перейти к шагу EE 2. EE 4. [Получение результата. ] Положить g=a, x=x 2, y=y 2 ♦ #include #include int main() { int a, b, q, r, x 1 = 0, x 2 = 1, y 1 = 1, y 2 = 0; int a 0, b 0, x, y, g; printf("Input a: "); scanf("%d", &a); a 0 = a; printf("Input b: "); scanf("%d", &b); b 0 = b; while (b) {q = a / b; r=a-q*b; a=b; b=r; x=x 2 -q*x 1; y=y 2 -q*y 1; x 2=x 1; x 1=x; y 2=y 1; y 1=y; } g=a; x=x 2; y=y 2; printf("Result: %d*(%d)+%d*(%d)=%dn", a 0, x, b 0, y, g); system("PAUSE"); return 0; Лекция 27. 09. 11 г. } 16

Создание отдельных модулей Оформим теперь оба алгоритма (E и EE) в виде функций языка Создание отдельных модулей Оформим теперь оба алгоритма (E и EE) в виде функций языка С. Алгоритм Е (модифицированный): int euclid(int a, int b) { while (a && b) if(a>=b) a%=b; else b%=a; return a + b; } Алгоритм ЕЕ: extern int x, y; int e_euclid(int a, int b) { int q, r, x 1 = 0, x 2 = 1, y 1 = 1, y 2 = 0; while (b) { q = a / b; r=a-q*b; a=b; b=r; x=x 2 -q*x 1; y=y 2 -q*y 1; x 2=x 1; x 1=x; y 2=y 1; y 1=y; } x=x 2; y=y 2; return a; } Замечание по поводу использования внешних переменных x и y. Лекция 27. 09. 11 г. 17

Создание отдельных модулей Обе функции: euclid(a, b) и e_euclid(a, b) можно записать в отдельные Создание отдельных модулей Обе функции: euclid(a, b) и e_euclid(a, b) можно записать в отдельные файлы и компилировать независимо. В этом случае основная программа, которая и решает поставленную задачу, также размещается в отдельном файле и уже не содержит определений функций euclid(a, b) и e_euclid(a, b), а только лишь их объявления. Более того, объявления функций, используемых в основной программе, можно поместить в самостоятельный заголовочный файл, который подключается к программе с помощью препроцессорной директивы #include. Лекция 27. 09. 11 г. 18

Основная программа (без заголовочного файла) #include <stdio. h> #include <stdlib. h> #include <math. h> Основная программа (без заголовочного файла) #include #include #include внешние переменные int x, y; int euclid(int, int); объявления функций int e_euclid(int, int); int main() { int a, b, c, g, u, v, t; int x_max, y_max; printf("Input a, b, c: "); scanf("%d%d%d", &a, &b, &c); printf("Input x_max, y_max: "); scanf("%d%d", &x_max, &y_max); if (c % euclid(a, b)) printf("***no solutionn"); else { вызовы функций g = e_euclid(a, b); for(t = -25; t <= 25; t++) { if (fabs(u = x*(c/g) - t*(b/g)) > x_max) continue; if (fabs(v = y*(c/g) + t*(a/g)) > y_max) continue; printf("result: %d*(%d)+%d*(%d)=%dn", a, u, b, v, c); } } system("PAUSE"); return 0; } Лекция 27. 09. 11 г. 19

Об использовании внешних переменных Алгоритм ЕЕ (без внешних переменных): int e_euclid(int a, int b, Об использовании внешних переменных Алгоритм ЕЕ (без внешних переменных): int e_euclid(int a, int b, int *x, int *y) { int q, r, x 1 = 0, x 2 = 1, y 1 = 1, y 2 = 0; while (b) { q = a / b; r = a-q*b; a = b; b = r; *x = x 2 -q*x 1; *y = y 2 -q*y 1; x 2=x 1; x 1=*x; y 2=y 1; y 1=*y; } *x = x 2; *y = y 2; return a; } Лекция 27. 09. 11 г. 20

Модифицированная программа #include <stdio. h> #include <stdlib. h> объявления функций #include <math. h> можно Модифицированная программа #include #include объявления функций #include можно поместить int main() { внутри main int a, b, c, g, u, v, t; int x, y, x_max, y_max; int euclid(int, int), e_euclid(int, int*, int*); printf("Input a, b, c: "); scanf("%d%d%d", &a, &b, &c); printf("Input x_max, y_max: "); scanf("%d%d", &x_max, &y_max); if (c % euclid(a, b)) printf("***no solutionn"); else { вызовы функций g = e_euclid(a, b, &x, &y); for(t = -25; t <= 25; t++) { if (fabs(u = x*(c/g) - t*(b/g)) > x_max) continue; if (fabs(v = y*(c/g) + t*(a/g)) > y_max) continue; printf("result: %d*(%d)+%d*(%d)=%dn", a, u, b, v, c); } } system("PAUSE"); return 0; } Лекция 27. 09. 11 г. 21

Программа с заголовочным файлом #define T 25 #include <stdio. h> int euclid(int, int); #include Программа с заголовочным файлом #define T 25 #include int euclid(int, int); #include int e_euclid(int, int*, int*); #include #include "diophant. h" int main() { int a, b, c, g, u, v, t; int x, y, x_max, y_max; printf("Input a, b, c: "); scanf("%d%d%d", &a, &b, &c); printf("Input x_max, y_max: "); scanf("%d%d", &x_max, &y_max); if (c % euclid(a, b)) printf("***no solutionn"); else { g = e_euclid(a, b, &x, &y); for(t = -T; t <= T; t++) { if (fabs(u = x*(c/g) - t*(b/g)) > x_max) continue; if (fabs(v = y*(c/g) + t*(a/g)) > y_max) continue; printf("result: %d*(%d)+%d*(%d)=%dn", a, u, b, v, c); } } system("PAUSE"); return 0; } Лекция 27. 09. 11 г. 22

Модульная структура программы Итог: спроектирована программа, состоящая из 4 модулей: diophant. h euclid. c Модульная структура программы Итог: спроектирована программа, состоящая из 4 модулей: diophant. h euclid. c diophant. c e_euclid. c Командная строка для компиляции такой программы: gcc euclid. c e_euclid. c diophant. c -o diophant Лекция 27. 09. 11 г. 23

Рекурсия Язык С допускает рекурсивный вызов функций, т. е. функция может вызывать саму себя Рекурсия Язык С допускает рекурсивный вызов функций, т. е. функция может вызывать саму себя прямо или косвенно. Классический пример рекурсии – вычисление факториала целого числа: #include #include int fact(int n) { return n ? n * fact(n-1) : 1; } main() { int n; for(n = 0; n < 10; n++) printf("%2 d! = %10 dn", n, fact(n)); system("PAUSE"); return 0; } Лекция 27. 09. 11 г. 24

Рекурсия Еще один классический пример рекурсии – алгоритм быстрой сортировки, разработанный английским учёным Чарльзом Рекурсия Еще один классический пример рекурсии – алгоритм быстрой сортировки, разработанный английским учёным Чарльзом Энтони Ричардом Хоаром (C. A. R. Hoare) в 1962 г. Этот алгоритм на сегодняшний день является наиболее популярным алгоритмом сортировки. Суть алгоритма: в сортируемом массиве берется произвольный элемент x и все остальные элементы разбиваются на два подмножества: элементы, меньшие x и элементы, большие или равные x. При этом x помещается на такую позицию массива, что слева от него располагаются элементы первого подмножества, а справа – второго. Затем процедура сортировки применяется рекурсивно к каждому из двух подмножеств. Если в подмножестве меньше двух элементов, сортировка ему не требуется и рекурсия на этом прекращается. Лекция 27. 09. 11 г. 25

Рекурсивный алгоритм быстрой сортировки #include <stdio. h> #include <stdlib. h> void swap(int v[], int Рекурсивный алгоритм быстрой сортировки #include #include void swap(int v[], int i, int j) { int temp; temp = v[i]; v[i] = v[j]; v[j] = temp; } void qsort_(int v[], int left, int right) { int i, last; if (left >= right) return; swap(v, left, (left + right)/2); last = left; for (i = left + 1; i <= right; i++) if (v[i] < v[left]) swap(v, ++last, i); swap(v, left, last); qsort_(v, left, last-1); qsort_(v, last+1, right); } main() { int x[] = {8, 2, 6, 5, 4, 3, 7, 1}; int i, n = sizeof(x)/sizeof(x[0]); printf("before: "); for (i = 0; i