Вещественный тип данных, указатели, функции
Вещественные типы данных
Представление констант
Представление вещественных чисел в компьютере
Точность вычислений и «ловушка» для программиста
Преобразования типов данных
Правила неявного (арифметического) преобразования
Преобразование типов данных при операции присваивания
Явное преобразование типов данных
Исходы явного преобразования типов данных
Исходы явного преобразования типов данных
Тип данных «Указатель»
Работа с указателями
Работа с указателями
Внимание!
Арифметика указателей
Преимущества использования функций
Описание функций в программе
Пример описания функции
Завершение работы функции (инструкция return)
Список параметров функции
Примеры
Вызов функции
Передача данных по значению
Передача данных с помощью указателей
Пример
Передача массивов в качестве аргумента
Пример
Передача данных по ссылке
Аргументы - константы
Перегружаемые функции
Параметры по умолчанию
Параметры по умолчанию
Рекурсивное использование функций
Реализация функции
Передача функций в качестве параметров
Пример
Встраиваемые функции (inline - функции)
Прототипы функций
358.50K
Категория: ПрограммированиеПрограммирование

Вещественный тип данных, указатели, функции

1. Вещественный тип данных, указатели, функции

2. Вещественные типы данных

Стандарт C++ определяет три типа данных для хранения
вещественных значений: float, double и long double. Все эти
типы предназначены для представления отрицательных и
положительных значений (спецификатор unsigned к ним не
применим) в разных диапазонах:
• тип float занимает в памяти 4 байта с диапазоном абсолютных
значений от 3.4е-38 до 3.4е+38;
• тип double занимает в памяти 8 байт с диапазоном абсолютных
значений от 1.7е-308 до 1.7е+308;
• тип long double занимает в памяти 10 байт с диапазоном
абсолютных значений от 3.4e-4932 до 3.4e+4932.
Замечание. В консольных приложениях Windows тип данных
long double занимает в памяти 8 байт, то есть ничем не
отличается от типа double.
2

3. Представление констант

Константы вещественных типов задаются двумя
способами:
• нормальный формат: 123.456 или -3.14;
• экспоненциальный формат: 1.23456e2
(1.23456е+2). Привести другие примеры.
Дробная часть отделяется от целой части точкой, а
не запятой.
3

4. Представление вещественных чисел в компьютере

Внутреннее представление вещественного числа состоит из
двух частей — мантиссы и порядка:
-1.2345e+2
|
|
мантисса порядок
• Тип float занимает 4 байта, из которых один двоичный разряд
отводится под знак мантиссы, 8 разрядов под порядок и 23 под
мантиссу.
• Для величин типа double, занимающих 8 байт, под порядок и
мантиссу отводится 11 и 52 разряда соответственно.
• Длина мантиссы определяет точность числа, а длина порядка —
его диапазон.
4

5. Точность вычислений и «ловушка» для программиста

• Все вычисления с вещественными значениями
осуществляются приближенно, при этом, ошибки
вычислений могут достигать весьма существенных
значений. Это объясняется дискретностью
внутреннего (машинного) представления
непрерывного диапазона вещественных значений.
Точность представления значений вещественных
типов зависит от размера мантиссы. Относительная
точность представления вещественных значений
остается постоянной при различных значениях
порядка. Однако, абсолютная точность существенно
зависит от значения порядка (с уменьшением
порядка абсолютная точность возрастает).
5

6. Преобразования типов данных

• При выполнении различных операций над
разнотипными данными необходимы
преобразования одних типов данных к
другим.
• В языке C++ различают неявное
(автоматическое) и явное преобразование
типов данных.
6

7. Правила неявного (арифметического) преобразования

1.
2.
3.
4.
5.
Все данные типов char и short int преобразуются к типу int.
Если хотя бы один из операндов имеет тип double, то и другой
операнд преобразуется к типу double (если он другого типа);
результат вычисления имеет тип double.
Если хотя бы один из операндов имеет тип float, то и другой
операнд преобразуется к типу float (если он другого типа);
результат вычисления имеет тип float.
Если хотя бы один операнд имеет тип long, то и другой
операнд преобразуется к типу long (если он другого типа);
результат имеет тип long.
Если хотя бы один из операндов имеет тип unsigned, то и
другой операнд преобразуется к типу unsigned (если его тип не
unsigned); результат имеет тип unsigned.
Если ни один из случаев 1-5 не имеет места, то оба операнда
должны иметь тип int; такой же тип будет и у результата.
7

8. Преобразование типов данных при операции присваивания

