Основные элементы языка программирования Си алфавит, идентификаторы, ключевые слова, константы
Скачать 0.78 Mb.
|
Ниже приведен пример динамического задания вектора и матрицы (используется vector) #include #include #include using namespace std; int main() { // Создание вектора размерности n и заполнение его псевдослучайными числами int n; cout << "n="; cin >> n; vector for (auto &it: v) it = rand() % 100; // Псевдослучайные числа в интервале 0..99 for (auto it: v) cout << it << ' '; // Печать // Создание матрицы n x m и заполнение псевдослучайными числами int m; cout << endl << "n="; cin >> n; cout << "m="; cin >> m; vector for (int i = 0; i < n; ++i) { M[i].resize(m); // Новый размер for (int j = 0; j < m; j++) M[i][j] = (rand() % 10000) / 100.; // Дробная часть не 0 } // Печать матрицы for (int i = 0; i < M.size(); ++i) { for (int j = 0; j < M[i].size(); ++j) cout << fixed << setw(7) << setprecision(2) << M[i][j]; cout << endl; } // Другой способ просмотра элементов матрицы for (auto pos: M) { for (auto pos2: pos) cout << fixed << setw(7) << setprecision(2) << pos2; cout << endl; } } 17.Шаблон класса std::string (Строка). Основные методы, пример использования. Класс std::string (cтрока) – реализация шаблона std::basic_string Некоторые методы класса (перегруженные операции): size() или length() Получить длину строки (без нулевого символа) empty() Проверяем, является ли строка пустой == != Можно использовать для сравнения строк ++= Можно складывать строки (соединение строк) c_str() Возвращает указатель на символьный массив at() [] Обращение к элементу по индексу Примеры работы со string: #include using namespace std; int main() { string str; cout << "Enter your name: "; getline(std::cin, str); cout << "Hello, " << str << "!!! \n"; string str1 = "Hello"; string str2 = " World"; string str3 = str1 + str2; cout << endl << str3 << endl; for (int i = 0; i < str3.length(); i++) cout << str3[i] << ' '; cout << endl; for (auto pos: str3) cout << pos << ' '; string str4 = str1; str4 += str2; if (str3 == str4) cout << endl << "str3==str4"; else cout << endl << "str3!=str4"; const char *str_old = str.c_str(); // Старый указатель на строку return 0; } 18.Цикл «range-based for loop», использование в нем ключевого слова auto. for (auto ar_i : arr1) cout << ar_i << endl; Новая разновидность цикла Си++, так называемый Range-based for loop (или «цикл, основанный на диапазоне»). Ключевое слово auto позволяет определять тип автоматически, если это можно сделать. В данном случае это тип элемента массива. Можно было записать: for (int ar_i : arr1) cout << ar_i << endl; 19.Структуры и объединения в языке Си. Структура — это производный тип языка Си, включающий в себя множество элементов, элементы следуют в памяти друг за другом и могут быть разнотипными. Формат определения структуры, следующий: struct [<имя_структурного_типа>] { ..... // Объявления полей структуры } [<имя_переменной_струрного_типа>]; Пример: struct A { int x; // Поле структуры float y; // Поле структуры }; После объявления структуры имя структуры является именем нового типа. Аналогично переменным стандартных типов могут объявляться переменные структурного типа, при этом каждая переменная структурного типа имеет свои копии полей структуры в оперативной памяти. Также можно объявлять указатели на структуры, массивы структур, полями структуры могут быть массивы или указатели. A a1, a2; // Объявление переменных структурного типа Примечание. Такой формат определения переменных структурного типа разрешен в языке Си++ в исходном Си стандарта ANSI требуется при подобном объявлении переменных структурного типа дополнительно указывать ключевое слово struct: struct A a1, a2; // Объявление переменных структурного типа в Си стандарта ANSI Разрешено объявлять переменные структурного типа одновременно с объявлением структуры, и, если далее переменные этого типа не будут создаваться, то имя структуры может отсутствовать. struct A { int x; // Поле структуры float y; // Поле структуры } a1, a2; // Объявление переменных одновременно со структурой Или struct // Имя структуры отсутствует, далее переменные этого типа не могут создаваться { int x; // Поле структуры float y; // Поле структуры } a1, a2; // Объявление переменных одновременно со структурой Обращение к полям структуры производится с помощью операции «.» - обращение к полю структуры через имя переменной, или операции «->» -обращение к полю структуры через указатель. a1.x=10; a1.y=1.5; A *pA=&a1; // Указатель на структуру pA->x=11; // или (*pA).x=11; Инициализация структуры Переменная структурного типа может быть определена с инициализацией полей. Инициализация структуры похожа на инициализацию массива. Например: struct BOOK // Структура описывает некоторую книгу { 10 char * author; // Имя автора книги char *title; // Заголовок книга char *firm; // Название издательства int year, page; // Год издания и число страниц }; BOOK book1={ “Керниган Б., Ритчи Д.”, “Язык программирования С”, “ М.: Издательский дом «Вильямс»”, 2009, 304}; Расширение понятие структуры – класс Класс – это производный тип языка Си++, который включается в себя данные (поля класса) и методы (функции класса) для обработки данных. Класс – это расширение понятия структуры, по сути, это структура, в которую дополнительно введены методы (функции класса). Определение функции класса похоже на определение обычной функции. Внутри функции класса можно обращаться к полям класса или вызывать другие функции класса просто по имени. За пределами класса функция класса вызывается через переменную типа класса, используя операцию «.» (точка) или через операцию «->» если используется указатель. Переменная, имеющая тип класса, называется объектом (термин «Объектно-ориентированное программирование»). Пример простейшего класса point, задающего точку на плоскости: #include using namespace std; struct point { double x, y; // Координаты точки (поля) void print() // Метод класса печатает поля { cout << "x=" << x << " y=" << y << endl; } // Методы для задания значений полям void setX(double x_par) { x = x_par; // К полю класса обращаемся просто по имени } void setY(double y_par) { y = y_par; } }; int main() { point Ob1, Ob2; // Объявляем два объекта класса // Задаем значения полей и печатаем Ob1.setX(12.233); Ob1.setY(10.2424); Ob2.setX(1.1111); Ob2.setY(2.2222); Ob1.print(); Ob2.print(); point *pOb = &Ob1; pOb->print(); // Вызов метода через указатель return 0; } 20.Битовые поля структур и объединений в языке Си. Битовое поле — это поле структуры или объединения, представляющее собой целое или беззнаковое целое значение (любые стандартные целые типы), занимающее в памяти фиксированное число битов (не более, чем соответствующий целый тип), размер битового поля задается в битах и не обязательно кратен целому числу байтов. С битовыми полями можно работать так же, как с обычными целыми полями, только следует учитывать их диапазон. Для битовых полей не определена операция &, не существует указателей на битовые поля. Пример структуры с битовыми полями: struct A { int a1: 10; // Битовое поле из 10 бит int a2: 14; // Битовое поле из 14 бит …..}; Далее создаем переменную структурного типа и можем обращаться к битовым полям A a; a.a1=125; Все, что связано с битовыми полями является системно зависимым. Например, только в конкретной реализации определяется, могут ли поля перекрывать границы слов. В некоторых компиляторах можно поля в структурах выравнивать по границам слов или осуществлять плотную упаковку. На уровне правил языка Си (Си++) можно пропускать заданное число бит, если использовать битовое поле без имени. struct B { int a1:12; int a2:14; int :6; // Пропускаем 6 бит ….}; Приведем пример объединения, в котором первое поле занимает 1 байт, а второе поле является переменной структурного типа, включающей 8 битовый поле, каждое по 1 биту. Тем самым можно работать с 1 байтом, как с целым числом или обращаться к отдельным битам этого байта. #include union MyByte { unsigned char byte; struct { unsigned char b1: 1; unsigned char b2: 1; unsigned char b3: 1; unsigned char b4: 1; unsigned char b5: 1; unsigned char b6: 1; unsigned char b7: 1; unsigned char b8: 1; } bits; }; void main() { union MyByte B; // Заполняем отдельные биты B.bits.b1 = 1; B.bits.b2 = 1; B.bits.b3 = 0; B.bits.b4 = 0; B.bits.b5 = 1; B.bits.b6 = 0; B.bits.b7 = 1; B.bits.b8 = 0; printf("%x", B.byte); // Будет напечатано 53 в шестнадцатиричной СС или 01010011 } 21.Определение, описание и вызов функции в языке Си. Формат определения функции: [<модификаторы>] <тип_возвращаемого_значения> <имя_функции>([<спецификация_формальных_параметров>]) { <тело_функции> } Спецификация формальных параметров в круглых скобках определяет параметры функции, один параметр отделяется от другого запятой. Спецификация формальных параметров имеет следующий формат: <тип1> [<имя1>][=<умалчиваемое_значение>], <тип2> [<имя2>][=<умалчиваемое_значение>],… Формальные параметры функции могут отсутствовать в этом случае после имени функции идут пустые скобки (или в скобках может быть ключевое слово void). Имя формального параметра может отсутствовать, если этот параметр пока не используется в теле функции, но зарезервирован на будущее. Тело функции является блоком, в нем локализованы формальные параметры заголовка. Важным оператором тела функции является оператор return, наличие данного оператора обязательно, если тип возвращаемого значения отличен от void. Если функция возвращает некоторое значение, то она может вызываться внутри оператора- выражения в любом месте, где разрешено использовать значение данного типа. В качестве модификаторов функции может присутствовать модификатор класса памяти extern или static. По умолчанию у функции класс памяти extern. Если функция определена с ключевым словом static, то функцию можно использовать только в том файле, где она определена. Функция может быть определена только один раз, определение функции может находиться в отдельном файле, но чтобы ее использовать в другом файле (или в том же файле, но выше места определения) необходимо до вызова функции включить описание функции. Описание функции, по сути, является заголовком функции без тела, оно имеет формат: [<модификаторы>] <тип_возвращаемого_значения> <имя_функции> (<спецификация_формальных_параметров>); Совокупность формальных параметров определяет сигнатуру функции. В описании имена параметров можно не использовать, важно количество и типы параметров. Также в описании не должны присутствовать умалчиваемые значения параметров, если они есть в определении функции. Пример простой функции, которая возвращает сумму двух свои параметров: double summa(double x, double y) { return x+y; } Описание или заголовок этой функции имеет вид: double summa(double, double); Вызов функции имеет следующий формат (вызов функции часто выполняется внутри оператора- выражения): <имя_функции>(<список_факт_параметров>); При вызове функции вместо формальных параметров подставляются фактические параметры (переменные, константы, указатели и др.) и выполняются операторы тела функции со значениями фактических параметров. Следует отметить, что функция может вызываться «как функция», когда используется возвращаемое значение, и функция вызывается внутри выражения: S=summa(a, b); // Переменной S присваивается значение, возвращаемое функцией Функция может вызываться «как процедура», когда возвращаемой значение не используется (если оно вообще не нужно, можно объявить тип void): printf(“x=%f”, x); // Функция printf возвращает значение типа int, но оно не используется 22.Изменение значений скалярных параметров в функциях. Передача параметров в функции по значению: Рассмотрим пример, когда функция пытается изменить значение своего параметра. #include using namespace std; void MyFun(int a) { a = 100; } int main() { int a = 10; MyFun(a); cout << "a=" << a << endl; // Будет напечатано a=10 return 1; } В данном примере несмотря на то, что функция меняет значение параметра, на печать будет выведено старое значение фактического параметра – переменной a, т.е. изменение, которое произвела функция с параметром, после выхода из функции не сохраняется. Это происходит потому, что при передаче параметров в функцию создаются копии параметров в стеке (также все локальные переменные функции размещаются в стеке), и функция работает не с исходной переменной, а с копией этой переменной в стеке. При выходе из функции место в стеке, где размещены параметры функции и ее локальные переменные, освобождается и все изменения пропадают. Данный процесс демонстрирует рисунок, представленный ниже. Примечание. Стек (англ. stack — стопка) — структура данных, в которой доступ к элементам организован по принципу LIFO (англ. last in — first out, «последним пришёл — первым вышел»). В оперативной памяти выделяется специальный сегмент стека для временного хранения данных, который работает по такому же принципу. На рисунке показано, что исходная переменная a может размещаться как в стеке – в данном примере, так оно и есть, так как переменная a локальная переменная функции main, так и за пределами стека, если бы a была бы глобальной переменной, в данном случае это не имеет значение, так как все равно функция работает с копией a. Такой механизм передачи параметров в функцию называется передача параметров по значению (в стек копируется значение параметра). Передача параметров в функции по указателю (по ссылке): Рассмотрим, как можно добиться того, чтобы функция могла изменить значение параметра, и это изменение сохранялось бы после выхода из функции. Изменим немного представленный пример. #include using namespace std; void MyFun(int *pa) { *pa = 100; } int main() { int a = 10; MyFun(&a); cout << "a=" << a << endl; // Будет напечатано a=10 return 1; } В этом примере значение переменной a будет изменено, так как в этом случае в стек помещается не копия переменной a, а адрес переменной, при выполнении операции обращение по адресу внутри функции, новое значение будет записано по адресу исходной переменной а и переменная будет изменена. Данный процесс демонстрируется на рисунке. Такой механизм передачи параметров называется передача параметров по указателю. Таким образом можно сделать вывод, для того чтобы функция могла изменить значение параметра, параметр должен передаваться по указателю, т.е. в функцию необходимо передавать адрес исходного объекта (переменной). Примечание. Поэтому в стандартной функции ввода scanf и некоторых других функциях при вызове передается адрес переменной (используется операция &). В языке Си++ появился новый тип – ссылки. В некоторых случаях их удобно использовать вместо указателей в качестве параметров функции, так как ссылка, по сути, является указателем, но к ней не надо применять операцию обращения по адресу («*»). В представленном ниже примере функция также меняет значение параметра. #include using namespace std; void MyFun(int &la) // Параметр функции - ссылка { la = 100; // Не нужно применять операцию – обращение по адресу } int main() { Оперативная память ….. ….. ….. Стек Переменная a = 10, 100 pa - адрес a *pa = 100 int a = 10; MyFun(a); cout << "a=" << a << endl; // Будет напечатано a=10 return 1; } |