2. Обзор курса. Модуль №1
Модуль №1. Углубленное программирование на языке C.
Управление памятью
•
•
•
•
Лекция №1. Цели и задачи курса. Язык C11. Основы организации и
использования оперативной и сверхоперативной памяти в программах
на языке C
Практикум №1. Адресная арифметика. Одномерные массивы и строки.
Алгоритмы их обработки
Лекция №2. Дополнительные вопросы организации и использования
оперативной и сверхоперативной памяти в программах на языке C
Практикум №2. Многомерные массивы и прочие составные типы языка C.
Алгоритмы их обработки. Взаимодействие с ОС
2
3. Обзор курса. Модуль №2
Модуль №2. Объектная модель языка С++. Безопасное
программирование
•
•
•
•
•
Лекция №3. Специальные вопросы инкапсуляции
Лекция №4. Специальные вопросы наследования и полиморфизма.
Множественное и виртуальное наследование. Динамическая идентификация
типов (RTTI)
Практикум №3. Проектирование полиморфной иерархии классов повышенного
уровня сложности
Лекция №5. Шаблоны классов и методов. Обработка исключительных ситуаций.
Обобщенное и безопасное программирование
Практикум №4. Разработка и обеспечение безопасности полиморфной
иерархии с шаблонами классов
3
4. Обзор курса. Модуль №3
Модуль №3. Библиотеки для промышленной разработки ПО:
STL, Boost
•
•
•
Лекция №6. Практическое введение в STL. Функциональное программирование
в C++11
Лекция №7. Практическое введение в Boost
Практикум №5. Оптимизация полиморфной иерархии классов с использованием
элементов библиотек STL и Boost
4
5. Обзор курса. Модуль №4
Модуль №4. Шаблоны объектно-ориентированного
проектирования. Основы промышленной разработки ПО
•
•
•
•
Лекция №8. Принципы и шаблоны объектно-ориентированного проектирования.
Базовые шаблоны, шаблоны GoF
Практикум №6. Оптимизация полиморфной иерархии классов с использованием
шаблонов объектно-ориентированного проектирования однопоточных
приложений
Лекция №9. Идиоматика C++. Основы рефакторинга и качество исходного кода.
Стандарты кодирования и методологии разработки ПО
Практикум №7. Рефакторинг и документирование объектноориентированного исходного кода
5
6. Лекция №1. Цели и задачи
курса. Язык C11. Основы
использования памяти в
программах на языке C
1. Язык C в современной
промышленной разработке. Новое в
языке C11.
2. Основы препроцессорной обработки.
3. Вопросы управления памятью и
производительность кода.
4. Физическая и логическая
организация оперативной и
сверхоперативной памяти.
5. Классы памяти в языке C.
6. Указатели и арифметика
указателей. Одномерные массивы и
строки.
7. Постановка индивидуальных задач к
практикуму №1.
6
7. Цель и структура курса
Цель курса — сформировать практические навыки и умения,
необходимые специалистам по разработке программного
обеспечения (ПО) для участия в проектах промышленной
разработки среднего уровня сложности, в том числе для
замещения стажерских должностей разработчиков серверной
части высоконагруженных приложений.
Состав курса — 9 лекций, 7 практикумов.
Для сравнения:
весна 2013 — 10 лекций, 6 практикумов;
осень 2012 — 12 лекций, 4 практикума.
Общая аудиторная нагрузка — 64 акад. часа:
лекционные занятия — 36 акад. часов;
практические работы — 28 акад. часа.
7
8. Чему научимся?
Практический результат (1 / 2)
Обязательно:
моделировать систему при помощи UML-диаграмм классов;
разрабатывать код на языке C / C++;
создавать качественный код в структурной и объектноориентированной парадигме;
использовать приемы обобщенного и безопасного программирования;
применять промышленные библиотеки STL, Boost;
внедрять в продукт классические архитектурные шаблоны GoF;
оценивать качество и выполнять рефакторинг исходного программного
кода;
презентовать и защищать свои разработки перед аудиторией.
8
9. Чему научимся?
Практический результат (2 / 2)
По желанию:
моделировать варианты использования продукта;
выполнять кодогенерацию по UML-моделям;
использовать новые возможности языка C++11;
писать многопоточные приложения;
создавать POSIX-совместимый переносимый исходный код;
реализовывать графический интерфейс пользователя в Qt.
9
10. Организационные положения
Расписание занятий:
постановка задач к практикумам — на лекциях №№1, 2, 3, 4, 6, 8 и 9
(работа выполняется индивидуально и в парах!).
Регламент занятия:
продолжительность — 4 акад. часа с 1 или 2 перерывами общей
продолжительностью до 10 минут;
вопросы — общезначимые: в любое время (во время пауз или по
поднятию руки!), индивидуальные: в перерыве или после занятия;
ведение профессиональной видеосъемки (задавайте вопросы по
существу и разборчиво!).
Знакомство с аудиторией:
известные языки программирования (C, C++, Java);
опыт разработки, известные среды и технологии.
10
11. Рекомендуемая литература (1 / 5)
Гамма Э., Хелм Р., Джонсон Р., Влиссидес Дж. Приемы объектноориентированного проектирования. Паттерны проектирования. — Питер,
2007. — 366 с.
Дейтел Х., Дейтел П. Как программировать на C++. — Бином-Пресс, 2009. —
800 с.
Дьюхэрст С. Скользкие места С++. Как избежать проблем при
проектировании и компиляции ваших программ. — ДМК Пресс, 2012. —
264 с.
Кериевски Дж. Рефакторинг с использованием шаблонов. — Вильямс, 2006.
— 400 с.
Керниган Б., Ритчи Д. Язык программирования C. — Вильямс, 2012. — 304 с.
Коплиен Дж. Программирование на C++. — Питер, 2005. — 480 с.
11
12. Рекомендуемая литература (2 / 5)
Липпман С., Лажойе Ж. Язык программирования C++. Вводный курс. —
Невский Диалект, ДМК Пресс. — 1104 с.
Липпман С., Лажойе Ж., Му Б. Язык программирования C++. Вводный курс. —
Вильямс, 2007. — 4-е изд. — 896 с.
Лишнер Р. STL. Карманный справочник. — Питер, 2005. — 188 с.
Макконнелл С. Совершенный код. Мастер-класс. — Русская редакция, 2012.
— 896 с.
Мейерс С. Наиболее эффективное использование C++. 35 новых
рекомендаций по улучшению ваших программ и проектов. — ДМК Пресс,
2012. — 298 с.
Мейерс С. Эффективное использование C++. 55 верных советов улучшить
структуру и код ваших программ. — ДМК Пресс, 2006. — 300 с.
12
13. Рекомендуемая литература (3 / 5)
Мюссер Д., Дердж Ж., Сейни А. C++ и STL. Справочное руководство. —
Вильямс, 2010. — 432 с.
Прата С. Язык программирования C. Лекции и упражнения. — Вильямс, 2013.
— 960 с.
Прата С. Язык программирования C++. Лекции и упражнения. — Вильямс,
2012. — 6-е изд. — 1248 с.: ил.
Саммерфилд М. Qt. Профессиональное программирование. Разработка
кроссплатформенных приложений на C++. — Символ-Плюс, 2011. — 560 с.
Саттер Г. Новые сложные задачи на C++. — Вильямс, 2005. — 272 с.
Саттер Г. Решение сложных задач на C++. — Вильямс, 2008. — 400 с.
Саттер Г., Александреску А. Стандарты программирования на C++. —
Вильямс, 2008. — 224 с.
Седжвик Р. Алгоритмы на C++. — Вильямс, 2011. — 1056 с.
13
14. Рекомендуемая литература (4 / 5)
Страуструп Б. Программирование. Принципы и практика использования C++.
— Вильямс, 2011. — 1248 с.
Страуструп Б. Язык программирования C++. — Бином, 2011. — 1136 с.
Фаулер М. Рефакторинг. Улучшение существующего кода. — Символ-Плюс,
2008. — 432 с.
Шилдт Г. C++: базовый курс. — Вильямс, 2008. — 624 с.
Шилдт Г. C++. Методики программирования Шилдта. — Вильямс, 2009. — 480
с.
Шилдт Г. Полный справочник по C. — Вильямс, 2009. — 704 с.
Шилдт Г. Полный справочник по C++. — Вильямс, 2007. — 800 с.
Шлее М. Qt 4.8. Профессиональное программирование на C++. — БХВПетербург, 2012. — 894 с.
14
15. Рекомендуемая литература (5 / 5)
Abrahams, D., Gurtovoy, A. C++ Template Metaprogramming: Concepts, Tools, and
Techniques from Boost and Beyond (Addison Wesley Professional, 2004).
Demming, R., Duffy, D.J. Introduction to the Boost C++ Libraries; Volume I –
Foundations (Datasim Education BV, 2010).
Demming, R., Duffy, D.J. Introduction to the Boost C++ Libraries; Volume II –
Advanced Libraries (Datasim Education BV, 2012).
Drepper, U. What Every Programmer Should Know About Memory (2007). URL:
http://people.redhat.com/drepper/cpumemory.pdf.
King K. C Programming: A Modern Approach, 2nd ed. (W. W. Norton & Co., 2008).
Meyers, S. CPU Caches and Why You Care. URL:
http://aristeia.com/TalkNotes/PDXCodeCamp2010.pdf
Musser, D.R., Saini, A. STL Tutorial and Reference Guide: C++ Programming with
the Standard Template Library (Addison-Wesley, 1995).
15
16. Web-ресурсы и онлайн-книги
Официальный Web-сайт проекта Boost: http://www.boost.org/.
Официальный Web-сайт проекта Eclipse: http://www.eclipse.org/.
Справка по языкам C / C++: http://ru.cppreference.com/w/,
http://en.cppreference.com/w/.
C Programming: http://en.wikibooks.org/wiki/C_Programming.
Google C++ Style Guide: http://googlestyleguide.googlecode.com/svn/trunk/cppguide.xml
More C++ Idioms: http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms.
Schäling, B. The Boost C++ Libraries: http://en.highscore.de/cpp/boost/.
16
17. Блог дисциплины
Размещен по адресу: http://tp.mail.ru/blog/cpp/
Что делать:
подписаться на обновления;
изучить более ранние записи;
задавать вопросы;
участвовать в опросах и обсуждениях.
17
18. Язык C в современной
промышленной разработке
Разработанный в начале 1970-х гг. язык C
по-прежнему активно используется в практике
программирования:
ядра операционных систем: Linux, Windows NT, z/OS;
инструментальные средства: gdb, vi / vim, gedit;
системы управления БД: MySQL, Oracle Database;
Web-серверы: Apache, nginx;
проекты Mail.Ru Group: ICQ, tarantool и пр.
Особый интерес представляет применение С
в *nix-проектах с высокой нагрузкой или высокой
доступностью.
18
19. Основы препроцессорной обработки
Препроцессор анализирует исходный код программы до
компиляции, следуя предназначенным ему директивам.
Директивы препроцессора:
обычно имеют первым символом # («решетку»);
распространяют свое действие от точки вхождения до конца файла.
Типичными директивами препроцессора являются:
#include — включает в текст файлы с исходным кодом;
#define — вводит в исходный код символические константы и
макроопределения (обратная директива — #undef);
#if, #ifdef, #ifndef, #else, #elif, #endif — реализуют условное включение
фрагментов исходного кода в текст, передаваемый компилятору;
#error — возбуждает ошибку времени компиляции;
#pragma — осуществляет действие, определяемое реализацией
компилятора.
19
20. Основы препроцессорной обработки:
пример использования #define
#define SIZE 100
#define PRINT_X printf("X:t%7dn", &x)
#define CUBE(N) (N) * (N) * (N)
int a[SIZE];
// эквивалентно: int a[100];
PRINT_X;
// эквивалентно: printf("X:t%7dn", &x);
printf("%dn", CUBE(SIZE));
// эквивалентно:
// printf("%dn", 100 * 100 * 100);
20
21. Основы препроцессорной обработки:
пример условной компиляции и #error
// выбор генератора псевдослучайных чисел
#ifdef ANSI_C_LIKE
#define A 1103515245
#define C 12345
#else
#define A 22695477
#define C 1
#endif
// выброс ошибки, если компилятор не является компилятором C++
#ifndef __cplusplus
#error A C++ compiler is required!
#endif
21
22. Вопросы управления памятью и
производительность кода: зачем?
Неоптимальная работа с
памятью становится
ограничивающим фактором
для большинства программ.
Проблему усугубляет рост
сложности подсистемы памяти,
в частности — механизмов
кэширования и пр.
22
23. Модели управления памятью
и области видимости объектов данных
Предлагаемые языком C модели управления объектами данных
(переменными) закреплены в понятии класса памяти, которое
охватывает:
время жизни — продолжительность хранения объекта в памяти;
область видимости — части исходного кода программы, из которых
можно получить доступ к объекту по идентификатору;
связывание — части исходного кода, способные обращаться к объекту
по его имени.
Для языка C характерны три области видимости:
блок — фрагмент кода, ограниченный фигурными скобками (напр.
составной оператор), либо заголовок функции, либо заголовок
оператора for, while, do while и if;
прототип функции;
файл.
23
24. Связывание объектов данных
Объекты данных, видимые в пределах блока и прототипа
функции, связывания не имеют (замкнуты в областях, где
определены).
Для объектов, видимых в пределах файла (глобальных), язык
предлагает два варианта связывания:
внутреннее — объект является «приватным» для файла и может
использоваться лишь в нем (но любой функцией!);
внешнее — объект может использоваться в любой точке
многофайловой программы.
24
25. Связывание объектов данных: пример
// область видимости: прототип функции
int foo(double *d, int n);
// область видимости: блок
for(int i = 0; i < n; i++) {
int bar;
// ...
}
// область видимости: файл, внутреннее связывание
static int count = 0;
// область видимости: файл, внешнее связывание
double accuracy = 0.001;
25
26. Время жизни объектов данных
Объекты данных в программах на языке С имеют статическую
или автоматическую продолжительность хранения:
время жизни статических объектов тождественно времени выполнения
программы;
время жизни автоматических объектов в целом тождественно времени
выполнения охватывающего их блока.
Статическими являются, главным образом, объекты, видимые в
пределах файла. Спецификатор static при таком объекте
определяет только тип связывания, но не время жизни объекта.
Автоматическими является большинство объектов, видимых в
пределах блока.
26
27. Инициализация объектов данных
Статические объекты неявно инициализируются нулем
(0, '0‘), автоматические объекты неявно не инициализируются
вообще.
Для явной инициализации статических объектов должны
использоваться константные выражения, вычислимые
компилятором.
Например:
char
space
size_t int_sz
= 0x20;
// верно
= sizeof(int);
// верно
size_t int10_sz = 10 * int_sz;
// неверно
27
28. Классы памяти в языке C
Класс памяти
Время жизни
Область
видимости
Тип
связывания
Точка
определения
Автоматический Автоматическое
Блок
Отсутствует
В пределах блока,
опционально auto
Регистровый
Блок
Отсутствует
В пределах блока,
register
Статический, без Статическое
связывания
Блок
Отсутствует
В пределах блока,
static
Статические, с
внешним
связыванием
Статическое
Файл
Внешнее
Вне функций
Статические, с
внутренним
связыванием
Статическое
Файл
Внутреннее
Вне функций,
static
Автоматическое
28
29. Автоматические и регистровые
переменные: пример
// автоматические переменные
int foo(unsigned u)
{
auto int bar = 42;
// ...
}
// регистровые переменные
int get_total(register int n)
{
// ...
for(register int i = 0; i < n; i++)
// ...
}
29
30. Размещение объектов данных
на регистрах процессора
Применение ключевого слова register для активно
используемых переменных:
несет все риски «ручной оптимизации» кода и полезно преимущественно
для встроенных систем и аппаратных архитектур, не имеющих
компиляторов C с долгой историей (gcc разрабатывается с 1987 г.);
относится к регистрам ЦП (в x86/x86-64: AX, EBX, RCX и т.д.), но не кэшпамяти ЦП 1-го или 2-го уровня;
является рекомендацией для компилятора, но не требованием к нему;
вполне может игнорироваться компилятором, который будет
действовать «на свое усмотрение» (например, разместит переменную на
регистре, потребность в котором возникнет позднее всего).
Операция взятия адреса переменной со спецификатором
register недопустима вне зависимости от того, размещена ли
она фактически на регистре.
30
31. Статические объекты с внутренним
связыванием и без связывания: пример
// без связывания:
// статические внутренние объекты функций
int callee(int n)
{
static int counter = 0; // не часть функции!
// …
}
// с внутренним связыванием:
// статические внутренние объекты файлов
static double epsilon = 0.001;
int foo(double accuracy)
{
if(accuracy < epsilon) // ...
}
31
32. Статические объекты с внешним
связыванием: пример
// с внешним связыванием:
// статические внешние объекты ("внешняя память")
double
time;
// внешнее определение
long int
fib[100]; // внешнее определение
extern char space;
// внешнее описание
// (объект определен в другом файле)
int main(void)
{
extern double time;
extern long int fib[];
// необязательное описание
// необязательное описание;;
// размер массива необязателен
// ...
}
32
33. Классы памяти функций
Применительно к невстраиваемым функциям различают два
класса памяти:
внешний — выбирается компилятором по умолчанию и позволяет
ссылаться на функцию (вызывать ее) из любой точки многофайловой
программы;
статический — выбирается при наличии спецификатора static и
позволяет изолировать функцию в том файле, где она определена.
Например:
int one(void);
// внешнее определение
// статическое определение
static int two(void);
// необязательное внешнее описание
extern int three(void);
33
34. Операция sizeof и тип size_t
Унарная операция sizeof:
допускает скобочную и бесскобочную (только для переменных) нотацию:
sizeof a или sizeof(T);
возвращает объем памяти, выделенной под объект простого или
составного типа, в байтах как значение переносимого типа size_t,
являющегося псевдонимом одного из базовых беззнаковых целых типов
(ср. int32_t и пр.).
не учитывает возможного выравнивания объекта.
Использование вычисляемых компилятором конструкций вида
sizeof(T) не влияет на производительность кода, но повышает
переносимость.
34
35. Указатели и арифметика указателей.
Тип ptrdiff_t
Стандартные указатели типа T* как составной тип языка C и
символический способ использования адресов можно условно
считать «шестым классом памяти», важной особенностью
которого является поддержка специфической арифметики.
Пусть p, p2 — указатели типа T*, а n — значение целого типа
(желательно — ptrdiff_t). Тогда:
p + n либо n + p — адрес, смещенный относительно p на n единиц
хранения размера sizeof(T) в направлении увеличения адресов
(«вправо»);
p – n — адрес, смещенный относительно p на n единиц хранения
размера sizeof(T) в направлении уменьшения адресов («влево»);
p++ либо ++p, p-- либо --p — аналогичны p + 1 и p – 1, соответственно;
p – p2 — разность содержащихся в указателях адресов, выраженная в
единицах хранения и имеющая тип ptrdiff_t. Разность положительна при
условии, что p расположен в пространстве адресов «правее» p2.
35
36. Одномерные массивы (строки)
Для одномерного массива T a[N] в языке C справедливо:
массивы поддерживают полную и частичную инициализацию, в том числе
с помощью выделенных инициализаторов;
в частично инициализированных массивах опущенные значения
трактуются как нули;
элементы массивов размещаются в памяти непрерывно и занимают
смежные адреса, для обхода которых может использоваться арифметика
указателей;
строки char c[N] конструктивно являются частными случаями массивов,
при этом в корректных строках c[sizeof(c) – 1] == ‘0‘;
sizeof(a) возвращает размер массива в байтах (не элементах!);
sizeof(a[0]) возвращает размер элемента в байтах.
Принятая система обозначения массивов является лишь
особым способом применения указателей.
36
37. Одномерные массивы (строки): пример
// с освобождением круглых скобок
int a[] = {1, 2, 3};
// эквивалентно int a[3] = {1, 2, 3};
// с частичной неявной инициализацией
int b[5] = {1, 2, 3};
// эквивалентно:
// int b[5] = {1, 2, 3, 0, 0};
// с выделенными инициализаторами
int c[7] = {1, [5] = 10, 20, [1] = 2};
// эквивалентно:
// int c[7] = {1, 2, 0, 0, 0, 10, 20};
37
38. Одномерные массивы (строки)
и указатели
Пусть T a[N] — массив. Тогда:
имя массива является константным указателем на 0-й элемент:
a == &a[0];
для любых типов и длин массивов справедливо:
&a[i] == a + i и a[i] == *(a + i);
С учетом этого эквивалентны прототипы:
int foo(double [], int);
int foo(double *, int);
Передать массив в функцию можно так, как показано выше, или
как пару указателей: на 0-й и N-й элементы (обращение к
элементу a[N] без его разыменования допустимо):
int foo(double *, double *);
38
39. Макроопределение NULL
Стандартное макроопределение NULL расширяется
препроцессором до константы с семантикой «пустого»
указателя, который…
является константным целочисленным выражением, вычисляемым в
длинный или короткий нуль (0L или 0), либо
выступает как результат приведения такого значения к void*
(напр. (void*)0).
Значение NULL приводимо к любому типу-указателю и может
использоваться в конструкциях вида:
if (p != NULL) // ...
if (q == NULL) // ...
39
40. Вопросы безопасного
программирования
Инициализировать указатели во время определения:
допустимый адрес;
0, (void*)0 или NULL.
Проверять:
значения указателей перед их разыменованием;
значения индексов элементов массивов перед использованием;
возвращаемые значения стандартных функций после их вызова.
40
41. Стандартные функции ввода-вывода
Имя
функции
Назначение функции
Причины
ошибок
POSIXсовместима?
int scanf(const char *restrict format, ... );
scanf
Осуществляет форматный ввод с консоли —
чтение из стандартного входного потока
stdin. Возвращает количество успешно
считанных элементов ввода
Некорректная Да
входная
послед-сть
(EILSEQ)
Недостаточно
аргументов
(EINVAL)
int printf(const char *restrict format, ...);
printf
Осуществляет форматный вывод в консоль
— запись в стандартный выходной поток
stdout. Возвращает количество
переданных в поток байт
EILSEQ,
EINVAL и др.
Да
41
42. Стандартные функции для работы
с динамической памятью (1 / 2)
Имя
функции
Назначение функции
Причины
ошибок
POSIXсовместима?
Недостаточно
памяти
(ENOMEM)
Да
Недостаточно
памяти
(ENOMEM)
Да
void *malloc(size_t size);
malloc
Выделяет неиспользуемый участок памяти
объекту данных размера size байт, не
меняя содержимое указанного участка
void *calloc(size_t nelem, size_t elsize);
calloc
Выделяет неиспользуемый участок памяти
массиву из nelem элементов размера
elsize байт каждый и выполняет его
поразрядное обнуление
42
43. Стандартные функции для работы
с динамической памятью (2 / 2)
Имя
функции
Назначение функции
Причины
ошибок
POSIXсовместима?
Недостаточно
памяти
(ENOMEM)
Да
void *realloc(void *ptr, size_t size);
realloc
Изменяет размер объекта данных, на
который указывает ptr, до size. Если
указатель ptr пуст, вызов эквивалентен
malloc. Если size == 0, память под
объектом освобождается
void free(void *ptr);
free
Вызывает освобождение памяти, на которую Нет
указывает ptr, делая ее доступной для
нового выделения. Дальнейшее
использование ptr влечет неопределенное
поведение
Да
43
44. Выравнивание объектов, размещаемых
статически. GCC-атрибут aligned (1 / 2)
Одним из способов повышения производительности программы на
языке «среднего уровня» является такое размещение данных в ОЗУ,
при котором они эффективно загружаются в кэш-память ЦП. Для
этого данные должны быть, как минимум, выровнены на границу
линии кэш-памяти данных 1-го уровня (L1d).
Выравнивание объекта данных в ОЗУ обычно определяется
характеристиками выравнивания, которые имеет соответствующий
тип данных. При этом:
выравнивание скалярного объекта определяется собственные
характеристикой выравнивания приписанного ему базового типа;
выравнивание массива, — если размер каждого элемента не кратен
величине выравнивания, — распространяет свое действие только на
элемент с индексом 0;
выравнивание объектов в программе на языке C может регулироваться на
уровне отдельных переменных и типов данных., для чего в компиляторе
GCC служит атрибут aligned.
44
45. Выравнивание объектов, размещаемых
статически. GCC-атрибут aligned (2 / 2)
Выравнивание статически размещаемых переменных имеет силу как
для глобальных, так и для автоматических переменных. При этом
характеристика выравнивания, присущая типу объекта, полностью
игнорируется.
При выравнивании массивов гарантированно выравнивается только
начальный, нулевой элемент массива.
45
46. Выравнивание объектов, размещаемых
статически. Атрибут aligned: пример
// выравнивание, регулируемое на уровне объекта
// переменная qwd выравнивается на границу 64 байт
uint64_t qwd __attribute((aligned(64)));
// выравнивание, регулируемое на уровне типа
// переменные типа al128int_t (синоним int)
// выравниваются на границу 128 байт
typedef
int __attribute((aligned(128))) al128int_t;
al128int_t aln;
46
47. Выравнивание объектов, размещаемых
динамически. Функция posix_memalign
Функция posix_memalign:
определена в стандарте POSIX 1003.1d;
имеет прототип
int posix_memalign(void **memptr, size_t alignment, size_t size);
выделяет неиспользуемый участок памяти размера size байт,
выровненный на границу alignment, и возвращает указатель на него в
memptr;
допускает освобождение выделенной памяти функцией free().
Требования к значению alignment:
кратно sizeof(void*);
является целочисленной степенью числа 2.
Ошибки (EINVAL, ENOMEM):
значение alignment не является кратной sizeof(void*) степенью 2;
недостаточно памяти.
47
48. posix_memalign: пример (1 / 2)
int b[7] = {1, [5] = 10, 20, [1] = 2}; // массив-источник
int *p = NULL,
errflag;
// массив-приемник
// код ошибки posix_memalign
// установить размер линии кэш-памяти данных 1-го уровня
// (L1d); типичное значение: 64 байта
long l1dcls = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
// проверить, удался ли вызов sysconf()
if (l1dcls == -1)
// если вызов sysconf() неудачен, использовать значение
// выравнивания по умолчанию
l1dcls = sizeof(void*);
48
49. posix_memalign: пример (2 / 2)
// выделить память с выравниванием на границу строки L1d
errflag = posix_memalign((void**)&p, l1dcls, sizeof b);
if(!errflag)// в случае успеха posix_memalign возвращает 0
{
printf("nL1d cache line size is %ldn", l1dcls);
printf("p and &p are %p and %pn", p, &p);
p = memcpy(p, b, sizeof(b));
// ...
free(p);
}
else
printf("posix_memalign error: %dn", errflag);
49
50. Практикум №1
Постановка задачи
Решить индивидуальные задачи №№1 и 2 в соответствии с
формальными требованиями.
Для этого в блоге дисциплины:
узнать номер индивидуального варианта;
узнать постановку задач.
50