При выполнении операции присваивания
тип значения выражения автоматически
преобразуется к типу левого операнда (к
типу данных переменной в левой части).
При этом возможны потери данных или
точности.
8

9. Явное преобразование типов данных

Явное преобразование типов данных осуществляется с помощью
соответствующей операции преобразования типов данных,
которая имеет один из двух следующих форматов:
(<тип данных>) <выражение> или <тип данных> (<выражение>)
Например:
(int) 3.14
(double) a
(long) (a + 1e5f)
или
int (3.14)
double (a)
long (a + 1e5f)
9

10. Исходы явного преобразования типов данных

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

11. Исходы явного преобразования типов данных

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

12. Тип данных «Указатель»

Указатели – это тоже обычные переменные, но они служат для
хранения адресов памяти.
Указатели определяются в программе следующим образом:
<тип данных> *<имя переменной>
Здесь <тип данных> определяет так называемый базовый тип
указателя.
<Имя переменной> является идентификатором переменнойуказателя.
Признаком того, что это переменная указатель, является символ
*, располагающийся между базовым типом указателя и именем
переменной-указателя.
Например:
int *p1;
double *p2;
12

13. Работа с указателями

Присвоить указателю адрес некоторой
переменной можно инструкцией
присваивания и операцией &, например,
int A = 2351, *p1;
double B = 3.14, *p2;
p1 = &A;
// Указателю p1 присваивается адрес
переменной А
p2 = &B;
// Указателю p2 присваивается адрес
переменной В
13

14. Работа с указателями

• Получить значение объекта, на который ссылается некоторый
указатель можно с помощью операции * (эту операцию обычно
называют разыменованием указателя):
• int A = 2351, *p1;
• double B = 3.14, *p2;
• p1 = &A;
// Указателю p1 присваивается адрес переменной А
p2 = &B;
// Указателю p2 присваивается адрес переменной В
cout << “Значение переменной А: ” << *p1 << endl;
cout << “Адрес переменной А: ” << p1 << endl;
cout << “Значение переменной В: ” << *p2 << endl;
cout << “Адрес переменной В: ” << p2 << endl;
14

15. Внимание!

При использовании указателей в
выражениях важно помнить, что операция
* имеет наивысший приоритет по
отношению к другим операциям (за
исключением операции унарный –
(минус)).
15

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

• К указателям можно применять некоторые
арифметические операции. К таким операциям
относятся: +, -, ++, --. Результаты выполнения этих
операций по отношению к указателям
существенно отличаются от результатов
соответствующих арифметических операций,
выполняющихся с обычными числовыми
данными.
• Добавлять к указателям или вычитать из
указателей можно только целые значения.
16

17. Преимущества использования функций

• Использование функций позволяет:
• значительно упростить разработку сложных
программ;
• сократить объем текста программы и
генерируемого результирующего кода программы;
• значительно упростить отладку и модификацию
программ;
• распределить работу над одной программой между
различными исполнителями программистами.
• Фактически разработка более-менее сложных
программ практически невозможна без
использования функций.
17

18. Описание функций в программе

• Любая функция состоит из двух основных элементов:
заголовка и тела функции.
• Заголовок функции имеет следующий формат:
• <Тип возвращаемого значения> <Идентификатор – имя
функции> (<Параметры>)
• Тело функции представляет собой блок инструкций языка
программирования, разделенных символами “точка с
запятой”:
• {
<Инструкция 1>;
<Инструкция 2>;
………………….
<Инструкция N>;
• }
18

19. Пример описания функции

double Example (double d, int k)
{
double r;
r = d * k;
return r;
}
• Не все функции должны возвращать значения. В
этом случае <Тип возвращаемого значения>
задается ключевым словом void, которое означает
– “пусто”
19

20. Завершение работы функции (инструкция return)

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

21. Список параметров функции

• Параметры функций служат для обеспечения взаимодействия
между функцией и вызвавшей ее программой.
• Параметры функций перечисляются в заголовке через символ
‘,’ (запятая). Каждый параметр должен соответствовать
определенному типу данных. Параметры могут быть любых
типов данных. Тип данных для каждого параметра (даже если
все они имеют один и тот же тип данных) задается отдельно в
соответствии со следующим форматом:
• <Тип данных параметра> <Идентификатор – имя
параметра>
21

22. Примеры

• void Example1 (int a, int b)
• или
• double Example2 (int A, double B)
22

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

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

24. Передача данных по значению

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

25. Передача данных с помощью указателей

