Будь умным!


У вас вопросы?
У нас ответы:) SamZan.ru

Конспект лекций Теория и технологии программирования

Работа добавлена на сайт samzan.ru: 2015-07-05


Хлопин Сергей Владимирович

Конспект лекций «Теория и технологии программирования. Процедурное программирование»

Санкт-Петербург

2011 


Оглавление.

Этапы создания программного продукта. 5

Структурирование программы. 6

Этапы получения программного кода 8

Функциональная декомпозиция 8

Алгоритм программы 9

Функция main 16

Блоки кода 17

Комментарии 17

Базовые понятия языка Си. 18

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

Ключевые слова Си 20

Типы данных. 22

Функции 23

Блоки кода программы. 24

Операторы языка C. 25

Базовые типы данных. 29

Файловый ввод - вывод. 30

Понятие, объявление и определение. 31

Ключевое слово typedef. 32

Виды компоновки программ: 32

Область видимости. 33

Пространство имён. 34

Оператор разрешения области видимости. 36

Пространство имён – namespace. 36

Инструкции. 37

Инструкции условий. 38

Инструкции цикла. 39

Инструкции безусловного перехода. 40

Препроцессор. Заголовочные файлы. Директивы препроцессора. 41

Указатели. 45

Арифметика указателей. 46

Связь массивов и указателей. 50

Двумерные массивы. 51

Трёхмерные массивы. 52

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

Динамическое распределение памяти. 53

Управление памятью. Оператор new и delete. 53

Оператор new и массивы. 54

Многомерные массивы. 55

Ссылки. 56

Функции. 58

Определение функции (реализация). 60

Вызов inline функции. 61

Указатели на массивы в качестве параметров функции. 65

Значения аргументов функций по умолчанию. 65

Переменное число параметров функции. 67

Функции стандартной библиотеки 68

Виды возвращаемых значений и механизмы их формирования. 69

Проблемы при возвращении ссылки или указателя. 70

Ключевое слово const. 71

“Перегрузка” имён функции. 71

Возможный конфликт при использовании параметров по умолчанию. 73

Рекурсивные функции. 73

Структуры Си. 74

Структуры и функции. 77


Этапы создания программного продукта.

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

Постановка задачи

Документирование

Проектирование

Оценка риска

Верификация

Кодирование

Тестирование

Уточнения

Производство

Сопровождение

Рис. 1. Этапы создания программного продукта.

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

Структурирование программы.

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

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

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

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

Задача

Подзадача 2

Подзадача 1

Подзадача 3

Подзадача 1.1

Подзадача 1.2

Рис. 2. Декомпозиция задачи на подзадачи.

 

Программа

Файл 1

Файл 2

Файл 3

Функция 1

Функция 3

Функция 2

Блок 3

Блок 2

Блок 1

 

Рис.  3. Файловая декомпозиция.

Разбиение на файлы позволяет:

  1.  лучше ориентироваться в тексте программы;
  2.  уменьшить общее количество времени создания нового загрузочного модуля компилятором.

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

Этапы получения программного кода

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

  1.  Формирование исходного кода в текстовом редакторе.
  2.  Предварительная обработка текста процессором.
  3.  Компиляция объектного файла.
  4.  Компоновка и сборка исполняемого файла.
  5.  Отладка (добавляется отладочная информация).

Функциональная декомпозиция

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

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

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

Пример функциональной декомпозиции изображен на следующей схеме:

main ( )

{…

вызов f1 ( )

вызов f4 ( )

}

f1 ( )

{…

вызов f2 ( )

вызов f3 ( )

}

f4 ( )

{

}

f2 ( )

{

}

f3 ( )

{

}

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

Алгоритм программы

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

Ниже приведены графические обозначения (обозначения на блок-схемах) ОАС.


Структура “следование”


Полная развилка


Неполная развилка


Цикл с предусловие (цикл ПОКА)


Цикл с постусловием (цикл ДО)


Цикл с параметром

На схемах СЕРИЯ обозначает один или несколько любых операторов; УСЛОВИЕ есть логическое выражение (ЛВ) (если его значение ИСТИНА, переход происходит по ветви ДА, иначе — по НЕТ). На схеме цикла с параметром использованы обозначения: ПЦ — параметр цикла, НЗ — начальное значение параметра цикла, КЗ — конечное значение параметра цикла, Ш — шаг изменения параметра цикла.

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

В примерах мы будем использовать запись алгоритмов с помощью блок-схем и словесное описание.

Линейные алгоритмы

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

Пример 1. Пешеход шел по пересеченной местности. Его скорость движения по равнине v1 км/ч, в гору — v2 км/ч и под гору — v3 км/ч. Время движения соответственно t1, t2 и t3 ч. Какой путь прошел пешеход?

1. Ввести v1, v2, v3, t1, t2, t3.

2. S1 := v1 * t1.

3. S2 := v2 * t2.

4. S3 := v3 * t3.

5. S := S1 + S2 + S3.

6. Вывести значение S.

7. Конец.

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

Пример 2. Дано натуральное трехзначное число n, в записи которого нет нулей. Составить алгоритм, который возвращает значение ИСТИНА, если верно утверждение: "число n кратно каждой своей цифре", и ЛОЖЬ — в противном случае.

1. Ввести число n

2. A := n mod 10 {разряд единиц}

3. B := n div 100 {разряд сотен}

4. C := n div 10 mod 10 {десятки}

5. L := (n mod A=0) and (n mod B=0) and (n mod C=0)

6. Вывод L

7. Конец

На приведенной выше схеме DIV и MOD соответственно операции деления нацело и получения остатка от целочисленного деления. В фигурных скобках записаны пояснения (комментарии) к операторам.

Развилка

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

Пример 1. Вычислить значение функции

1. Ввести x.

2. Если x£–12, то y:=–x2

3. Если x<0, то y:=x4

4. y := x–2

5. Вывести y

6. Конец

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

Пример 2. Дано натуральное число n. Если число нечётное и его удвоение не приведет к выходу за 32767 (двухбайтовое целое число со знаком), удвоить его, иначе — оставить без изменения.

Чтобы удовлетворить условию удвоения, число n должно быть нечетным и меньше 16384.

1. Ввести число n

2. Если число n нечетное и меньше 16384, то n := n * 2

3. Вывод n

