C++ - занятие 2
1
Какие типы вы бы использовали?
age // возраст
salary // зарплата за месяц (в рублях)
grade // средний балл
char short long int
unsigned long long float double
Я бы написал так:
int age; // short если очень экономить память
int salary; // double если вспомнить про копейки??
// long если вспомнить про 16 бит??
// Но тогда уж лучше typedef
double grade; // float если очень экономить память
Можно ли так написать?
Чему будут равны i, j, k?
int i=3; int j=2; int k=1;
1. i %= j;
2. i = j++ > 0 || k++ > 0;
3. (i *= 10) += j;
4. i = (int n = 2)*j;
5. i = '9' - '0';
6. i = k++ / k++;
7. i += sizeof(j++);
// i = 1
// i = 1; j = 3; k = 1;
// i = 32
// ошибка
// i = 9
// результат не определен
// i = 7 (но вообще-то зависит от компилятора), j = 2
Д.з.
4
Задача 1: 1/13 + 1/3 3 + 1/5 3 …#include <iostream>
using namespace std;
int main()
{
int n;
cin >> n;
double sum = 0;
for (int i = 1; i <= 2*n+1; i+=2) {
sum += 1.0/(i*i*i);
}
cout << sum;
return 0;
}
5
Перебираем нечетные числа
Или 1/i/i/i или pow(i,3)
Задача 2:
Среднее арифметическое до 0
// Найти среднее арифметическое чисел пока != 0
int sum = 0;
int num = 0;
for (;;) {
int n;
cin >> n;
if (n == 0)break;
sum += n;
num++;
}
cout << sum/num;
Прием «1.5 цикла»
Повторять до бесконечности
Задача 3: Количество элементов до
первого нуля
Без указателей
int i = 0;
for (; a[i] != 0; i++)
;
cout << i;
С указателями
int i = 0;
for (int* p = a; *p != 0; p++)
i++;
cout << i;
или
int* p = a;
for (; *p != 0; p++)
;
cout << p - a;
Вызов
int a[10] = {1,2,3,0,0,0,0,0,0,0};
Или можно короче
int a[10] = {1, 2, 3};
Нули можно не писать
А как можно быстрее?
Задача 4 – пока только некоторые
замечания
… проверяем, например на простоту …
for (int i = 0; i < n/2; i++)
Неэффективно!
Можно закончить проверять раньше
for (int i = 0; i <= sqrt(n); i++)
Неэффективно
Вычислять корень в цикле – это плохо
Задача 4 – еще замечание
… проверяем на почти простоту …
for (int i = 0; …; i++) {
if (n % i == 0 && isPrime(i) && isPrime(n/i) == 0)
return true;
}
return false;
Неэффективно!
То, что число не почти простое тоже можно выяснить не перебирая все делители!
Достаточно двух делителей!
Самый маленький делитель можно не проверять, он всегда простой.
Еще про операторы
10
switch
switch ( ocenka ) {
case 1:
case 2: cout << "плохо";
break;
case 3: cout << "удовлетворительно";
break;
case 4: cout << "хорошо";
break;
case 5: cout << "отлично";
break;
default: cout << "что-то странное";
}
Пустой оператор
;
while (a[i++] != 0);
Еще про массивы
13
На самом деле, вместо массивов
удобнее использовать тип vector
Забегая вперед, вообще-то удобнее всего писать так:
vector<int> v(10); // Массив из 10 чиселv[5] = 7;…
Но это мы пройдем позже
Многомерные массивы?
Используются массивы массивов
int c[10][10]; // Так определяемc[i][j] = 0; // Так используем
Еще разное
16
Самые частая простая ошибка
1. if (i = 0)
if (i == 0)
Скобки
if (i > 0 && i < n)
*p++
*(p++)
Скобки вокруг сравнений не нужны
Указатели
20
Указатели – динамическая память Динамическая память: new и delete
p = new int;p = new int(56); // С инициализацией…delete p;
Динамическое отведение массивов
p = new int[k]; // Отвести массив из k элементов
Размер может быть любым целым выражением
Освобождение массивов
delete [] p; // Не забыть [] !!!
20
Инициализация в С++11 Так в С++11 можно инициализировать нулями:
int a[10] {};
int* p = new int [n] {};
Так в С++11 можно инициализировать динамический массив:
int* p = new int [n] {1, 2, 3};
21
Умные указатели (забегая вперед)
С++11:
shared_ptr
unique_ptr
И в результате new и delete пишут довольно редко.
Но об этом будет позже.
22
23
Ошибки при работе с указателями - 1
1. p = nullptr;*p = 5;
2. int* p;*p = 5;
3. delete p;*p = 5;
4. p = q;…delete p;
…*q = 5;
Обращение к нулевому адресу
Использование не инициализированного адреса
Обращение к уже освобожденной памяти (dangling pointer)
Ошибки при работе с динамической
памятью - 2
1. int* p = new int;p = nullptr;
2. p = new int;…p = new int;
Утечка памяти (memory leak)
Указатели и массивы
Адресная арифметика
Указатели и массивы
int a[100];
int* p;
p = a; // a – адрес нулевого эл-та (&a[0])
p + n; // на n ячеек впередp - n; // на n ячеек назад
p += n p -= np++ p--
p1 – p2 // Сколько ячеек между p1 и p2?
if (p1 < p2)
p[i] // Как бы считаем p началом массива// p[i] тоже, что *(p+i)
Зачем это все?
С указателями можно работать, как с массивами
int* p = new int[k];p[3] = 1;
С массивами можно работать с помощью указателей
int a[100];
for (int* p = а; p < a + 100; p++) s += *p;
Замечания
Равенства:
&a[i] то же, что а+i
&a[0] то же, что а
Операции с массивами не отличаются (почти) от указателей
А в чем все-таки отличаются?
sizeof a - размер массива
sizeof p - размер указателя
Еще про функции
Как в C++ описать функцию, которая
ничего не возвращает?
Любую функцию можно вызывать, как процедуру -результат просто не используется
А можно написать тип результата void
void f(int i){
if (i>0) {cout << "Положительное";return;
}cout << "Отрицательное";
}
И тогда можно писать return;
Передача параметра по ссылке
void twice(int& i){
i *= 2;}
int k = 10;twice(k);cout << k;
// Теперь 20
примерно то же, что var в Паскале подробнее про & позже
int& - параметр передается по ссылке.
Означает, что мы хотим менять параметр в функции
Объявление функции Вообще компилятор С++ однопроходный.
Перед тем, как использовать что-то, оно должно быть объявлено
Иногда бывает, что мы хотим использовать фунцкию до того, как мы ее определили.
void f(int i, int j);
…
cout << f(3, 5);…
void f(int i, int j){
…}
void f(int i, int j); - примерно то же, что forward в Паскале
Определение фунцкии
Объявление функции (прототип)
Массивы, как параметры
Что будет, если массив передать, как
параметр?
int arr[100];…cout << sum(arr);
int sum(int *a){
int s = 0;for(int i = 0; i < 100; i++)
s += a[i];return s;
}
В функцию передается указатель на первый элемент
Массив, как параметр - продолжение
И так можно писать:
int sum(int a[])
Но это транслируется в
int sum(int* a)
(Если сомневаетесь – попробуйте sizeof)
Замечание о длине массива:
А можем мы как-то в функции узнать длину массива?
Нет!
Ну и что тогда делать?
Передавать длину в качестве доп. параметра (или использовать
vector и т.д.)
Дополнительные
возможности для функций
inline
inline int square(int i){
return i*i;}
n = square(k);
Зачем?
Будет работать быстрее (но код будет больше)
Это просто совет.. Компилятор может и не послушать
По-моему, современным компиляторам особо нет смысла что-то советовать…
Заменяется прямо в тексте на n = k*k
Параметры по умолчанию void f(int i, int j = 55) {
…}
…
f(3)
f(3, 55)
Замечания:
Умолчания могут быть для нескольких параметров
void f(int i = 0, double x = 1, int j = 100)
Нельзя опускать параметры в середине (только в конце)
Вопрос: где писать – в объявлении (прототипе) или в описании?
В обьявлении!
Перегрузка (overload) void f(int i) {… }
void f(double j) { … }
OK, компилятор различит:
f(5); // Первая функция
f(3.14); // Вторая функция
Может перегружаться с разным количеством аргументов
void f(int i) { … }
void f(int i, int j) { … }
f(5); // Первая функция
f(7, 8); // Вторая функция
Всегда ли можно перегружать
функции?
int f(int i) { … }double f(int i) { … }
Будет ошибка при вызове
f(5); // Непонятно, что вызывать
int f(int i) { … }int f(int i, int j = 0) { … }
А так можно?
Тоже будет ошибка при вызове
Компилятор должен однозначно понимать, какая функция вызывается
Ссылки
42
Ссылки
int a[100];
int& k = a[5];
k++; // тоже, что a[5]++;
cout << k; // cout << a[5];
Типичные применения
1. Синоним для быстрого доступа к объекту
а[7].abc.b[88] += 10;
int& k = а[7].abc.b[88];
k += 10;
2. Параметры по ссылке – аналог var в Pascal
void dbl(int& i){
i *= 2;}
int m = 10;dbl(m);
// При вызове dbl выполняется int& i = m; 43
Применения ссылок - продолжение
3. Функции, возвращающие lvalue
Что такое lvalue?
11 x+1 sin(x) true
x a[i] *p x.abc
lvalue – то, у чего есть адрес(или можно сказать - то, что может писаться слева от равенства, но это не совсем точно)
44
Применения ссылок - продолжение
int& f(int* a) // Функция, возвращающая lvalue
{ // (просто для примера, совершенно бессмысленная)
return a[5];
}
int arr[100];
cout << f(arr); // то же, что cout << аrr[5]
f(arr) = 77; // то же, что аrr[5] = 77
f(arr)++; // то же, что аrr[5]++
45
Д.з.
46
Задачи1. Описать функцию reverse.
Должна позволять переставить: массив чисел в обратном порядке.
Желательно реализовать с помощью арифметики указателей (не используя [])
(И *(a+i) тоже не используя…)
2. “Треугольный массив”Массив указателей:int* a[10];
а. Отвести динамические массивы длины 1, 2, 3… и запомнить указатели в массиве a.
б. Инициализировать массивы так: в каждом массиве последний элемент 1, остальные 0.
в. Напечатать массив.
3. Ф-я order(x, y, z); - меняет значения в x, y, z так, чтобы они шли по возрастанию,x <= y <= z. Например:
int a=5, b=1; c=3;order(a, b, c);
… теперь a==1, b==3, c==5
4. Изменение параметров без ссылок (старинный способ:)
Опишите функцию, которая удваивает данное число. В качестве параметра передается указатель на число.
Пример вызова:
int i = 5;twice(&i);… // теперь i == 10
47