Для использования передачи данных с помощью
указателей необходимо обязательно выполнить три
следующих пункта:
1. Соответствующий параметр в заголовке функции
необходимо определить как указатель на тип
данных аргумента.
2. При вызове функции на месте параметровуказателей необходимо использовать адрес
аргумента, а не сам аргумент.
3. При обращении внутри функции к значению
аргумента через параметр-указатель необходимо
осуществить разыменование этого указателя.
25

26. Пример

Необходимо разработать функцию, возвращающую
результат деления и остаток от деления двух целых
чисел.
int Div (int N1, int N2, int *Ost) // int *Ost – параметруказатель
{
*Ost = N1 % N2; // *Ost – разыменование
параметра-указателя
return N1 / N2;
}
26

27. Передача массивов в качестве аргумента

• Несколько проще обстоит дело с передачей
массивов, так как переменные типа массив
сами являются указателями на первый
элемент массива. В связи с этим отпадает
необходимость в выполнении пунктов 2 и 3
из перечисленных выше.
27

28. Пример

• void WriteArr ( int Arr[], int n)
• {
• for (int I = 0; I < n; ++I)
cout << Arr[I] << “ “;
• cout << endl;
• }
28

29. Передача данных по ссылке

• В языке C++ имеется более простой способ
передачи данных по адресу, а именно –
передача данных по ссылке.
int Div (int N1, int N2, int &Ost) // int &Ost –
параметр-ссылка
{
Ost = N1 % N2; // Разыменования параметрассылки Ost не требуется
return N1 / N2;
}
29

30. Аргументы - константы

• Недостатком передачи данных по адресу является скрытый
побочный эффект, связанный с возможным непредвиденным
изменением внутри функции значения аргумента переданного
по адресу. Однако этого эффекта можно избежать, если
определить соответствующий параметр функции как константу:
• void Proc(const double *D)
• {
……
*D = 3.14; // Ошибка в процессе компиляции
……
• }
30

31. Перегружаемые функции


Перегруженными функциями называются функции,
имеющие одинаковые имена, но различающиеся
количеством, типами данных или порядком следования
разнотипных параметров. Например:
void f (char c)
void f (int c)
void f (char c, int i)
void f (int c, char i)
void f (char c, char i)
• Нельзя перегружать функции, различающиеся только
типами данных возвращаемых значений.
int f (char c, char i)
31

32. Параметры по умолчанию


void F (int I, double D, char C = ’a’, int J = 10)
{
cout << C << “ “ << J << endl;
}
int main ()
{
F (0, 3.14);
// Результат: а 10
F (0, 3.14, ’G’);
// Результат: G 10
F (0, 3.14, ’G’, 1000); // Результат: G 1000
32

33. Параметры по умолчанию

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

34. Рекурсивное использование функций

Функции внутри своего тела могут вызывать сами
себя. Такой вызов называется рекурсией.
Рассмотрим функцию Pow(D,P)возведения
вещественного значения D в целую
положительную степень P.
Очевидно, что:
• Pow(D,P)=1 при P=0;
•Pow=Pow(D,P-1)*D при P>0;
34

35. Реализация функции

• double Pow (double D, unsigned P)
• {
if (P)
return Pow ( D, P – 1 ) * D;
else
return 1;
• }
35

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

• Используя тот факт, что имена функций являются
обычными указателями, можно передавать
функции в качестве аргументов других функций.
• Для этого необходимо с помощью оператора
typedef определить тип данных указателя на
функции, передаваемые в качестве аргумента,
который должен соответствовать характеристикам
передаваемых функций (тип возвращаемого
значения, параметры функции).
36

37. Пример


double add (double a, double b)
{ return a + b;}
double mul (double a, double b)
{return a * b;}
typedef double (*f_Ptr) (double, double);
double oper (f_Ptr F, double a, double b)
{return F (a, b);}
int main ()
{
cout << oper (add, 20, 30) << endl; // 50
cout << oper (mul, 20, 30) << endl; // 600
return 0;}
37

38. Встраиваемые функции (inline - функции)

• Встраиваемые функции задаются ключевым словом
inline:
inline int ReadInt(char *S)
Некоторые компиляторы накладывают определенные
ограничения на содержание встраиваемых функций. К
таким ограничениям обычно относятся использование
внутри встраиваемых функций:
• рекурсии;
• циклов, переключателей, инструкций goto;
• статических (static) переменных.
38

39. Прототипы функций

Прототипом функции называется заголовок
функции (со списком параметров),
заканчивающийся символом ;.
Например:
double F (int P1, double P2 ); // Это прототип
функции F
double F (int P1, double P2 ) // А это сама
функция F
{ return P1 * P2;}
39
English     Русский Правила