4. Конец

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

Циклы

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

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

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

1. Ввести число n

2. K := 0 {подготавливаем счётчик}

3. Если n = 0, переход к п. 7

4. Если n mod 10 mod 2 = 1, то K := K +1

5. n := n div 10

6. Переход к п. 3

7. Вывод K

8. Конец

Задача решена двумя способами. Слева решение оформлено с использованием цикла с предусловием, справа — с постусловием.

Пример 2. Дана последовательность, общий член которой определяется формулой

Вычислить при n>2 сумму тех ее членов, которые больше заданного числа e.

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

1. Ввести e

2. S := 0

3. A := 1/4

4. n := 3

5. Сравнить А с e. Если A>=e, переход к п. 10

6. S := S + A

7. A := (n-1)/(n*n)

8. n := n + 1

9. Переход к п. 5

10. Вывод S

11. Конец

В рассмотренных выше примерах количество повторений заранее неизвестно. В первом оно зависит от количества цифр в записи натурального числа, во втором — от числа e.

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

Пример 3. Найти произведение первых k натуральных чисел, кратных трём.

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

1. Ввод k

2. P := 1 {здесь накапливаем произведение}

3. T := 0 {здесь будут числа, кратные 3}

4. I := 1

5. Если I > k, переход к п. 10

6. T := T + 3

7. P := P * T

8. I := I + 1

9. Перейти к п. 5

10. Вывод P

11. Конец

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

Функция main

main - основная функция языка си. Для языка си характерна следующуя специфика:

  1.  Каждая программа на С обязательно имеет функцию с именем main
  2.  Имя main может быть только у одной функции
  3.  Тело программы заключено в лексеммы: «{» - начало программы, «}» – выход из программы (корректный)

Для различных версий компиляторов языка си могут быть применены различные виды главной функции main:

  1.  main – С, С++
  2.  wmain и tmain – Visual Studio (с поддержкой Unicode)
  3.  WinMain –Windows программирование

В языке С имеет различие написание маленьких и заглавных букв

Завершение программы происходит в случаях:

  1.  достижения окончания функции main – лексема «}»;
  2.  по выполнению return – инструкции из main;
  3.  вызов функции exit( ) из любой функции программы;
  4.  вызов функции abort( ) из любой функции программы.

при этом в случаях 1, 2 и 3 – имеется корректный выход, 4 – некорректный выход из программы.

Блоки кода

Блок кода – несколько строк, заключённые в фигурные скобки.

Если внутри блока кода содержится другой блок кода, то он называется составной инструкцией.

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

Комментарии

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

  1.  // комментированный текст - простой комментарий ( действует на 1 строку)
  2.  /* комментированный текст */ комментарий действует на весь блок заключенный между набором символов "/*" и "*/".

Рекомендации:

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

Базовые понятия языка Си.

Ключевые слова Cи - слова, зарезервированные только для использования компилятором языка Си.

примеры:

#include
#define
if, for, while, int, float, char…

Идентификаторы – используются для присвоения имён объектам.

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

  1.  Идентификатор С++ состоит из последовательности букв и цифр. [a-z], [A-Z], [0-9], _.
  2.   Первый символ должен быть буквой.
  3.   _ - подчёркивание равно букве, но имена, начинающиеся с “_”, обычно зарезервированы для специфических нужд среды или использования стандартных библиотек.
  4.  Компилятор различает символы в верхнем и нижнем регистре. Например, fool и FOOL – разные переменные.
  5.  Для некоторых компиляторов есть ограничения для количества символов в  имени ( С – 31 символ) . В количестве имён нельзя использовать ключевые слова С.
  6.  В качестве имён нельзя использовать ключевые слова С
  7.  Чем шире область использования, тем осмысленнее должны быть имена
  8.  Согласно Венгерской нотации рекомендуется в имени указывать тип переменной

примеры:

Примеры правильных имён

Примеры неправильных имён

hello

Hello

this_is_very_long_var

_u_name

a var1

my.var1

char

$my

_TYPE

Ключевые слова Си

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

Список ключевых слов:

auto, double, int, struct, break, else, long, switch, case, enum, register, typedef, char, extern, return union, const, float, short, unsigned, continue, for, signed, void, default, goto, sizeof, while, do, if, static, volatile.

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

Понятие lvalue и rvalue.

Для того, чтобы отличать выражения, обозначающие объекты, от выражений, обозначающих только значения были введены понятия lvalue и rvalue. Определение lvalue использовается для обозначения выражений, которые могли стоять слева от знака присваивания (left-value); им противопоставлялись выражения, которые могли находиться только справа от знака присваивания (right-value).

Характерные случаи использования lvalue и rvalue:

rvalue:

  1.  Выражения, обозначающие временные объекты. В частности, результат вызова функций, возвращающих объекты не по ссылке; результат встроенных операций +, -. *, / и т.п.; явное создание временной переменной int() или C(); преобразования не к ссылочным типам и т.д.
  2.  Результат встроенной операции взятия адреса (&) — rvalue типа указатель.
  3.  Результат встроенных постфиксных операций ++, --.
  4.  Литералы за исключением строковых.
  5.  Константы перечислений.

lvalue:

  1.  Выражения, непосредственно обозначающие объект, non-modifiable в случае const-квалификации. Например, имя переменной, параметра функции и т.п.
  2.  Выражения ссылочных типов. non-modifiable в случае const-квалификации. В частности, результат вызова функций, возвращающих объекты по ссылке; выражения, состоящие из имен ссылочных переменных; операции преобразования к ссылочному типу и т.д.
  3.  Результат встроенной операции разыменования (*) — lvalue указуемого типа; non-modifiable в случае const-квалификации.
  4.  Результат встроенных префиксных операций ++, --.
  5.  Имя функции — non-modifiable lvalue; может быть преобразовано к rvalue «указатель на функцию».
  6.  Имя массива — non-modifiable lvalue; может быть преобразовано к rvalue «указатель на первый элемент массива».
  7.  Строковые литералы — non-modifiable lvalue; может быть преобразовано к rvalue «указатель на char/wchar_t».

Типы данных.

Типы данных. В языке Си имеются два существенно различных типа данных: int- целый и float - вещественный(с плавающей точкой). Из них можно создавать еще два типа: char - символьный , double - вещественный с двойной точности.

