Смекни!
smekni.com

Основы программирования на языке Си (стр. 17 из 27)

Введите первую строку: Строка 1.

Введите вторую строку: Строка 2.

До копирования строки были не одинаковыми.

После копирования строки стали одинаковыми.

После конкатенации первая строка равна: Строка 2.Строка 2.

6. Сводка результатов

Набор однотипных переменных можно представить в виде массива. Массивы

можно передавать в качестве параметров внутрь функций. В лекции описан простой

алгоритм сортировки массивов методом выбора наименьшего элемента. Кратко рас-

смотрены приемы работы с двумерными массивами.

Символьные строки в Си++ хранятся в виде символьных массивов, последний

символ в которых является служебным нуль-символом. В стандартной библиотеке

Си++ есть специальные функции для обработки символьных строк.

7. Упражнения

Упражнение 1

Напишите библиотеку функций для работы с целочисленными массивами.

Прототипы _______функций поместите в заголовочный файл "IntegerArray.h", а опреде-

ления в файл реализации "IntegerArray.cpp". Библиотека должна содержать

следующие функции:

"input_array(a,n)" для ввода с клавиатуры первых n значений массива a.

"display_array(a,n)" для вывода на экран первых n значений массива a.

"copy_array(a1,a2,n)" для копирования первых n элементов массива a2 в соответ-

ствующие элементы массива a1.

"standard_deviation(a,n)", которая вычисляет и возвращает среднеквадратическое

отклонение первых n элементов массива a. (Для реализации этой функции может

пригодиться функция "average(a,n)" из программы 2.1.) Формула для вычисления

среднеквадратического отклонения дана в упражнении 3 из лекции 3.

Проверьте разработанные функции с помощью подходящей тестовой программы.

74

Упражнение 2

Из функции "selection_sort(...)", рассматривавшейся в лекции, сделайте

функцию сортировки символьной строки "string_sort(...)". У этой функции один

параметр строка, которая должны быть отсортирована в алфавитном порядке (точ-

нее, по возрастанию ASCII-кодов, при этом все прописные буквы будут помещены до

строчных). Позиция завершающего нуль-символа при сортировке не меняется. Про-

верьте работу функции с помощью тестовой программы, которая должна выводить на

экран сообщения:

Введите строку: Вася Иванов

Отсортированная строка равна: ВИааввнося

Упражнение 3

Напишите функцию "no_repetitions(...)", которая удаляет из строки все по-

вторяющиеся символы. Напишите тестовую программу для проверки этой функции,

которая воспроизводит следующий диалог с пользователем:

Введите строку: Эта строка содержит повторяющиеся символы

Строка без повторяющихся символов равна: Эта срокдежипвяющмлы

Подсказка: Как и многие задачи программирования, эту задачу легче решить с по-

мощью процедурной абстракции.

Упражнение 4

С использованием двумерных массивов напишите функцию (и соответствую-

щую тестовую программу) для умножения целочисленной матрицы размером mxn на

матрицу размером nxr. В тестовой программе для задания значений m, n и r заведите

глобальные константы. Ниже приведен пример сообщений, выдаваемых программой:

Введите первую матрицу (размер 2x2):

Введите 2 значения для 1-й строки (через пробелы): 3 4

Введите 2 значения для 2-й строки (через пробелы): 5 7

Введите вторую матрицу (размер 2x2):

Введите 2 значения для 1-й строки (через пробелы): 1 1

Введите 2 значения для 2-й строки (через пробелы): 2 2

3 4

5 7

УМНОЖИТЬ

1 1

2 2

РАВНО

11 11

19 19

75

ЛЕКЦИЯ 7. Указатели

1. Назначение указателей

Язык Си++ унаследовал от языка Си мощные средства для работы с оператив-

ной памятью: динамическое выделение и освобождение блоков памяти, доступ к от-

дельным ячейкам памяти по их адресам. Эти возможности делают язык Си++, как и

