6 Структурированные типы данных 2012
.pdf1
6Структурированные типы данных
6.1Массивы
Массив (регулярный тип данных) – структурированный тип данных, описывающий упорядоченную последовательность элементов одного базового типа. Доступ к элементам массивов производится с помощью имени переменной и целочисленных индексов, указываемых в квадратных скобках. Наименьшее значение индекса у первого элемента массива, а наибольшее – у последнего. В отличие от языка Паскаль в С нет отдельного идентификатора для описания массивов, для этого используется идентификатор базового типа и имя переменной, после которого в скобках указывается число элементов массива. В С и С++ первый элемент массива всегда соответствует нулевому значению - 0.
Различают три основных вида массивов:
1) одномерный массив (или вектор) – линейная последовательность элементов, адресуемых одним индексом. Они соответствуют матрицам-столбцам или матрицамстрокам; формат описания одномерного массива следующий
Базовый_тип имя_массива[размер];
Примеры описания одномерных массивов:
float vect[100]; |
/* |
массив из |
100 вещественных элементов */ |
int numbers[1000]; |
/* |
массив из |
1000 целочисленных элементов */ |
2) двухмерные массивы |
(матрицы), индексы которых показывают номер столбца |
и номер строки элемента; формат объявления массива
тип_массива имя_матрицы [размер_столбцов][размер_строк];
Примеры матриц: |
|
|
|
float Matr[4][5]; |
/* вещественная матрица из 4 строк |
и 5 столбцов */ |
|
int matrix[8][4]; |
/* целочисленная матрица в 8 строк |
и 4 столбцов */ |
|
3) многомерные массивы (тензоры) – адресация элементов |
которых |
||
производится тремя и более индексами; формат объявления N-мерного массива |
|
||
базовый_тип имя [размер_индексаN] … [размер_индекса2][размер_индекса1]; |
|||
Примеры: |
|
|
|
float M[10][10][10]; |
/* вещественный массив размерности |
10 x 10 x 10 */ |
|
Доступ к элементам массива производится с помощью |
указания |
имени |
|
переменной массива и номера (номеров) соответствующего индекса: |
|
|
Имя_массива[индекс];
Пример задания единичного значения первым элементам массивов
vect[0]=1; |
/* первый элемент вектора |
*/ |
|||
Matr[0][0]=1; |
/* |
первый |
элемент |
матрицы |
*/ |
M[0][0][0]=1; |
/* |
первый |
элемент |
трехмерного массива */ |
В С отсутствует проверка правильности задания границ массивов, поэтому следует внимательно задавать индексы в тексте программы. При задании номера большего размера массива, производится дальнейший отсчет от начала массива.
Пример использования массивов для вывода на печать первых 20 чисел
Фибоначчи на языке С++: |
|
#include <iostream.h> |
// подключение заголовочного файла |
const int size=20; |
// константа размера массива |
int vector[size]; |
// переменная-вектор |
int main() |
// главная функция программы |
{ int i; |
// переменная индекса |
cout << "\nЧисла Фибоначчи" << endl; |
|
vector[0] = vector[1] = 1; |
// начальная инициализация двух значений |
for (i=2; i<size; i++) |
// цикл расчета числа Фибоначчи |
{vector[i] = vector[i-1] + vector[i-2]; // расчет i-го числа cout << vector[i] << endl; // вывод значения i-го числа
|
} // |
конец тела цикла for |
|
|
return 0; |
// возврат кода успешного завершения |
|
} |
// |
конец main |
|
Пример программы расчета значений на интервале квадратной функции по
формуле y(x) = a x2 + b x + c,
с использованием векторов для значений аргумента и функции на языке С++
|
|
|
2 |
|
#include |
<iostream.h> |
// подключение заголовочного файла ввода-вывода |
||
#include |
<math.h> |
// подключение модуля математических ресурсов |
||
#include |
<conio.h> |
// подключение заголовочного файла консоли |
||
const |
size=20; |
// константа размера векторов |
|
|
float |
x[size]; |
// вектор аргумента (независимой переменной) |
||
float |
y[size]; |
// вектор функции (зависимой переменной) |
||
float |
a, |
b, c; |
// параметры расчетного выражения |
|
float |
xn, xk, hx; |
// начало, конец и шаг изменения аргумента |
||
int main() |
// заголовок главной функции |
|
||
{ cout << "a="; |
// вывод экранного запроса на |
ввод значения a |
||
cin |
>> |
a; |
// ввод с клавиатуры значения |
a |
cout << "b="; |
// вывод экранного запроса на |
ввод значения b |
||
cin |
>> |
b; |
// ввод с клавиатуры значения |
b |
cout << "c="; |
// вывод экранного запроса на |
ввод значения c |
||
cin |
>> |
c; |
// ввод с клавиатуры значения |
c |
cout << "начало х="; |
// вывод экранного запроса на |
ввод значения xn |
||
cin |
>> |
xn; |
// ввод с клавиатуры значения |
xn |
cout << "конец x="; |
// вывод экранного запроса на |
ввод значения xk |
||
cin |
>> |
xk; |
// ввод с клавиатуры значения |
xk |
hx = (xk-xn)/(size-1); |
// Определение шага изменения |
аргумента |
||
x[0]=xn; |
// инициализация первых элементов массивов |
|||
y[0]=a*pow(x[0],2)+b*x[0]+c; |
|
|
||
cout << "\nX=" << x[0] << " |
Y=" << y[0]; |
|
||
for |
(int i=1; i<size; i++) |
|
|
{x[i]=x[i-1]+hx; y[i]=a*pow(x[i],2)+b*x[i]+c;
cout << "\nX=" << x[i] << " Y=" << y[i] ;
}// конец тела цикла for
getch(); |
// |
приостановка программы |
return 0; |
// |
код успешного завершения |
}// -------- конец main ----------------------------------
Инициализация начальных значений массивов при их объявлении производится
после знака равенства с помощью фигурных скобок, внутри которых указываются значения элементов. Формат инициализации одномерного массива (вектора):
базовый_тип имя_массива[размер]={первый_элемент, …, последний_элемент }
Примеры
int vector[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; float row[3] = {0.12, 3.45, 6.78};
В этом примере элементы массивов имеют следующие значения
vector[0]=0, |
vector[1]=1, …, |
vector[9]=9 |
row[0]=0.12, |
row[1]=3.45, |
row[2]=6.78 |
Если требуется проинициализировать двухмерный массив, тогда используется |
||
следующая субагрегатная |
форма: |
|
тип массив [n][m]={{a0,0,… , a0,m}, …,{ an,0,… , an,m }};
Пример инициализации матрицы целочисленной матрицы в переменной M
|
|
|
1 |
2 |
3 |
|
|
|
4 |
5 |
6 |
7 |
8 |
9 |
|
|
|
int M[3][3]={{1,2,3},{4,5,6},{7,8,9}};
При объявлении переменной массива можно не указывать внутри квадратных скобок его размер. В этом случае имеет место безразмерный массив, размер которого определяется автоматически по мере заполнения. Пример безразмерного целочисленного вектора
int row[];
Допускается создавать двумерные и многомерные безразмерные массивы при условии, что у них определен хотя бы крайний справа размер. Например
float massa[][][2];
Над переменными-массивами целиком допустимы операции передачи их значений в качестве параметров функций и операции сравнения (равно или не равно). Все остальные операции выполняются над элементами массивов.
3
6.2 Строковый тип данных
Строковый тип данных используется для операций над строками текста. Значением строкового типа является последовательность символов с динамическим атрибутом длины (в зависимости от действительного числа символов при выполнении программы) и константным атрибутом предельного размера строки.
В С строковый тип задается неявно и представляется как одномерный символьный массив произвольной длины, оканчивающийся нулевым символом (\0). Поэтому при объявлении длины строки ее следует увеличивать на единицу по отношению к размеру хранимого текста.
Формат объявления короткого строкового типа char имя_строки [атрибут_длины_строки];
Примеры описания:
char str80[80]; /* Строка длиной 80 символов */ char message[16]; /* Строка длиной 16 символов */
Константы строкового типа в С и С++ заключаются с двух сторон в кавычки
(”): “символы строки”.
Примеры инициализации строковых констант:
char name[12]=”Borland C++”; |
|
|||
char |
error_1[20]=”Нет |
доступа к файлу”; |
||
char |
zero[1]=””; |
// |
Пустая |
строка |
Последний пример описывает пустую строку.
К отдельным символам в строке доступ аналогичен как к компонентам массива. В языке С с помощью заголовочного файла string.h над строками допустимы
стандартные операции, реализуемые с помощью функций:
-strcat(s1, s2) - сложение двух строк s1 и s2, результат помещается в первую строку s1;
-strcmp(s1, s2) - сравнение двух строк;
-strlen(s) – возвращает длину строки в символах;
-strcopy(s1, s2) – копирует строку s2 в строку s1.
Для ввода строк с клавиатуры в С удобно использовать функцию gets(), а для вывода – puts(), которые находятся в заголовочном файле stdio.h.
Для вывода строк можно в С использовать функцию printf() из заголовочного
файла stdio.h. |
|
|
|
|
Пример программы |
для инвертирования |
(перестановки символов в обратном |
||
порядке) вводимой с клавиатуры строки. |
|
|
||
#include <stdio.h> |
/* |
Модуль с функциями ввода-вывода С */ |
||
#include <conio.h> |
/* |
Модуль для работы |
с консолью */ |
|
#include <string.h> |
/* |
Модуль для работы |
со строками */ |
|
char s1[80]; |
/* |
Исходная строка */ |
|
|
char s2[80]; |
/* |
Строка результат */ |
||
int main(void) |
/* |
Главная функция */ |
|
|
{ int i, size; |
/* |
локальные переменные счетчика и размера */ |
||
printf("Введите исходную |
строку \n"); |
/* |
Вывод на экран приглашения */ |
|
gets(s1); |
|
|
/* |
Ввод строки */ |
size=strlen(s1); |
|
|
/* |
Определение длины строки */ |
printf("\nsize %d \n", size); |
/* |
Вывод числа элементов строки */ |
||
for (i=0; i<(size); i++) |
|
/* |
Цикл инвертирования строки */ |
|
{ s2[i]=s1[size-1-i]; |
|
|
/* |
Формирование символа строки */ |
} /* конец цикла for */ |
|
|
|
|
printf("\nРезультат\n %s",s2); |
/* |
Вывод строки на экран */ |
||
getch(); |
/* |
Ожидание нажатия любой клавиши */ |
||
return 0; |
|
|
/* |
Возврат успеха работы программы */ |
}/* -------------------- main ----------------------------- */
ВC++Builder до версии 2006 имеется специальный строковый тип-класс, совместимый со стандартом ASCII – AnsiString, аналогичный в языкам Object Pascal и Delphi. Начиная с версии 2006 он замене на UnicodeString. Формат объявления имеет вид
AnsiSring имя_строки; или UnicodeString имя_строки;
4
Этот класс имеет встроенные функции преобразования данных, наиболее важные из которых указаны в таблице 6.1. Эти функции вызыв аются после указания переменной через точку в следующем формате:
имя_переменной_строки.имя_встроенной_функции();
Таблица 6.1 – Некоторые встроенные функции класса AnsiString
Функция класса |
|
|
|
Описание |
|
|
|
|
|
|
|
ToInt() |
Преобразует строковый |
тип |
в |
целочисленные данные типа int |
|
ToDouble() |
Преобразует |
строковый |
тип |
в |
вещественные данные типа double |
c_str() |
Преобразует |
данные AnsiString в символьный массив |
Примеры использования строкового типа AnsiString:
AnsiString Name; // Объявление переменной типа AnsiString char ctext[80]; // Объявление строковой переменной языка С
…
ctext=Name.c_str(); // Преобразование AnsiString
6.3 Комбинированный тип данных (структуры)
Комбинированный тип данных в языках программирования описывает упорядоченную структуру данных различных базовых типов. В языке программирования С в качестве комбинированного типа данных выступает структура.
Структура является совокупностью элементов (полей) произвольных типов. Они широко используются при формировании баз данных в некоторой предметной области. Формат объявления структуры:
struct имя_структуры
{тип_поля_1 имя_поля_1; тип поля_2 имя_поля_2;
|
… |
|
|
|
тип поля_N имя_поля_N; |
|
|
|
} список_переменных; |
|
|
|
Список переменных является необязательным и включает имена переменных, |
||
которые будут иметь созданный |
тип структуры. |
||
|
Пример структуры записи |
календарной даты |
|
struct date |
|
|
|
{ |
short int day; |
/* поле - |
число */ |
|
short int mount; |
/* поле - |
месяц */ |
|
int year; |
/* поле - |
год */ |
}; |
/* конец описания date */ |
|
|
|
Внутри структуры поля могут также иметь тип структуры. Пример описания |
||
структуры для записи данных о |
человеке: |
|
|
struct man |
|
|
|
{ |
char name[40]; |
/* поле - |
имя */ |
|
char secondname[40]; |
/* поле - |
отчество */ |
|
char family[100]; |
/* поле - |
фамилия */ |
|
date birthday; |
/* поле - |
день рождения */ |
}; |
/* конец описания man */ |
|
|
Приведенное выше описание является описанием имени типа. Если в конце структуры не было списка переменных или нужно его дополнить, то для непосредственной работы с данными структур в программах необходимо объявить переменные указанного типа структуры. Формат объявления следующий:
имя_типа_структуры имя_переменой;
Примеры: |
|
|
|
|
man client; |
/* |
переменная |
структуры |
man */ |
date finish; |
/* |
переменная |
структуры |
date */ |
В переменных |
комбинированного типа (структуры) доступ к отдельным членам |
(полям) задается с помощью составного имени (операции . (точка)). Общий формат доступа к данным поля структуры:
имя_переменной_структуры.имя_поля
Пример задания значений полей в переменной структуры:
5
client.name = “Иван”; client.birthday.year=1979; client.birthday.day=29; client.birthday.mount=2;
Непосредственно присвоить значение всей структуры можно от переменной аналогичной структуры. Пример:
man client1, client2; client1=client2;
Пример программы, вводящей с клавиатуры почтовый адрес в структуру:
#include <stdio.h> |
|
|
|
struct |
addr |
/* задание структуры для хранения почтового адреса */ |
|
{ char |
name[255]; |
|
/* Имя, фамилия адресата */ |
char |
country[40]; |
/* Страна проживания */ |
|
long |
int index; |
|
/* Почтовый индекс */ |
char |
sity[80]; |
|
/* Город */ |
char |
street[80]; |
|
/* Улица */ |
int house; |
|
/* Номер дома */ |
|
int apartment; |
|
/* Номер квартиры */ |
|
} man; |
|
|
/* конец описания структуры addr*/ |
int main(void) |
|
/* главная функция */ |
{printf("\nФамилия и инициалы \n"); scanf("%s",man.name); printf("\nСтрана проживания\n"); scanf("%s",man.country); printf("\nГород проживания\n"); scanf("%s",man.sity); printf("\nУлица \n"); scanf("%s",man.street); printf("\nНомер дома \n"); scanf("%d",&man.number); printf("%s ",man.name); printf("%s ",man.country); printf("%s ",man.sity); printf("%s ",man.street); printf("%d \n",man.number);
return 0; |
|
} /* --------------------------------------------------- |
*/ |
Объекты типа структура можно присваивать, передавать как параметры функции и возвращать из функции в качестве результата. Остальные такие операции, как сравнение (== и !=) не определены. Размер переменной структурного типа нельзя вычислить просто как сумму его членов.
Два структурных типа являются различными даже тогда, когда они имеют одни
ите же члены. Например:
struct s1 { int a; float b; };
struct s2 { int a; float b; };
есть два разных типа, поэтому s1 x;
s2 y = x; // ошибка: несоответствие типов
Структурные типы отличны также от основных типов, поэтому s1 x;
int i = x; // ошибка: несоответствие типов
6.4 Битовое поле
Битовое поле является модификацией структуры в языке С, которое позволяет хранить массив полей, имеющих длину в один или несколько байт, внутри непрерывной последовательности. Битовые поля активно используются для хранения состояния группы различных логических переключателей (флагов). В них каждый бит кодирует состояние соответствующего номера поля: например, 1 – включено, 0 – выключено. Эффективно использование битовых полей при передаче данных по линиям связи.
6
Объявление битового поле аналогично структуре, однако после типа поля и имени указывается длина в битах. Формат объявления битового поля следующий:
struct имя_битового_поля
{тип_поля имя_поля : длина_поля ;
…
} список_переменных; Тип поля указывается в зависимости от длины поля. Если длина в один байт,
то следует указать unsigned (беззнаковый), при длине в два и более байта – знаковый. Длина полей может быть от 1 до 32.
Пример объявления битового поля для контроля состояния внешнего периферийного устройства:
struct status
{unsigned enabled : 1; /* признак включения устройства */ unsigned ready : 1; /* режим чтения данных с устройства */
unsigned write : 1; /* режим записи данных в устройство */ unsigned error : 1; /* сбой в работе устройства */
};
Пример программы задания с клавиатуры режима работы привода: #include <conio.h>
#include <stdio.h> struct device
{unsigned enabled : 1; /* Включение устройства */ unsigned ready : 1; /* Чтение данных с устройства */ unsigned write : 1; /* Запись данных в устройство */
unsigned error : 1; |
/* Состояние сбоя устройства */ |
||
}; |
|
/* device */ |
|
int main(void) |
|
|
|
{ device drive={0x0,0x0,0x0,0x0}; |
/* Переменная привода */ |
||
int ans; |
|
|
/* Код ответа */ |
do |
/* цикл опроса меню */ |
{printf("\n Введите режим работы ");
printf("\n 1 - Включить привод "); /* вывод меню */ printf("\n 2 - Читать данные привода ");
printf("\n 3 - Передать данные на привод ");
printf("\n 4 - Установить сбой привода "); |
|
|
printf("\n 0 - Выключить привод \n "); |
|
|
scanf("%d",&ans); |
/* чтение выбора */ |
|
switch (ans) { |
/* выбор режима */ |
|
|
case 1: drive.enabled=1; |
|
|
break; |
|
|
case 2: drive.ready=1; |
|
|
break; |
|
|
case 3: drive.write=1; |
|
|
break; |
|
|
case 4: drive.error=1; |
|
|
break; |
|
|
case 0:drive.enabled=0x0; |
|
|
break; |
|
} |
/* switch */ |
|
printf("\n Состояние системы %x",drive); |
/* вывод состояния */ |
|
} while (ans!=0); |
/* конец цикла while */ |
|
getch(); |
/* задержка экрана */ |
|
return 0; |
|
|
} |
|
/* main */ |
6.5 Объединения
Тип данных, позволяющий нескольким переменным различных типов занимать одну область памяти. Объявление объединения производится с помощью слова union аналогично структуре:
union имя_объединения
{ тип_поля_1 имя_поля_1;
7
тип поля_2 имя_поля_2;
…
тип поля_N имя_поля_N; } список_переменных;
Пример объединения для преобразования целого типа в символьный: union charint
{int i; /* данные типа int */
char c[2]; |
/* |
символьные |
данные */ |
}; |
/* |
charint */ |
|
Для работы с объединениями необходимо описывать переменные объявленного ранее типа объединения. Формат объявления переменной объединения:
имя_типа_объединения имя_переменой;
Пример: charint x;
Объединения используются для преобразования типов данных. Так как все поля размещаются в одной области памяти, то в отличие от структур изменение одного из полей автоматически вызывает изменение остальных.
Доступ к полям переменных объединений аналогичен структурам и производится
с помощью конструкции составного имени. |
||
Пример программы, |
преобразующей вводимое число типа long int в два |
|
значения типа signed int и |
символьный тип. |
|
#include <conio.h> |
/* |
подключение модуля консольных функций */ |
#include <stdio.h> |
/* |
подключение функций ввода-вывода языка С */ |
union transint |
/* |
Описание типа объединения */ |
{ long int i; |
/* |
Поле типа long int */ |
int shi[2]; |
/* |
Поле массива элементов int */ |
char ch[4]; |
/* |
Поле строки двух символов */ |
}; |
/* |
Конец описания типа */ |
int main(void) |
/* |
Главная функция */ |
{ transint dig; |
/* переменная |
типа |
объединения */ |
|
printf("\nВведите число типа int : "); |
/* |
вывод экранного |
запроса */ |
|
scanf("%i",&dig.i); |
/* |
ввод числа |
типа |
int с клавиатуры */ |
|
printf("\nМладший |
байт |
числа : %X",dig.shi[0]); |
/* вывод преобразования */ |
|
|
printf("\nСтарший |
байт |
числа : %X",dig.shi[1]); |
|
|
|
printf("\nСтрока числа |
: %s",dig.ch); |
/* Вывод результата - строки*/ |
||
|
getch(); |
|
/* Приостановка закрытия окна консоли */ |
||
|
return 0; |
|
/* Код успешного завершения */ |
||
} |
/* main |
*/ |
|
|
|
6.6 Ссылочный тип данных и указатели
Ссылочный тип данных в языках программирования предназначен для хранения
адресов ячеек оперативной |
памяти, |
которые связанны с некоторыми данными. |
Переменные ссылочного типа называются |
указателями. Переменная указатель содержит |
|
адрес начала расположения |
данных |
определенного типа в оперативной памяти |
компьютера при выполнении программы. |
|
|
|
|
|
|||
Объявляются переменные указатели в следующем формате: |
|
|
|
|||||
Имя_базового_типа *имя_указателя; |
|
|
|
|
|
|||
В |
качестве |
базового |
типа |
используются |
как |
скалярные, |
так |
и |
структурированные типы, которые указывают тип данных, которые хранятся в ячейках
памяти. В |
качестве базового типа |
возможно также использование и ссылочного типа. |
|
Примеры |
|
|
|
long int *pli; |
/* |
указатель на длинное целое */ |
|
float *vector[12]; |
/* |
объявление массива указателей */ |
|
int |
* pi; |
|
|
char ** cpp; |
/* |
указатель на указатель на символ */ |
|
int |
(*vp)[10]; |
/* |
указатель на вектор из 10 целых */ |
Для работы с указателями используют операцию взятия адреса, выполняемую с
помощью оператора &. Формат получения |
адреса при присваивании |
||
имя_указателя = &переменная_базового_типа; |
|||
Пример |
|
|
|
float x, *px; |
/* |
Объявление |
вещественной переменной и указателя*/ |
px = &x; |
/* |
Взятие адреса у вещественного числа */ |
8
Чтобы получить значение данных, которое хранится по адресу указателя, используется оператор *, который ставится перед указателем. Эта основная операция над указателем называется разыменование (ссылка на объект, на который указывает указатель) или косвенным обращением. Операция разыменования - это унарное * (префиксное). Формат операции разыменовывания:
переменная_базового_типа = *переменная_указатель;
Примеры: x = *px; /* передача в х значения, хранимого по адресу px */ char c1 = 'a';
char* p = &c1; /* в p хранится адрес c1 char c2 = *p; // c2 = 'a'
Переменная, на которую указывает p,- это c1, а значение, которое хранится в c1, это 'a', поэтому присваиваемое c2 значение *p есть 'a'.
#include <conio.h> |
// Пример использования указателей для доступа к переменным |
|||
#include <stdio.h> |
|
|
|
|
int main(int argc, |
char* argv[]) |
|
|
|
{ float a, b, c; |
|
/* переменные */ |
|
|
float *pa=&a, *pb=&b, *pc=&c; /* |
указатели на переменные */ |
|||
printf("\na="); |
|
/* |
ввод a */ |
|
scanf("%f",&a); |
|
|
|
|
printf("\nb="); |
|
/* |
ввод b */ |
|
scanf("%f",&b); |
|
|
|
|
(*pc)=(*pa)+(*pb); |
/* |
расчет с через разыменовывание указателей */ |
||
printf("\n c= %4.1f address %p", |
c, pc); |
/* вывод результатов */ |
||
getch(); |
|
|
|
|
return 0; }//---------------------------------------------------------------------------
При работе с массивами компилятор трактует имя массива, как указатель на его первый элемент. Поэтому допустимо следующее присваивание:
int *p , m[100]; |
// |
объявления указателя и вектора; |
p=m; |
// |
присваивание адреса указателю на начало массива |
Для доступа к элементу массива с помощью указателя, нужно задать смещение адреса в указателе с помощью конструкции:
*(указатель + смещение)
Пример записи в 10-й элемент целочисленного вектора значение 256
int vector[100], *pv; |
// объявление |
вектора |
и указателя; |
||
pv = |
vector; |
// |
присваивание указателю адреса массива |
||
*(pv |
+10)=256; |
// |
присвоение |
значения элементу через указатель |
|
К переменным-указателям |
применяются следующие |
классические операции: |
1)присваивания, при этом следует внимательно следить, чтобы указатели имели одинаковый базовый тип;
2)получение адреса от переменной одинакового базового типа;
3) сложения и вычитания;
4) сравнения;
5)вызов функции с помощью указателя;
6)создания динамических переменных.
Возможна ситуация, когда указатель хранит адрес другого указателя, тогда имеет место многочисленное перенаправление. Пример объявления такого указателя:
int **pp;
Пример программы, считывающей с клавиатуры целое число в переменную i, и показывающую адрес оперативной памяти, где будет храниться это число:
#include <stdio.h> #include <conio.h> int i, *p;
int main(void)
{p=&i; /* присвоить p адрес i */ printf("\nЧисло типа int : "); scanf("%i \n", &i);
printf("\n %i по адресу %p ",i, p); getch();
return 0;
} |
/* main */ |
9
В примере показано, что для вывода значения указателя используется в функции printf спецификатор p.
Возможна начальная инициализация указателя, например, строковой константой char *p = “Электропривод и АПУ”;
Для указания нулевого значения указателя (которое не создает ссылки на область памяти) используется слово NULL.
Пример программы расчета действительного вектора из квадратов введенных значений с использованием указателя на массив для вывода данных.
#include <iostream.h>
#include <conio.h> |
|
|
|
int main() |
|
|
|
{ |
float M[10]; |
// |
вектор |
|
float *PM=M; |
// |
указатель на вектор |
|
for(int i=0;i<10;i++) |
{cout << "M[" << i << "]="; cin >> M[i]; M[i]=M[i]*M[i];
} // конец цикла for для ввода матрицы for(int i=0; i<10; i++)
{cout<<"M["<<i<<"]="<<(*PM)<< endl; // вывод матрицы через указатель
PM++; |
// перенаправление указателя на следующий элемент |
} // конец цикла for для вывода матрицы |
|
getch(); |
|
return 0; |
|
}//--------------------------------------------------------------------------- |
|
При использовании |
указателя на структуры и объединения для доступа к |
данным полям таких переменных возможно использовать оператор -> в формате наименование_указателя –> имя_поля_структуры;
Пример задания комплексного числа 10-j20 указателем на структуру: struct TComplex // Структура комплексного числа
{double Re, Im; // Действительная и мнимая части числа } Xcomp; // Список статических переменных
TComplex *pcoml=&Xcomp; Pcomp->Re=10; Xcomp.Im=-20;
6.7 Динамические переменные
Динамические переменные, в отличие от ранее рассматриваемых статических переменных, размещают данные при выполнении программы в свободной области оперативной памяти компьютера (динамической памяти). Эти переменные не создаются автоматически при начале выполнения программы, а только, если это предусмотрено алгоритмом расчета. Создав динамическую переменную, программа резервирует под нее часть ресурсов динамической памяти. Если динамическая переменная больше не нужна, она уничтожается с помощью соответствующей функции.
Динамическое распределение памяти в языке С реализуется в модуле stdlib.h
спомощью функций:
-malloc – создание динамической переменной указанного размера в формате; указатель = (базовый_тип *) malloc(число_байт_под_данные)
-free(указатель) – удаление динамической переменной.
Число |
байт под |
данные |
можно определить используя функцию sizeof. |
|
Пример объявления динамической переменной-вектора из 10 значений типа |
||||
float с проверкой |
успешного |
создания |
||
float |
*pfd; |
/* |
создание указателя */ |
if((pfd = (float *) malloc(10*sizeof(float)))==NULL)
{printf(“\nМало свободной памяти”); getch();
return -1; } // if
…
|
|
|
10 |
|
|
free(pfd); |
|
/*уничтожение указателя */ |
|
|
Пример программы определяющей среднее значение вещественного массива с |
|||
помощью динамического вектора произвольной длинны на языке С. |
||||
#include <stdio.h> |
|
|
||
#include <conio.h> |
|
|
||
float |
*pfm; /* Вектор заданный с помощью указателя */ |
|||
float |
middle=0; /* |
среднее значение */ |
|
|
int main(int argc, |
char* argv[]) |
|
||
{ int |
N; |
/* |
Число элементов массива |
*/ |
int |
i=0; |
/* |
Счетчик цикла */ |
|
printf("\nN="); |
/* Вывод на экран запроса */ |
|||
scanf("%d",&N); |
/* Ввод числа элементов массива */ |
|||
pfm |
= (float*) malloc(N*sizeof(float)); /*создание динамической переменной*/ |
|||
if (pfm==NULL) { |
printf("Not memory"); |
/* контроль наличия памяти */ |
||
|
|
|
getch(); |
|
|
|
|
return -1; |
/* код ошибки */ |
}
for(i=0;i<N;i++)
{printf("\nM[%2d]=",i);
scanf("%f",pfm);
middle+=*pfm;
pfm++;
} /* конец цикла for */ |
|
|
free(pfm); |
/* уничтожение динамического массива и Освобождение памяти */ |
|
printf("\nmiddle=%f",middle/N); |
/* вывод результата */ |
|
getch(); |
/* приостановка |
закрытия окна консоли */ |
return 0; |
/* возврат кода |
успешного завершения */ |
}/*---------------------------------------------------------------------- |
|
|
В языке |
С++ для этих целей |
используются операторы new и delete. Создание |
динамической переменной выполняется в форме
указатель = new тип_переменной;
Удаление динамической переменной delete переменная_указатель;
При освобождении памяти для указателей на массивы используется следующая конструкция:
delete [] указатель_массива;
Операция new создает динамическую переменную указанного типа, распределив (при возможности) требуемое число байт в свободной области памяти (которую называют "кучей"). Продолжительность существования динамической переменной - с момента создания и до операции delete, отменяющей распределенную для неѐ память, либо до конца работы программы.
В случае успешного завершения new возвращает указатель нового объекта. Пустой указатель означает неудачное завершение операции (например, недостаточный объем). Прежде чем пытаться обращаться к динамической переменной, следует проверить указатель на наличие значения NULL. В отличие от malloc, new сама вычисляет размер динамической переменной, и указывать операцию sizeof ненужно.
Пример создания динамической переменной целочисленного тензора
int *pmatr; |
// Создание |
указателя |
|
pmatr = new int[3][10][12]; |
// |
Выделение динамической памяти указателю |
|
… |
|
|
|
delete nameptr; |
// |
Удаление |
переменной и освобождение памяти |
Пример программы считывающей действительный вектор с клавиатуры и записывающий его элементы в текстовый файл с помощью динамической переменной на языке С++.
#include <iostream.h> #include <fstream.h> #include <conio.h>
float *pfm; // Указатель на вектор
int main(int argc, char* argv[]) //---------------------------------------
{char *fname; // строка-указатель на имя файла int N; // Число элементов массива