Следует различать тип данных и модификатор типа.

Имеются следующие базовые типы:

  1.  • char (символьный) – занимает 1 байт (8 бит) памяти
  2.  • int (целый) – 2 байта
  3.  • float (вещественный) – 4 байта
  4.  • double (вещественный с двойной точностью) – 8 байт
  5.  • void (пустой тип)

К модификаторам относятся:

  1.  • unsigned (беззнаковый)
  2.  • signed (знаковый)
  3.  • short (короткий)
  4.  • long (длинный)

Тип данных и модификатор типа определяют:

  1.  - формат хранения данных в оперативной памяти (внутреннее представление данных)
  2.  - диапазон значений в пределах которого может изменяться переменная
  3.  - операции которые могут выполняться над данными соответствующего типа

Итак, к данным целого типа относятся следующие типы:

char, int, long int (2 байта), short int (4 байта). Модификаторы signed и unsigned могут предшествовать любому целому типу, но они не обязательны. Они указывают, как интерпретируется старший бит переменной. По умолчанию все переменные целого типа считаются signed, т.е. левый бит интерпретируется как знак.

К вещественным типам данных (с плавающей точкой) относятся:

float, double, long double (10 байт).

Функции

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

Функции могут возвращать значение. Это значение может быть использовано далее в программе.

Использование функций позволяет:

  1.  улучшить структуру текста программы;
  2.  избежать дублирования кода.

Существует два типа функций:

  1.  прямые (выполняют ряд действий и передают управление вызывающей функции);
  2.  рекурсивные (многократно вызывают "сами себя").

Пример рекурсивной функции:

int Factorial (int a)
{
    if(a==a)
   {
        return 1;
   }
    else
    {
         int fact a=a factorial(a-1);
         return fact;
    }
}


 Блоки кода программы.

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

{

Блок кода

}

Могут содержать в себе вложенные Блоки кода.

{

Блок кода

{

Блок кода 1

}

}

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

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

Операторы языка C. 

Арифметические операторы +   -  *  …

1.1) % -  остаток от целочисленного деления
int x=1, y=2, z;
z=x/y;  //z=0
z=x%y;  //z=1
z=3%5;  //z=2

1.2)Инкремент – увеличение числа на единицу.
Декремент – уменьшение числа на единицу.

x=1;
y=x++;    (y=x+1)
y++;   (y=y+1)

префиксная запись   y=x++
постфиксная запись  y=++x

  1.  Оператор присваивания
    x=выражение
    a=b=c=d
    a=(b=(c=d))
  2.  Логические операторы

true (!=0)
false  (0)

3.1) Операторы отношения
< , <= , >=, >, = =, !=
3.2) Логические операторы
!,   && ,    //

   ( и )    ( или)
if(year>14&&year<18)
if(t>36.7|| t<36 || p>140)

  1.  Тернарный оператор

Тернарный оператор «?:» является сокращенной формой конструкции if…else. Он получил такое имя потому, что включает в себя три операнда. Оператор вычисляет условие и возвращает одно значение в случае, если условие верно, и другое значение, если условие неверно. Синтаксис оператора:

условие ? значение_истина : значение_ложь

Здесь условие — это выражение типа Boolean, значение_истина представляет собой значение, которое возвращается, если условие равно true, и значение_ложь возвращается в противном случае.

  1.  Оператор запятая “ , ”
    Группирует несколько выражений там, где компилятор ожидает одно.


1) int a=1, b=2, c;
c=a,b;   //
с=1
c=(a,b);  //c=2
c=a++,b++ ;  //c=1, a=2, b=3


2) if(std::cin>>x, x>0)  
//вычисляет все, но формируется последняя

char symbol;
symbol=‘A’;
printf(“%c”, symbol);
char symb 1[10];
symb 1[0]=’s’;
symb 1[1]=’t’;
symb 1[2]=’u’;
symb 1[3]=’d’;
………………

Данные

Данные

Константы

Переменные

Логические данные

Символы (строки)

Числа

Плавающая (.)

Целые

Данные могут быть:

  1.  целые числа;
  2.  числа с плавающей точкой(арифметические типы данных). Служат для выполнения вычислений;

    int float double unsigned short long

3) символы и строки;

char     char[]

4) логические;

bool

Символьные данные (литералы) – символ, пара символов или три символа,          заключенные в кавычки.

ASCII (7-и битная кодировка)
0-127 символов
KOI-8
UNICODE   65535 символов

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

const char[]

                                                     Перечисление.
enum
my_action =0  
//плюс
                 =1  
//минус
                 =2  
//умножить
                 =3  
//делить

1)enum Action

{
   plus,  //0
   minus,  //1
   multiply,  //2
   division,  //3
};
my_action = plus,  //
автоматически присваивает значение

2)enum Action
  {
      plus=5,
      minus=3,
      multiply=2,
      division=1,
  };


3)enum Action
  {
       plus=5,  //5
       minus,  //6
       multiply, //7
       division,  //8
  };

   int m=7;
   if(m>minus)
  {
        true
  }

Базовые типы данных.

Типы данных

           базовые                                                                       производные

1)Базовые

целые
bool                                                                                                    
void                                                                                                    
плавающие                                                                                        

char (1)
short(2)
long(4)

int (системно зависим)
_int n
(n=8,16,32,64)
long long (8)

Плавающая точка:
float (4)
double (8)
long double (8)

2)Производные

  1.  простые  -  указатель, ссылка
  2.  составные  -  класс(class),  структура(structure), объединение(union), массив(array)
  3.  sizeof(_)

    либо выражение, либо тип данных
    sizeof(char) = =1
    double d;
    sizeof(d) //= =8

y=300*sin(x)

        int                 double

 Неявное приведение типов производит компилятор автоматически по правилу:
”Полученное в результате вычисления выражение приводится к типу слева от знака равенства.”.

Явное приведение типов.
y=300*(int)sin(x);   //неверно!!!


y=(int)(300*sin(x));

float var1;
cin>>var1;
if(var1 !=
(int)var1)                         проверка на тип данных
{
 
// не целое
}

Файловый ввод - вывод. 

  1.  infile = fopen(“имя”, w);
    “Z:\\...\\myfile.txt”;

  1.  myfile.txt
    *.vcproj
    *sln
    ..\\ на уровень выше.

