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

Лекция-11.ppt

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

Тема Указатели и массивы (продолжение) Лекция 10. 11 г. 1 Тема Указатели и массивы (продолжение) Лекция 10. 11 г. 1

Константные указатели Свойство константности объекта задаётся описателем const и символизирует запрет на выполнение операций Константные указатели Свойство константности объекта задаётся описателем const и символизирует запрет на выполнение операций записи в память. Это свойство можно задать для разных объектов программы: - переменных, - параметров функций, - возвращаемых из функций значений. Контроль за корректным использованием свойства const выполняет компилятор, поэтому ошибки обнаруживаются на ранних стадиях создания программы, что повышает ее надежность. Лекция 10. 11 г. 2

Константные указатели С помощью описателя const можно сделать неизменяемым как указатель, так и область Константные указатели С помощью описателя const можно сделать неизменяемым как указатель, так и область памяти, на которую он ссылается. … main() { int a = 5, b = 7; int *p 1 = &a; *p 1 = 3; // ok! p 1 = &b; // ok! const int *p 2 = &a; //констант. область памяти *p 2 = 9; // err! p 2 = &b; // ok! int *const p 3 = &a; //констант. указатель *p 3 = 9; // ok! p 3 = &b; // err! const int *const p 4 = &a; //и указ. , и обл. пам. конст. *p 4 = 6; // err! p 4 = &b; // err! … } Лекция 10. 11 г. 3

Аргументы командной строки В операционной среде, обеспечивающей поддержку С, имеется возможность передать аргументы (параметры) Аргументы командной строки В операционной среде, обеспечивающей поддержку С, имеется возможность передать аргументы (параметры) запускаемой программе с помощью командной строки. В момент вызова функции main она получает два аргумента. В первом, обычно называемом argc (от argument count – счетчик аргументов), содержится число, равное количеству аргументов, заданных в командной строке. Второй, argv (от argument vector – вектор аргументов), является указателем на массив символьных строк, содержащих сами аргументы, - по одному в строке. #include #include main(int argc, char *argv[]){ while (argc-- > 0) printf("%d: %s ", argc, *argv++); printf("n"); system("PAUSE"); return 0; } Лекция 10. 11 г. argv ● ● D: pptest 30. exe ● qwerty ● asdfg ● 12345 0 char *argv[] = char **argv 4

Аргументы командной строки Следующая программа, которая названа factorial, позволяет вычислить факториалы нескольких чисел, которые Аргументы командной строки Следующая программа, которая названа factorial, позволяет вычислить факториалы нескольких чисел, которые задаются как параметры вызова данной программы. #include #include int fact(int n) { return n ? n * fact(n-1) : 1; } main(int argc, char **argv) { int n; while(--argc > 0) { n = atoi(*++argv); printf("%2 d! = %10 dn", n, fact(n)); } system("PAUSE"); return 0; } Лекция 10. 11 г. 5

Дополнительные сведения о функциях: rvalue и lvalue Если тип возвращаемого значения функции отличен от Дополнительные сведения о функциях: rvalue и lvalue Если тип возвращаемого значения функции отличен от void, то в результате вызова функции получается, как правило, некоторое значение, которое может быть использовано как операнд в выражении. Например, для подсчета среднего значения массива может быть использован следующий фрагмент: double sum(double *x, int n); // прототип функции sum. . . sr = sum(m, 100)/100; В данном примере функция возвращает так называемое rvalue, иначе говоря, значение. Термин rvalue происходит от read value – значение для чтения, или от right value – правое значение – по его месту относительно операции присваивания. Лекция 10. 11 г. 6

Дополнительные сведения о функциях: rvalue и lvalue В некоторых случаях типом возвращаемого значения функции Дополнительные сведения о функциях: rvalue и lvalue В некоторых случаях типом возвращаемого значения функции может быть тип, дающий lvalue, иначе говоря, адрес. Термин lvalue происходит от location value – значение местоположения, или от left value – левое значение – также по его месту относительно оператора присваивания. Следовательно, вызов такой функции может располагаться слева от оператора присваивания. Например: #include #include int *fp(int *p) { return p; } int main(void) { int x = 5; printf("before: x = %dn", x); *fp(&x) = 7; printf("after : x = %dn", x); system("PAUSE"); return 0; } Лекция 10. 11 г. 7

Дополнительные сведения о функциях: rvalue и lvalue Внимание! Необходимо с осторожностью обращаться с lvalue, Дополнительные сведения о функциях: rvalue и lvalue Внимание! Необходимо с осторожностью обращаться с lvalue, возвращаемыми из функций и не допускать, чтобы из функций возвращались адреса несуществующих объектов, например, локальных переменных, объявляемых внутри функции: #include #include int *fe() { int z; return &z; } int main(void) { *fe() = 11; printf("*fe() = %dn", *fe()); system("PAUSE"); return 0; } Лекция 10. 11 г. 8

Указатели на функции В программировании для Windows широко применяется техника функций обратного вызова (callback Указатели на функции В программировании для Windows широко применяется техника функций обратного вызова (callback functions). Функции обратного вызова – это указатели на функции, через которые могут быть вызваны функции. Пример указателя на функцию: #include #include int f(int k) { printf("Call f : k = %6 dn", k); return 100*k; } int f 1(int k) { printf("Call f 1: k = %6 dn", k); return 1000*k; } int main(void) { pf – это указатель на функцию int i = 2; int (*pf)(int); pf = f; printf("1: %6 dn", pf(i++)); pf = f 1; printf("2: %6 dn", pf(i++)); system("PAUSE"); return 0; Лекция 10. 11 г. } 9

Указатели на функции В предыдущем примере был использован указатель на функцию pf как самостоятельная Указатели на функции В предыдущем примере был использован указатель на функцию pf как самостоятельная переменная. Синтаксис языка С допускает объявить указатель на функцию как тип данных, что позволяет использовать динамические переменные и массивы указателей. #include #include D – это тип, т. е. множество int f(int k) { … } указателей на функции int f 1(int k) { … } typedef int (*D)(int); int main() { int i = 2; D p[] = {f, f 1}; D p 1 = f; printf("%d n", (*p)(i++)); printf("%d n", p[1](i++)); printf("%d n", p 1(i++)); system("PAUSE"); return 0; } Лекция 10. 11 г. 10

Сложные объявления Язык С часто обвиняют за чрезмерно сложный синтаксис объявлений, особенно тех, которые Сложные объявления Язык С часто обвиняют за чрезмерно сложный синтаксис объявлений, особенно тех, которые содержат в себе указатели на функции. Например: int *f(); функция f, возвращающая указатель на int (*f)(); указатель на функцию f, возвращающую int Приоритет оператора * ниже, чем приоритет (), поэтому во втором случае скобки необходимы. Ещё пример: int *daytab[13]; массив из 13 указателей на int (*daytab)[13]; указатель на массив из 13 чисел типа int И ещё одно (очень запутанное!) объявление: int (*(*(*fun)())[])(); Его следует понимать так: «fun – это указатель на функцию, возвращающую указатель на массив указателей на функции, возвращающие int» . Рассмотрим общее правило (которое называется правило «право-лево» ), позволяющее разбирать любые «запутанные» объявления. Лекция 10. 11 г. 11

Сложные объявления (правило «право-лево» ) При описании этого правила необходимо помнить следующие 3 операции: Сложные объявления (правило «право-лево» ) При описании этого правила необходимо помнить следующие 3 операции: () - функция, возвращающая. . . [] - массив из. . . * - указатель на. . . Первые две имеют наивысший приоритет, а третья – более низкий. Процесс разбора объявления итеративный: Шаг 1. Находим имя, с которого начинается разбор, и записываем начало предложения «Имя есть. . . » . Шаг 2. Начинаем движение вправо для поиска () или []. Если справа оказывается (), то в конструируемое предложение вместо многоточия подставляем: «функция, возвращающая. . . » и в итоге получаем: «Имя есть функция, возвращающая. . . » . Если справа [], то подставляем «массив из…» и получаем «Имя есть массив из. . . » . Таким образом мы идём вправо до тех пор, пока не дойдём до конца объявления или правой ) скобки. Шаг 3. После этого начинаем перемещаться влево. Если слева обнаруживается что-то отличное от упомянутых выше операций (то есть не (), [], *), то просто добавляем к уже имеющемуся предложению. Если же там что-то из этих трёх операций, то к предложению добавляем соответствующий текст. И так перемещаемся до начала объявления или левой ( скобки. Если обнаружена (, то переходим к шагу 2. Если дошли до начала объявления, разбор закончен. Лекция 10. 11 г. 12

Сложные объявления (правило «право-лево» ) Рассмотрим сложный пример: int (*(*(*fun)())[])(); ^^^ Hаходим имя и Сложные объявления (правило «право-лево» ) Рассмотрим сложный пример: int (*(*(*fun)())[])(); ^^^ Hаходим имя и записываем: «fun есть. . . » . Шаг вправо, но там ), и потому идём влево int (*(*(*fun)())[])(); ^ и получаем «fun есть указатель на. . . » . Продолжаем двигаться влево, но тут (. Идём вправо int (*(*(*fun)())[])(); ^^ получаем «fun есть указатель на функцию, возвращающую. . . » . Шаг вправо, но там ), и потому идём влево. Получаем int (*(*(*fun)())[])(); ^ «fun есть указатель на функцию, возвращающую указатель на. . . » . Слева опять (, поэтому идём вправо. Получаем int (*(*(*fun)())[])(); ^^ «fun есть указатель на функцию, возвращающую указатель на массив. . . » . Лекция 10. 11 г. 13

Сложные объявления (правило «право-лево» ) И снова справа ), поэтому перемещаемся влево и получаем Сложные объявления (правило «право-лево» ) И снова справа ), поэтому перемещаемся влево и получаем int (*(*(*fun)())[])(); ^ «fun есть указатель на функцию, возвращающую указатель на массив указателей на. . . » . Снова разворот вправо, т. к. обнаруживается ( , и получаем int (*(*(*fun)())[])(); ^^ «fun есть указатель на функцию, возвращающую указатель на массив указателей на функции, возвращающие. . . » . Обнаруживаем конец описания, переключаемся на движение влево и получаем окончательную расшифровку этого очень сложного и запутанного описания: int (*(*(*fun)())[])(); ^^^ «fun есть указатель на функцию, возвращающую указатель на массив указателей на функции, возвращающие int» . Лекция 10. 11 г. 14

Тема Структуры Лекция 10. 11 г. 15 Тема Структуры Лекция 10. 11 г. 15

Основные понятия Структура – это набор нескольких именованных переменных, объединённых в единое целое под Основные понятия Структура – это набор нескольких именованных переменных, объединённых в единое целое под общим именем. Входящие в структуру переменные называются элементами, полями или членами структуры. Члены структуры имеют произвольные типы и уникальные в пределах структуры имена. Доступ к членам структуры производится по составному имени, включающему имя структуры и имя члена структуры. Пример: struct {int x; int y; } pt 1, pt 2; pt 1. x = 3; pt 1. y = 6; pt 2 = pt 1; Синтаксис языка С позволяет отделить описание «внутреннего устройства» структуры от определения конкретных переменных, имеющих это внутреннее устройство: struct point {int x; int y; }; struct point pt 1, pt 2; Такой подход позволяет рассматривать первое объявление как новый тип данных, а второе – как определение переменных этого типа. Замечание. Допускается совмещать объявление типа и объявление переменных: struct point {int x; int y; } pt 1, pt 2; Лекция 10. 11 г. 16

Основные понятия Элементы структуры могут иметь произвольный тип, в том числе, они сами могут Основные понятия Элементы структуры могут иметь произвольный тип, в том числе, они сами могут быть структурами. #include #include struct point { int x, y; }; struct rect { struct point pt 1, pt 2; }; int pt_in_rect(struct point p, struct rect r) { if(p. x < r. pt 1. x || p. x > r. pt 2. x) return 0; if(p. y < r. pt 1. y || p. y > r. pt 2. y) return 0; return 1; } int main() { struct point z = {2, 2}; struct rect rc = {{1, 1}, {4, 5}}; printf("%sn", pt_in_rect(z, rc) ? "in" : "out"); system("PAUSE"); return 0; } Лекция 10. 11 г. 17