Си, удобным для разработки системного программного обеспечения и прикладных

программ, в которых применяются динамические структуры данных (т.е. такие, раз-

мер которых не известен на этапе компиляции и может меняться во время выполне-

ния программы).

Во всех программах из предыдущих лекций переменные объявлялись так, что

компилятор резервировал для каждой из них некоторое количество памяти (в соот-

ветствии с типом данных) еще на этапе компиляции. В начале выполнения блока опе-

раторов, внутри которого объявлена переменная, автоматически выполняется выде-

ление памяти для этой переменной, а при выходе из блока освобождение памяти.

В данной лекции подробно рассматривается понятие указателя, средства, ко-

торое дает программисту наиболее гибкий способ контроля операций выделе-

ния/освобождения памяти во время выполнения программ.

1.1 Объявление указателей

Указателем называется адрес переменной в оперативной памяти. Переменная

указательного типа (часто она называется переменная-указатель или просто указа-

тель) это переменная, размер которой достаточен для хранения адреса оперативной

памяти. Переменные-указатели объявляются с помощью символа "*", который добав-

ляется после названия обычного типа данных. Например, оператор описания (его

можно прочитать как "указатель на целочисленную переменную"):

int* number_ptr;

объявляет переменную-указатель "number_ptr", которая может хранить адреса пере-

менных типа "int".

Если в программе используется много однотипных указателей, то для их объ-

явления можно ввести новый тип. Это делается с помощью оператора описания ново-

го типа "typedef". Например, если записать оператор:

typedef int* IntPtrType;

то в программе можно будет объявлять сразу несколько переменных-указателей, не

применяя для каждой из них символ "*":

IntPtrType number_ptr1, number_ptr2, number_ptr3;

1.2 Применение символов "*" и "&" в операторах присваивания значений указателям

В операторах присваивания допускается совместное использование обычных и

указательных переменных одного типа (например, "int"). Для получения значения

переменной по ее указателю предназначена операция разыменования указателя "*". У

нее есть обратная операция операция взятия адреса "&". Упрощенно, операция "*"

означает "получить значение переменной, расположенной по этому адресу", а опера-

ция "&" "получить адрес этой переменной". Для пояснения смысла этих операций

далее приводится программа 1.1.

76

#include <iostream.h>

typedef int* IntPtrType;

int main()

{

IntPtrType ptr_a, ptr_b;

int num_c = 4, num_d = 7;

ptr_a = &num_c; /* СТРОКА 10 */

ptr_b = ptr_a; /* СТРОКА 11 */

cout << *ptr_a << " " << *ptr_b << "&bsol;n";

ptr_b = &num_d; /* СТРОКА 13 */

cout << *ptr_a << " " << *ptr_b << "&bsol;n";

*ptr_a = *ptr_b; /* СТРОКА 15 */

cout << *ptr_a << " " << *ptr_b << "&bsol;n";

cout << num_c << " " << *&*&*&num_c << "&bsol;n";

return 0;

}

Программа 1.1.

Программа 1.1 выдает на экран следующие сообщения:

4 4

4 7

7 7

7 7

Графически состояние программы 1.1 после выполнения операторов присваи-

вания в 10-11-й строках, 15-й и 19-й показано, соответственно, на рис. 1, 2 и 3.

Рис. 1.. Состояние про-

граммы 1.1 после выпол-

нения 10-й и 11-й строк.

Рис. 2.. Состояние про-

граммы 1.1 после выпол-

нения 13-й строки.

Рис. 3.. Состояние про-

граммы 1.1 после выпол-

нения 15-й строки.

Операции "*" и "&", в некотором смысле, являются взаимно обратными, так что

выражение "*&*&*&num_c" означает то же самое, что и "num_c".

1.3 Операторы "new" и "delete". Константа "NULL"

Рассмотрим оператор присваивания в 10-й строке программы 1.1:

ptr_a = &num_c;

Можно считать, что после выполнения этого оператора у переменной "num_c"