Булевые переменные (тип BOOL).

True =1                                                                                                                                False=0
ex. :
bool b=(x<=y);
//b=5 – ошибка
int n=b;
(неявное приведение к типу int)

Старый BOOL – 4 бита
Сейчас BOOL – 1 бит

Понятие, объявление и определение.

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

Declaration
Объявлений одного имени может быть сколько угодно, но они должны быть согласованы.
Определение может быть только одно.

extern int iNumber;
float var2;
 //объявление + определение
double var3 = 3.14159,
Один тип объявляется через ‘ , ’ :
char var1, var2,var3, var4 = ’ y ’;

Ключевое слово typedef. 

typedef    тип    синоним типа,

typedef  unsigned char  BYTE;
BYTE var8;
#if defined(_win32)
typedef int int32;
#else
typedef long int 32;
#endif
int32  i=1    -   32
разряда памяти

Нельзя комбинировать имена, созданные с помощью typedef.

//typedef long int 32 MY; - НЕЛЬЗЯ
for (int index…)
{
   index
}

Виды компоновки программ:

1) Внешняя
external
(все переменные определены вне {} без спецификатора static)
int var1,
int main
{}

2) Внутренняя
internal
(объявление вне {},  но с ключевым словом static )

3) Без компоновки
no linkage
(
не подлежат компоновке)
{int var1;
}

file.cpp
int z;
static int y;
void f1()
{
   int x;
   static int;
}
void f2()
{
   использование y
}
void f3()
{
   использование y, z
}

    

file2.cpp
extern int z;
void f4();
{
   
использование z;
}

   Автоматическая память – все переменные, которые были определены внутри блока, или параметры функции.

Область видимости.

{
   …
   int i;
}
//i=1

 File scope – область видимости для переменной не {} будет видна только в этом файле

#include…
int z;
void main()
{
   z=10;        
   

}


Область видимости переменной.

--- namespace;
---function scope;
---void f1(void)
{
   label1:
}
   void f2(void)
{
   label1:
}
---class scope
---
область видимости имён параметров прототипа функции.

Скрытие имени переменной.

int ix; //глобальная

void Func ()

{

   int ix; //локальная

   ix=2;

 {

  int ix; //локальная

  ix=3;

}

}


Оператор разрешения области видимости.

 К скрытому глобальному имени можно обратиться  с помощью оператора разрешения области видимости “ :: ”

int ix; // глобальная

void Func ()

{

    ix=1;

int ix;

:: ix=50;

обращение к глобальной переменной

 ix=4;

}

Void F1 ()

{

ix=20;

}    

Пространство имён – namespace.

namespace one { int version=1;}

namespace two { int version=2;}

int main ()

{

int n=one::version; //1

int n1=two:: version; //2

}

Инструкции.

 Инструкции – идут по порядку, содержат имена, разделители.

       инструкция

                             имя                    оператор            разделители     

                       (частей   инструкции)

          TAB          пробел

Типы инстр.

Для чего она

Примечание

составная инструкция

группа интср.  

{} может не содержать ни

 

( заключ. В {})

одной инструкции

инструкция объявления

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

Вводит новое имя в новые

 

(нового типа)

области видимости

Последова-тельность операторов,

может содержать арифм.

инструкция выражения 

операндов, действ.  

выражения, логич. выраж.,

 

над ними

вызов ф-ии

пустая инструкция

;

 

инструкция выбора

if, else, switch

 

инструкция цикла

do, while, for

while (усл.) инструкция

инструкция безусловного

continue, go to, return,

 

перехода

break

 


Инструкции условий.

If, if – else

if (условие) {true инструкция}

 [ else {false инструкция}] – не обязательно

If (x!=0) … if (x)

Переключатель switch

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

{

case конст. _1:

…//

break;

case  конст. _2:

…//

break;

default:

}

Существенно:

1) В скобках switch находится выражение, которое в итоге сводится к целому типу (switch переводит к целому типу).

2) Точки, куда передаётся управление, помечены ключевым словом case. Каждой такой метке сопоставляется константа (типа int) для сравнения со значением,  вычисленным в скобках.

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

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

{

case 5;

cout<<5;

case 4;

cout<<4;

}

Выражение default будет выполняться, если не совпало ни одно условие.

switch (выр-е)

{

case 5;

{

 int iX;

 }

 break;

}

Инструкции цикла.

While.

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

while (условие продолжения цикла) {операторы}

Замечание: С помощью while очень легко создать бесконечный цикл.

while (int i=1) {}

 break – выход из бесконечных цикла

if(getch()=’_’) break;}

do… while

do тело цикла while (условие продолжения)

for

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

for ([инициализирующая инструкция];[выражение 1, условие]; [выражение 2])

{ тело цикла;}

for (index=0, index <10, index ++)

 Условие продолжения цикла с оператором for вычисляется на каждой итерации, поэтому не стоит компилировать вычисление сложения выражения.

Лучше  выражение  вычислять один раз до начала цикла.

for (… ; r<x*y*z; r++) => for (…,r<R; r++)

{}

В любой части цикла могут быть несколько выражений, они должны быть разделены “,”, а не “;”.

for (i=0, j=0; i<100; i++, j++)

Тело цикла может быть пустым.

Инструкции безусловного перехода. 

break, continue, go to , return.

 Break –прерывает выполнение switch, while, for и передаёт управление на инструкцию, следующую за ними.

Если имеет место вложенность, то прерывается самая внутренняя по отношению к break инструкция.

while ()

{

 continue

 while ()

 {

 break;

 }

}

continue – прерывает выполнение while и for и переходит к следующей итерации.

return - прерывает выполнение текущей функции и возвращает управление вызывающей функции.

go to – осуществляет безусловный переход, но только в пределах одной функции main function

Препроцессор. Заголовочные файлы. Директивы препроцессора.

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

Основная цель препроцессора - закончить форматирование исходного текста программы на С++.

Затем окончательный текст подвергается компиляции.

Замечание:

  1.   Директива препроцессора пишется каждая на определённой строке.
  2.   Большинство директив препроцессора можно использовать в любом месте программы, при этом их действия распространяются от того места, где они используются до конца файла.

