int rollDice()
{
return 4;
}
int square(int x)
{
return x * x;
}Исторически принятое соглашение для языка С.
Аргументы функций передаются через стек, справа налево. Аргументы, размер которых меньше 4-х байт, расширяются до 4-х байт. Очистку стека производит вызывающая программа. integer-like результат возвращается через регистр EAX.
Перед вызовом функции вставляется код, называемый прологом (prolog) и выполняющий следующие действия:
- сохранение значений регистров, используемых внутри функции
- запись в стек аргументов функции
После вызова функции вставляется код, называемый эпилогом (epilog) и выполняющий следующие действия:
- восстановление значений регистров, сохранённых кодом пролога
- очистка стека (от локальных переменных функции)
Соглашение о вызовах, используемое компиляторами для языка C++ при вызове методов классов.
Отличается от cdecl соглашения только тем, что указатель на объект, для которого вызывается метод (указатель this), записывается в регистр ecx.
Передача параметров через регистры: если для сохранения всех параметров и промежуточных результатов регистров не достаточно, используется стек (в gcc через регистры ecx и edx передаются первые 2 параметра).
[[gnu::fastcall]]
void foo1(int x, int y, int z, int a)
{
}
void foo2(int x, int y, int z, int a)
{
}
void bar1()
{
foo1(1, 2, 3, 4);
}
void bar2()
{
foo2(5, 6, 7, 8);
}g++ -c test.cpp -o test.o -O0 -m32
objdump -d test.o
000005c8 <_Z4bar1v>:
5c8: 6a 04 push $0x4
5ca: 6a 03 push $0x3
5cc: ba 02 00 00 00 mov $0x2,%edx
5d1: b9 01 00 00 00 mov $0x1,%ecx
5d6: e8 b5 ff ff ff call 590 <_Z4foo1iiii>
5dd: c3 ret
000005eb <_Z4bar2v>:
5eb: 6a 08 push $0x8
5ed: 6a 07 push $0x7
5ef: 6a 06 push $0x6
5f1: 6a 05 push $0x5
5f3: e8 b3 ff ff ff call 5ab <_Z4foo2iiii>
5fd: c3 ret - 6 регистров (RDI, RSI, RDX, RCX, R8, R9) для передачи integer-like аргументов
- 8 регистров (XMM0-XMM7) для передачи double/float
- если аргументов больше, они передаются через стек
- для возврата integer-like значений используются RAX и RDX (64 бит + 64 бит)
Иногда вызова функции не будет - оптимизирующий компилятор выполнит ее встраивание по месту вызова.
Можно подсказать компилятору выполнить встраивание:
inline void foo()
{
}Но, компилятор умный и скорее всего проигнорирует inline, но можно попросить настойчивей:
// ms vc
__forceinline void foo()
{
}// gcc
__attribute__((always_inline)) void foo()
{
}Все равно нет гарантий.
#ifdef __GNUC__
#define __forceinline __attribute__((always_inline))
#endifСсылка - псевдоним объекта.
Главное отличие от указателя - ссылка должна быть проинициализирована при объявлении и до конца своей жизни ссылается только на один объект.
int a = 1;
int b = 2;
int* ptr = nullptr;
ptr = &a;
ptr = &b;
int& ref; // Ошибка
int& ref = a;
ref = b; // Ошибкаint a = 2;
int* ptr = nullptr;
int*& ptrRef = ptr;
ptrRef = &a;void foo(int x)
{
x = 3;
}
int x = 1;
foo(x);
// x == 1
void bar(BigObject o) { ... }- В функции окажется копия объекта, ее изменение не отразится на оригинальном объекте
- Копировать большие объекты может оказаться накладно
void foo(int& x)
{
x = 3;
}
int x = 1;
foo(x);
// x == 3
void bar(BigObject& o) { ... }- Копирования не происходит, все изменения объекта внутри функции отражаются на объекте
- Следует использовать, если надо изменить объект внутри функции
void swap(int& x, int& y)
{
int tmp = x;
x = y;
y = tmp;
}void foo(const int& x)
{
x = 3; // ошибка компиляции
}
void bar(const BigObject& o) { ... }- Копирования не происходит, при попытке изменения объекта будет ошибка
- Большие объекты выгодней передавать по ссылке, маленькие - наоборот
void foo(int* x)
{
*x = 3;
}
void bar(BigObject* o) { ... }
void foo(const int* x)
{
*x = 3; // ошибка компиляции
}
void bar(const BigObject* o) { ... }- Копирования не происходит
- Если указатель на константный объект, то при попытке изменения объекта будет ошибка
- Есть дополнительный уровень косвенности, возможно придется что-то подгрузить в кеш из дальнего участка памяти
- Реализуется optional-концепция
int countObject(time_t* fromDate, time_t* toDate)
{
const auto begin =
fromDate == nullptr
? objects_.begin()
: objects_.findFirst(fromDate);
}void foo(int&& x) { ... }
void bar(BigObject&& o) { ... }Поговорим в отдельной лекции.
void print(int x) // 1
{
std::cout << x << std::endl;
}
void print(bool x) // 2
{
std::cout << (x ? "true" : "false") << std::endl;
}
print(10); // 1
print(true); // 2void print(const std::string& x) // 3
{
std::cout << "string" << std::endl;
}
print("hello!"); // 2 const char* приводится к boolПерегруженная функция - декорированная функция
Проблема:
// math.h
double cos(double x);// ваш код
double cos(double x);// ваш код
double fastCos(double x);namespace fast
{
double cos(double x);
}
fast::cos(x);
cos(x); // вызов из math.h- Проверка в текущем namespace
- Если имени нет и текущий namespace глобальный - ошибка
- Рекурсивно поиск в родительском namespace
void foo() {} // ::foo
namespace A
{
void foo() {} // A::foo
namespace B
{
void bar() // A::B::foo
{
foo(); // A::foo
::foo(); // foo()
}
}
}Добавляет имена из указанного namespace в текущий namespace.
void foo()
{
using namespace A;
// видимо все из A
}void foo()
{
using namespace A::foo;
// видима только A::foo()
}void foo()
{
namespace ab = A::B;
ab::bar(); // A::B::bar()
}using namespace fast;
cos(x); // ???
cos(x); // ???Не используйте using namespace в заголовочных файлах!
void foo(int x)
{
}
typedef void (*FooPtr)(int);
FooPtr ptr = foo;
ptr(5);using FooPtr = void(*)(int);Функция высшего порядка — функция, принимающая в качестве аргументов другие функции или возвращающая другую функцию в качестве результата.
void sort(int* data, size_t size, bool (*compare)(int x, int y));
bool less(int x, int y)
{
retunr x < y;
}
sort(data, 100, less);using OnMailReceived = void (*)(const Mail& newMail);
void addOnMailReceivedObserver(OnMailReceived handler);
void onMailReceivedHandler(const Mail& newMail)
{
...
}
addOnMailReceivedObserver(onMailReceivedHandler);using MoveFunctionPtr = void (*)(int& x, int& y);
void moveLeft(int& x, int& y) { ... }
void moveRight(int& x, int& y) { ... }
std::vector<MoveFunctionPtr> trajectory =
{
moveLeft,
moveLeft,
moveRight,
};
int x = 0;
int y = 0;
for (auto& func : trajectory)
{
func(x, y);
}auto lessThen3 = [](int x) { return x < 3; };
if (lessThen3(x)) { ... }[список_захвата](список_параметров) { тело_функции }[список_захвата](список_параметров) -> тип_возвращаемого_значения
{ тело_функции }int x = 5;
int y = 7;
auto foo = [x, &y]() { y = 2 * x };
foo();Если не указать &, то будет захват по значению, то есть копирование объекта; если указать, то по ссылке (нет копирования, можификации внутри функции отразяться на оригинальном объекте).
// Захват всех переменных в области видимости по значению
auto foo = [=]() {};// Захват всех переменных в области видимости по ссылке
auto foo = [&]() {};Использование переменных, определённых в той же области видимости, что и лямбда-функция, называют замыканием.
[] // без захвата переменных из внешней области видимости
[=] // все переменные захватываются по значению
[&] // все переменные захватываются по ссылке
[x, y] // захват x и y по значению
[&x, &y] // захват x и y по ссылке
[in, &out] // захват in по значению, а out — по ссылке
[=, &out1, &out2] // захват всех переменных по значению,
// кроме out1 и out2, которые захватываются по ссылке
[&, x, &y] // захват всех переменных по ссылке, кроме x,
// которая захватывается по значениюint x = 3;
auto foo = [x]() mutable
{
x += 3;
...
}#include <functional>
using MoveFunction =
std::function<void (int& x, int& y)>;
MoveFunction getRandomDirection() { ... }
std::vector<MoveFunction> trajectory =
{
moveLeft,
moveLeft,
moveRight,
[](int& x, int& y)
{
...
},
moveLeft,
getRandomDirection()
};
int x = 0;
int y = 0;
for (auto& func : trajectory)
{
func(x, y);
}char* data = (char*) malloc(1024);
...
free(data);std::unique_ptr<char[]> data(new char[1024]);
// или так
auto data = std::make_unique<char[]>(1024);char* ptr = data.get() // Указатель
char x = data[100]; // Элемент 100
data.reset(); // Явное освобождение памятиНужно написать программу, считающую, сколько раз простое число встречается в указанном диапазоне массива.
Массив состоит из упорядоченных по возрастанию целых чисел (int), числа находятся в диапазоне [0, 100000]. На вход программы поступают пары чисел из первого и последнего числа в диапазоне (пар может быть несколько). Нужно найти в массиве эти числа и вывести в консоль, сколько простых чисел находится между ними включительно.
Пример:
3 4 4 5 7 8 8 9 11 11 15
./test 4 15
4
2 2 2 3 3
./test 2 3
5
Если заданного числа в массиве нет, то вывести в консоль 0.
Пример:
3 4 4 5 7 8 8 9 11 11 15
./test 6 15
0
Если ввод некорректен или произошла внутренняя ошибка, вернуть -1, иначе программа должна возвращать 0.
Получить аргументы командной строки:
int main(int argc, char* argv[])
{
for (int i = 1; i < argc; ++i)
{
int v = std::atoi(argv[i]);
std::cout << v;
}
return 0;
}Массив с данными находится в директории с домашними заданиями - это файл numbers.zip, просто распакуйте его в свою директорию с кодом и в своем коде подключите его:
#include "numbers.dat"
// Data - массив
// Size - размер массиваЗаливать этот файл в репозиторий не надо!!!
Адрес сервера для проверки домашнего задания:
https://homework.trempoltsev.ru
Примерный вывод прогона тестов:
./test 12 18
ok
./test 1 99999
ok
./test 25 25
ok
./test 30 29
ok
./test 99999 1
ok
./test 97 62285
ok
./test 41753 91449
ok
./test 3 99993
ok
./test 3
ok
./test
ok
./test 3 3 3
ok
./test 12 18 1 99999
ok
bencmarking
0.120469093323 sec
Если программа работает дольше 10 секунд - она будет убита по таймауту
Если тесты прошли успешно (везде ок), то сообщаете Владиславу о своем успехе.
Файл с тестом и данными:
https://github.com/mtrempoltsev/msu_cpp_lectures/tree/master/homework/01
EOF