Список директив препроцессора (все начинаются с #).

# include –включает в исходный текст программы текстовый файл (работает только с исходным кодом)

> - включение происходит из стандартной библиотеки текстовых файлов

“ ” – файл сначала используется в исходном каталоге, а затем в стандартной библиотеке

# define – задаёт макроподстановки и определяет имена для компилятора

# undef – отменяет #define

# if
# elif  

# endif      

# ifdef           

# ifndef                                    

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


# pragma- позволяет настраивать компилятор с учётом специфических особенностей компьютера или ОС.


# error – включается между #if и #endif для проверки какого-либо условия на этапе компиляции. При выполнении такого условия компилятор выдаёт сообщение, указанное в #error, и восстанавливается.

Лучше избегать использование директив препроцессора.

1.  #define - задаёт макроподстановку.

-идентификатор_макро

-тело_макро

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

 #define First 1

{

 main ()

 If (n== First) // if (n==1)

}

#define False 0

#define True ! False

 Можно также работать с функциями:

 #define SQUARE(x) x*x

main()

 int z=2;

  y=SQUARE(z+1); //y примет значение 9

#define

Если тело_макро пустое, то идентификатор_макро будет заменён на пробел.

Предопределённые идентификаторы.

_ _cplusplus - исходный текст должен компилироваться как C++, а не C

_ _ DATE_ _ - во время компиляции преобразовывается в строку, заключая в кавычки, содержащую дату компиляции.

_ _FILE_ _ - вместо FILE подставляется строчка с именем файла, с учётом полного пути

_ _LINE_ _ - содержит в себе номер текущей строки

_ _TIME_ _ - время последней компиляции

cout << “Error in”<<_ _FILE_ _ #define

<<”str”<<_ _LINE_ _<<

<<”cur time”<<_TIME_;

Диагностический макрос (микрокоманда) assert.

Макрос assert имеет вид: assert (выражение)

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

assert (x<0)

assertion failed: x<0; file…;

line…….;

2. #undefотмена использования #define

 #define MESSAGE “hello”

 cout<<MESSAGE;

#undef MESSAGE

 cout<<MESSAGE; // выдаст ошибку

#ifdef MESSAGE

#else

#define MESSAGE “hello”

#endif

 #if defined(_Win32)

 typedef ind int 32;

#else

 typedef long int 32;

#endif

 int 32 m=1; // 32 разряда

3. #include – получаем доступ к стандартной библиотеке

#include

std lib         user files

             general.h               general.cpp                   func1.cpp                   func2.cpp

#include “general.h”  #include “general.cpp”  #include “func1.cpp”   #include “func2.cpp”

Указатели.

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

Если переменная содержит адрес некоторого другого объекта, то говорят, что переменная указывает на этот объект.

Виды указателей

на объект           на функции        

  -указатели

  -базовые типы

  -на массивы

  -struct, union, class…

int * pn; //объявим указатель на тип данных int

int *pn;        то же

int* pn;        самое

Замечание:

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

int a;

int b=10;

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

int *p; //глобальная

{

int * p1; //локальная

static int * p2; //статическая 

}

Инициализация указателя и оператор получения адреса объекта.

& - амперсант

Чтобы провести явную инициализацию указателя:

int n=1;

int *pn=&n;

int a[100];

int *pa=&a;

char *pstr=”string”;

char *pstr=&”string”;

Арифметика указателей.

int * p2;

p2=pn;

pn++;

pn=pn+sizeof(int);

p2=pn+5;

p2=pn+5*sizeof(int)

if (pn==p2)

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

void *;

void * pvoid;

int n;

char c;

int * pn;

pvoid=&h; // OK

pvoid=&c; //OK

pvoid=pn; //OK

reinterpret_cast

double d=99.99;

int n=d; //d=99

int n=0x12345;

char c=n;

int *pn=&n;

char *pc=reinterpret_cast

 <char*> (&n);

Массивы.

Свойства массивов:

  1.  В памяти расположена совокупность упорядоченных элементов данных (последовательно).
  2.  Данные должны быть одного и того же типа.
  3.  Данные занимают непрерывно линейную область памяти.
  4.  Каждый массив имеет имя (оно является константным указателем, равным адресу начала массива, нулевого байта, нулевого элемента массива). Тип такого указателя зависит от размерности массива.
  5.  Доступ к отдельным элементам массива может осуществляться по имени массива и индексу (порядковый №) элемента или с помощью оператора <<*>> разыменовывания.

Объявление встроенного массива.

  1.  без extern

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

  char cAr[10];

  int iAr[15];

  float fAr[5][3];

  float f1Ar[3][3][8];

 2. с extern

   При объявлении массива производим описание свойств внешнего массива, определённого в другом файле.

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

  extern in tar[10][5];

  const int N=10;

  char cAr[N]; //OK!

  int N1=5;

  char cAr1[N1]; //ошибка

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

int ar1[10][5];

main ()

{

static char ar2[100];

 float ar3[3][4][5];

}

Явная инициализация.

int ar4[3]={1,2,3};

char cAr[2]={‘A;B’};

char cAr1[2]={“AB”};

int nAr[2][2]={{1;1},{2;2}};

char cstring[2][80]={“Первая строка”,”Вторая строка”};

80

 

2

п

е

р

в

а

я

_

с

т

р

о

к

а

в

т

о

р

а

я

_

с

т

р

о

к

а

Можно объявить одномерный массив без указания числа элементов.

int nAr[][3]={{1,2,3},{2,3,4},…{7,5,8}}   //не указано количество строк

Обращение к элементу массива. Оператор [].

Доступ к отдельным элементам массива может выполняться с помощью индексов (они нумеруются с 0, а не с 1). Индекс может быть любым целым выражением.

char Ar[10];

char c=Ar[0];

c=Ar[g];

c=Ar[i];         //i-int

c=Ar[i+g-k];    // c=Ar[3.48];  ERROR!!

float b;

c=Ar[(int)b];     //приводит к типу int (может быть потеря данных)

size of – количество  памяти, которое занимает массив

char ar[]=”abc”;

size_t n=sizeof(ar)/sizeof(char)

n=sizeof(ar)/sizeof(ar[0]);

int ar[][3]={1,2,3,4,5,6,7};

size_t n=sizeof(ar)/sizeof(int);

n=sizeof(ar[0])/sizeof(ar[0][0])      //подсчёт элементов в строке

n=sizeof(ar)/sizeof(ar[0]);     //подсчёт числа строк в массиве

ar[]={2,4,5,8};

n=sizeof(ar)/sizeof(ar[0]);

Связь массивов и указателей.

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

ar[0]=1

ar[1]=2

ar[2]=3

ar[3]=4

ar[4]=5

ar

p

В памяти:

int *p=ar;

int *int [];

int tmp=ar[i];

  равнозначно

tmp=p[i];

tmp=*(p+i);

tmp=*(ar+i);

переместим указатель к следующему элементу массива, но не ar++(ошибка!)

p++;     //ar++i

Двумерные массивы.

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

int ar[2][3]={{1;2;3},{4;5;6}};

ar[0][0]=1

ar[0][1]=2

ar[0][2]=3

ar[1][0]=4

ar[1][1]=5

ar[1][2]=6

int ar[N][M];

int sum!=0;

for (int i=0; i<N; i++)

{

for (int j=0; j<M; j++)

sum+=ar[i][j];

int *p=&ar[0][0];

for (int i=0; i<sizeof(ar)/sizeof(int); i++)

 {

 sum+=*p;

 p++;

}

}

Трёхмерные массивы.

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

int ar[2][3][4]={{{1,2,3,4},{5,6,7,8},{9,10,11,12}},{{-1,-2,-3,-4},{-5,-6,-7,-8},{-9,-10,    -11,-12 }}}

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

char *ar[20];

char *ar[]={ “One”,”Two”,”Three” };

char ar1[][6]={ “One”,”Two”,”Three” };

char ar1[3][6];

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

ar1[1][1]=          //ar[1][1]=”D”; - ошибка!

Динамическое распределение памяти.

malloc

calloc

realloc

free

int Mbyte=…;

int *p=static_cast<int*>(malloc(Mbyte));

if (p)…

size_t n=_msize(p)

calloc - обнуляет содержимое памяти

realloc - изменяет размер  ранее  захваченного  блока памяти

p=ststic_cast<int*> realloc(p,2000);

free - освобождается выделенная память

free(p)

Управление памятью. Оператор new и delete.

Преимущество new и delete:

  1.   необязательно явно указывать количество требуемых байт;
  2.   результатом выделившейся памяти с помощью malloc является указатель с типом <void *>, в случае использования new: компилятор неявно приводит тип указателя к указанному типу;
  3.  оператор new совмещает выделившуюся память с вызовом инициализирующей функции;

int *p=new int;     //sizeof(int)

         malloc(4)

int *p=operator new (sizeof(int));

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

delete(p);

Оператор new и массивы.

Создавать массивы следует только тогда, когда:

  1.  все или некоторые размерности массива определяются только во время выполнения программы;
  2.  размерности массива могут измениться в процессе программы.

Не стоит забывать об удалении (освобождении) динамически занятой памяти.

int n=выражение;      

//int ar[n];     // ERROR!!!

int *pn=new int[n];  //выделившийся в памяти блок размером n*sizeof(int)+служебная информация

pn[i]=…;

int *tmp=new int[n*z];

for (int i=0; i<n; i++){tmp[i]=pn[i];}

memcpy(tmp,pn,n*sizeof(int));

delete[]pn;

pn=tmp;

pn[i]=…;

delete[]pn;

delete[]tmp;

pn= Ø;

tmp= Ø;

Многомерные массивы.

pf=new float[n][2];      //создали массив векторов

delete []pf;

int M=выражение;

int N=выражение;

int *p=new int[M*N];

p[i*M+j]=…;

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

Проблема состоит в определении: является ли данный указатель указателем на одиночный объект или на массив объектов. Для этих целей в C++ существует две формы delete:

  1.  delete для одиночных объектов;
  2.  delete для массива.

Ссылки.

Косвенное обращение к объекту.

Указатель (pointer) – переменная,                       Ссылка (reference)

переменная,  которая явно содержит адрес объекта.

которая тоже содержит адрес объекта, но синтаксически ею пользуется также, как и самим объектом.

  1.  Объявление:

                    int *p=&x;                                                     int &r=x; // ссылка с именем

  1.  Инициализация:

                  int *p; //OK                                                       //int &r; // ERROR!!!

  1.  Получение значений:

                 int tmp=*p;                                                           int tmp=r;

     (*p)++;                                  r++;

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

  1.  Модификация адреса. Если указатель не объявлен как константный, то значение такого адреса можно изменять. Ссылка тоже содержит адрес, но этот адрес формируется один раз при инициализации, и изменить его невозможно.

      p++;    //OK                          r++;

      int y;               int y=5;

      p=&y; //OK                 r=y;   //r=y=x=5;

Комментарий: переменной x (адрес которой содержится в ссылке r) будет присвоено значение y.

  1.  Нулевое значение. Указатель может быть равен нулю (никуда не указывает). Ссылка всегда содержит адрес того объекта, которым она была проинициализирована.

 if (p)       if (r)

  1.  Применение оператора &. Чтобы получить указатель на объект, псевдонимом которого является ссылка r, можно применить к ссылке операцию получения адреса объекта (&).

int **pp=&p;                    int *pr=&r; (указывает на объект, псевдоним

которого является

int y=**pp;               

       ссылка, то есть pr          содержит адрес r

       переменной x

  1.  Ссылка на указатель. 

  int n;      ссылки на ссылку не

      существует

 int *p=&n;        int n;

 int *&refp=p;       int &r=n;

 *ref p=2;    //n=2;      int &rr=r;  // ERROR!!!

 *p=4;

  1.  void

            void *p;  //OK      void &r=x;  

//ERROR!!!

не знаем какого типа или какая память выделится т.к. ссылка – это всегда псевдоним совершенно определённого объекта

  1.  size of

        double *p;              double d;

        size_t n=sizeof(p); //4 байта           double &rd=d;

              size_t n=sizeof(rd);  //8 байт

  1.   Инициализация литералом.

         константная ссылка

 int *p=0x10000000;  //ERROR!//СИ  int &r=1;  //ERROR!!!

int *p=(int*)0x10000000; //OK!//СИ++ const int &r=1;   //OK!

int *p=reinterpret_cast<int*>(0x10000000);

Функции.

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

Функции позволяют:

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

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

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

При необходимости компилятор может произвести неявное преобразование типа.

[спецификатор][тип][соглашение по вызову] имя_функций ([список_аргументов] //[void])

или

[] - необязательно, ( ) – обязательно.

Спецификатор: соглашение по функции.

[Тип]: задаёт тип возвращённое функцией значение.

Если поле отсутствует, то функция должна возвращать int.

Если void           не возвращает значение

char MyFunc()

char MyFunc()

void MyFunc()  //тип возвращённого значения – int

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

Список аргументов. Определяет количество и тип аргументов (параметров), передаваемых в функцию.

Список_аргументов==тип_аргумента1[имя_аргумента1], тип_аргумента2[имя_аргумента2]…

Если в функцию не передавать аргументы, то поле пустое или содержит void.

Определение функции (реализация).

Определение функции включает те же поля, что и прототип функции + тело функции – код, выполняющийся при вызове функции (заключён в {}).

double summa (double a, double b)

{

double c;

c=a+b;

 return c;

}

Вызов функции: для обеспечения эффективного и безопасного вызова функций требуется обеспечить:

  1.  связь по управлению;
  2.   вызов и возврат;
  3.  сохранение и восстановление контекста вызывающей части;

 2)  функция должна производить одни и те же действия с разными наборами  данных.

вызывающая часть

стек

функция

a=5; b=8;

 

1)   Сохранение контекста

sum(a;b)

 

вызова.

1)     Формирование

 

2)     Создание текущего

параметров в стеке.

5

контекста.

2) Сохранение адреса

8

3)         Тело функции.

возврата и передача

 

4)        Формирование

управления (вызова).

 

return sum func

3)    Восстановление

 

5) Восстановление контекста

стека.

 

вызова.

4)     Получение               результата.

 

6)            Возврат.

Вызов inline функции.

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

При этом:

  1.  исключаются накладные расходы на вызов функции;
  2.  сохраняется структурность текста программы.

“-“ – увеличивает объем памяти

Inline функция не применима:

  1.  для функции, которая вызывается посредством указателя;
  2.  для функции с переменным числом параметров;
  3.  для рекурсивных функций.

inline
int sum(int x, int y)
{
  return x+y;
}

main()
{
 c=sum(3,5);
}

_ _cdecl, _fastcall, _stdcall - соглашение о вызове функции

Соглашения включают понятия:

  1.  способ передачи параметров (порядок размещения параметров в стеке);
  2.  кто восстанавливает стек – вызывающая или вызываемая функция;
  3.  способ хранения имён функций в объектном модуле;

Пример: пусть существует функция вида:

void calltype f(char c, short s, int i);
main()
{
 f(‘A’, 10,9999 , 1.23)

Адрес возврата
A
10
9999
1,23

                                                      //вызов

     возврат организован командой ret()

сdecl - параметры формируются справа налево.

_stdcall (в стиле Паскаль) – используется для уменьшения объёма исполняемого файла.

_f@20

                 //количество байт, занимаемое параметром в стеке

‘A’

10

Адрес возврата

9999

1,23

Способы передачи параметров

по значению                                                                 по адресу

по указателю        по ссылке(C++)

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

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

Если при передаче адреса требуется запретить функции модифицировать значения по этому адресу, используется ключевое слово const.

main()
{
  int a=2, b=1, r1, r2;
 fp(a,b,&r1,&r2);
}


указатель:
void fp(int x, int y, int *sub,int sum)
{
 
sum=x+y;
 sub=x-y;

}

main()
{
  int a=2, b=1,r1,r2;
 fr(a,b,r1,r2);
}


ссылка:
void fr ( int x, int y, int sum, int sub)
{
 sum=x+y;
 sub=x-y;
}

int My (const char *p)
{
 int n=0;
 while (*p)
 {
  p++;
  n++;
 }
 return n;
}

main()
{
 char ar[10]=”QWERTY”
 int count = My(ar)
 count=My(“ABC”);

}

void f(int*)
main()
{
 int x=1;
 int 8p=&x;
 f(p);
 f(&x);
}

int f1();
void f2(int, int)
main()
{
 int x=1; y=5;
 int z=f1();
 int w=x+y;
 f2(z,w);   
// f2(f1(), x+y);

}

Использование “ , ” , формирование параметров.


void f(int);
int main()
{
 int x;
 std: cin>>x;
 f(x);         
//  f (cin>>x, x);
}

Указатели на массивы в качестве параметров функции.

Компилятор Си никогда не передаёт массивов по значению. Передаётся только указатель на массив.

int ar1[k];
int ar2[k][m];
F(ar1,ar2);
void F(int *p1, int (*p2)[M])
int **p2;
void F(int p1[], int p2[][M])

Значения аргументов функций по умолчанию.

void f (char, int, float);
f(‘A’, 3, 2.4);
void f1 (char, int, double=1.5);
f1(‘A’, 3, 2.4);  
// по умолчанию можно использовать >=1 аргумента, но все они должны располагаться в конце списка аргументов
f1(‘A’, 3);

Замечание: поставить запятую вместо пропущенного параметра по умолчанию НЕЛЬЗЯ!!
f1(‘A’, , 3.2);

c=10;

a=f;


Блок схемы.

Вывод а;
вывод
b;

Имя функции, передаваемые параметры

a<b

          нет      да

Имя функции

Возвращ.
параметр


Переменное число параметров функции.

 Си допускает использование переменного числа параметров.

Специфика вызова:

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

int Func(int i; …);
int Func (int i, float z, …);


Признаком конца списка параметров является  
-1.

int Func(int i, …)
{
 int count=0, sum=0;
 int *p=&i;
 while(*p!=-1)
 {
  count ++;
  sum+=*p++;
 }
 return sum;
}


Функции стандартной библиотеки

(printf, scanf).

int printf( const char *, …);

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

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

%c – char

%d – int

%i – int

%e – exp  // x=1,1e+0,02

%f – float

%g – float , но без лишних нулей

%o – восьмеричное представление числа

%p – значение адреса указателя в шестнадцатеричном виде

%s – массив строк

%u – преобразование аргумента к целому беззнаковому в десятичном виде

%x - преобразование аргумента к целому беззнаковому в шестнадцатеричном виде

int m = -1;

printf(“%x”,m);

%[+-n]s (строка символов)

%[+-n]f (дробное число в фиксированном формате)

%[+-n]e (количество цифр после десятичной точки)

%[+-n]i  (d) (количество выводимых цифр)

%[+-n]g  (максимальное количество цифр)

Виды возвращаемых значений и механизмы их формирования.

Пользовательский тип

Адрес

Базовый тип

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



указатель

Объединение

структура

ссылка

класс

Функция может возвращать:

-объект базового типа;

-объект пользовательского типа;

-указатель или ссылку;

bool func();   //базовый тип
main()   
{
 
bool b= Func();
}

int &f()
{
 static int n;
 return n;
}

Проблемы при возвращении ссылки или указателя.

int *f1(int n)
{
 int nN=n*5;    
//return &nN;
}
//функция возвращает указатель на область памяти в стеке, которая после возвращения из функции может быть задействована компилятором для других целей

int &f2(int n)
{
 int nN=n*5;
 return nN;
}   //
функция возвращает указатель на локальную временную переменную

int main()
{
  z=f2(1) +f2(2)+f2(3);
}

Нельзя возвращать адреса локальных объектов.

Возвращать можно:

  1.  указатель или ссылку на объект со статическим временем существования;
  2.  указатель на строковый литерал;
  3.  указатель на динамически созданный объект.


Ключевое слово const.

void func (const int *pn);    // позволяет объявлять функции с ключевым словом const

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

void func1 (const char *pc)
{
 //*pc=’A’;
}


Замечание:

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

void func( const int n);

“Перегрузка” имён функции.

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

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

Ограничение в использовании перегрузки:

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

int MaxInt(int x; int y)
{
 return (x>y);
}
double MaxDouble (double x, double y)
{
 return (x>y);

}


//примеры без перегрузки

main()
{
int i=MaxInt (12,8);
double d= MaxDouble(1.1;2.2);
}


C перегрузкой:

main()
{
int i=Max(12,8);
double d= Max(1.1, 2.2);
double dd=Max(1.1, 2);   //ERROR!!
}
void f(int x, int y=5);


Возможный конфликт при использовании параметров по умолчанию.

void f(int x, int y = 0);

void f (int);

int main () {

f (2, 5) ; // ошибка

f (5); // ошибка, компилятор не знает, какую функцию вызывать.

}

Рекурсивные функции.

Вызывают сами себя.

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

Каждое выполнение тела функции имеет свою область стека для параметров и локальных переменных.

Достоинства:

  1.  компактность.

Недостатки:

  1.  большие затраты времени на вызов функции;
  2.  большие затраты памяти (стека) для организации каждого вложенного цикла.

Специфика рекурсивных функций:

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


Факториал без рекурсии:


int n=5;
int res=1;
for(int i=n; i>1; i--)
 res *=i;
int F(int n)
{
 if (n<=1)
 return 1;
 else
 {
   int x=F(n-1);
  return n*x;
 }
}

Структуры Си.

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

 Объявление структуры – описание компилятором шаблона, по которому он будет создавать объект данного типа.

struct имя
{
 
список полей структуры
}
struct student
{
 char name[30]
 unsigned int cource,
 bool sex;
 int age;
 …
}


Создание объектов структуры.

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

student vasya;

Си: struct student vasya;

Присваивание значений полям структуры.

vasya. age=20;
vasya. sex=1;
vasya. cource=1;
strcpy(vasya. name, “Vasiliy”);

[struct] student g_182_4[20];
g_182.4[0]. age=18;

Создание структур – типов данных.

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

typedef struct
{
 char name[30]
 ...
 int age;
}
student;
student pety;

Инициализация структур.

[struct] student Ira ={“Irina”, 2, 0, 19, …};

Замечание: инициализация указывается в том порядке, в котором соответствующие поля объявлены в структуре.

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

{
“Masha”, 1, 0, 18…
}

Действия со структурами.

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

[struct] student Balabanov1={“Anton”, 2, 0, 19, …};
[struct] student Balabanov2=Balabanov1;
strcpy(Balabanov2. Name, “Vladimir”);
Balabanov2=Balabanov1;

Указатели и структуры.

[struct] student *man1=new[struct] student;

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

->(минус больше)

strcpy(man1->name, “Yan”);
man1->sex=1;
man1->age=25;
man1->course=1;

Оператор sizeof. Размерность объекта.

struct A
{int ma;;}
size_n n=sizeof(A); //4
struct AA
{int ma; char m_c;};
size_n n=sizeof(AA);  //8
struct AAA {int;char;double;};
size_t n=sizeof (AAA);  //16

Структуры и функции.

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

  1.  делает программу нечитаемой;
  2.  увеличивает время на формирование параметров (создания копий в стеке);
  3.  занимает место в стеке.

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




1. Реферат- Бюджет
2. настоящему зовут Но я его люблю
3. Вместе с тем относительная функциональная незрелость нервных клеток ЦНС слабость процессов активного вну
4. экономические и политические явления которые придали античному обществу определенную специфику по сравнен
5. Т~~ым~уалау а~паратыны~ берілу турлерін ж~не ба~ыттарын
6. живой труд те реальные отношения между работниками и работодателями
7. тематическое общество Дорогой друг Юношеская математическая школа при СПбГУ приглашает Вас приня
8. Кожному гарантується право на справедливий розгляд та вирішення справи в розумні строки незалежним і неупе
9. Состояние сельскохозяйственного предприятия по выращиванию зерновых культур
10. тематичне моделювання та обчислювальні методи Автореферат дисертації на здобуття наукового сту
11. На нем были одеты обычные испачканные грязью джинсы синего цвета и байковая кофта
12. Микроконтент- как писать заголовки, заглавия страниц и темы в почтовых сообщениях
13. ХVIII вв после революции и гражданских войн сгладились вопиющие противоречия
14. Тема 9 Организационная культура в системе менеджмента Основные вопросы по теме- Понятие и элементы
15. А~ылой м~денитарихи процесті ал~а жылжытады деп ~арастыр~ан метафизикалы~ рационалды~ т~жырым жасаушы
16. Професійна орієнтація, підготовка та перепідготовка незайнятого населення
17. деньги их виды и функции Деньги это историческая категория которая является результатом развития то.
18. Экологические проблемы города Луганск
19. это набор материалов демонстрирующих умение учителя решать задачи своей профессиональной деятельности вы.
20. Клеточная биотехнологи