Файл: А. Б. Шнейвайс основы программирования.pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 06.12.2023

Просмотров: 343

Скачиваний: 6

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

Здесь a, b, · · · , z — имена размещаемых массивов или указате- лей. После имени массива в круглых скобках указываются нижний и верхний индексы (по соответствующему измерению), разделенные двоеточием. Спецификатор stat – необязательный. Он присваивает указанной переменной код причины завершения allocate. Если код равен нулю, то размещение успешно (иначе – безуспешно).
3. строки 10 и 20. Оператор deallocate. Общая форма его записи подобна форме записи оператора allocate.
4. 15-я строка. Оператор allocate a(n1:n2) и выделяет память, и явно модифицирует диапазон индексов размещаемого массива.
5. 17-я строка. Вызывается функция allocated, которая возвращает
.true., если массив размещен, и .false., если нет (см. 22 строку).
6. После компиляции и пропуска программы allocat0 получим:
введи размер массива:
n=
2 1
0.1000000000000000D+01 2
0.1414213562373095D+01
введи граничные индексы:
n1=
-2
n2=
2
T
-2 0.1414213562373095D+01 0.2000000000000000D+01
-1 0.1000000000000000D+01 0.1000000000000000D+01 0
0.0000000000000000D+00 0.0000000000000000D+00 1
0.1000000000000000D+01 0.1000000000000000D+01 2
0.1414213562373095D+01 0.2000000000000000D+01
Массив a не размещен!!!
7. Изменение диапазона индексов при описании массива может потре- бовать соответствующих изменений в операторах, использующих граничные индексы.
8. Размещаемые массивы, как и автоматические, не могут использо- ваться в операторах common, equivalence и namelist, но в отли- чие от автоматических могут иметь атрибут save.
9. Динамическое создаваемые массивы (см. [2] 7.8).
203

5.2
СИ.
Для динамического распределения памяти используем функции malloc и free (последняя освобождает память). Их описания можно посмотреть посредством man malloc и man free.
void *malloc(размер памяти в байтах); – выделяет память нуж- ного размера. При успешном выделении возвращает указатель типа void на первый свободный байт выделенной памяти (в противном случае воз- вращается NULL). Например,
#include
// Потребуются scanf и printf.
#include
// Потребуются malloc и free !!!
int main()
{ double *a;
// a - указатель на ячейку для значения типа double int n, k;
// (место под указатель выделено, но сам он не определен).
printf(" введи размер массива:\n");
// Ввод и контрольная печать scanf ("%d", &n); printf(" n=%d\n", n); //
размера массива.
a=(double*) malloc(n*sizeof(double));
// Поиск адреса, с которого можно
// разместить n значений типа double.
// Согласно описанию прототипа malloc возвращает указатель на значение типа
// void. Нам же нужен указатель на значение типа double. Поэтому, используем
// операцию (double*) приведения указателя (void*), выработанного malloc, к
// типу указателя на double*.
if (!a) {printf("Не могу выделить!\n"); return 1;}
// Попытка неудачна.
for (k=0;k<=n-1;k++)
// При удаче работаем с массивом.
{ a[k]=sqrt((double)k);
// Имеем право именовать элемент printf("
%d
%20.13le %20.13le \n",
// массива: и *(a+k), и a[k].
k,*(a+k), a[k]*a[k]); }
free(a);
// После free(a) память, адресуемая a, отдана операционной системе.
n=n/2;
printf(" n=%d\n", n);
// Еще один пример формировки a=(double*) malloc(n*sizeof(double));
// размещаемого массива:
if (!a) {printf("Не могу выделить!\n"); return 1;}
for (k=0;k<=n-1;k++) {a[k]=(double)k*k; printf("
%d
%20.13le\n", k,a[k]);}
free(a); return 0; }
введи требуемый размер массива:
//
malloc - НЕ ИНИЦИАЛИЗИРУЕТ
5
//
выделяемую память !!!
n=5
//
(cм. man malloc)
0 0.0000000000000e+00 0.0000000000000e+00
// Аналогично malloc
1 1.0000000000000e+00 1.0000000000000e+00
// работает и calloc.
2 1.4142135623731e+00 2.0000000000000e+00
//
3 1.7320508075689e+00 3.0000000000000e+00
// calloc ОБНУЛЯЕТ !!!
4 2.0000000000000e+00 4.0000000000000e+00
// выделяемую память.
n=3
// У calloc два аргумента:
0 0.0000000000000e+00
//
число элементов в массиве
1 1.0000000000000e+00
//
и
2 4.0000000000000e+00
//
число байт на один элемент.
204


5.3
О чем узнали из пятой главы? (второй семестр)
1. Современный ФОРТРАН предоставляют возможность использова- ния динамических массивов.
2. Динамический массив – это массив, память под который выде- ляется в процессе работы исполнимого кода (не при компиляции).
3. Размер динамического массива задаётся при работе программы.
4. В ФОРТРАНЕ-95 существуют три вида динамических массивов:
автоматические, размещаемые и создаваемые.
5. Автоматические массивы локальны по отношению к процедурам,
использующим их, создаются при вызове процедуры, в которой опи- саны, и уничтожаются при выходе из неё автоматически.
6. Размер автоматического массива можно задать и константой, и переменной (глобальной или служащей формальным аргументом), и посредством функции size, в качестве аргумента которой выступает другой массив, известного размера.
7. Автоматический массив не инициализируем оператором data и операторами описания типа, не используем в операторах namelist и equivalence, и не снабжаем атрибутом save.
8. Размещаемые массивы определяются атрибутом allocatable (при описании) с указанием только ранга массива.
9. Распределение памяти под размещаемый массив осуществляется оператором размещения allocate, который требует задания разме- ров массива или его граничных индексов.
10. deallocate – оператор освобождения памяти, занимаемой размеща- емым массивом.
11. Автоматические и размещаемые массивы не должны быть фор- мальными аргументами.
12. Автоматически размещаемые массивы реализуются через стек.
205

13. Динамически размещаемые массивы реализуются через дина- мическую область “куча” .
14. В отличие от автоматических массивов, исчезающих после завер- шения работы процедуры, динамически размещаемые локальные массивы могут иметь атрибут save и тем самым сохранять свой ста- тус при последующих обращениях к процедуре.
15. Если размещаемый массив не имеет атрибута save, то перед выхо- дом из процедуры целесообразно явно освободить занимаемую ими память посредством оператора deallocate.
16. Создаваемые массивы — массивы, создаваемые динамически, без явного использования операторов описания массивов.
17. Создаваемые объекты (в том числе и массивы) не имеют имён так, что ссылаться на них можно только через указатели, которые содержат адреса созданных объектов, установленные операторами allocate и deallocate
18. В С для динамической работы с памятью используются функции malloc и free. Их прототипы void *malloc(size_t size); и void free(void *ptr); (описаны в stdlib.h; см. man malloc).
19. Вызов malloc(размер_памяти_в_байтах); – нацелен на выде- ление памяти нужного размера. При успешном выделении через имя функции возвращается указатель типа void на первый свободный байт выделенной памяти (иначе возвращается NULL). Память, вы- деленная функцией malloc не инициализируется.
20. Для выделения памяти под указатель на данное не типа void необ- ходимо посредством операции приведения типа указатель, найден- ный malloc, привести к требуемому, например,
(double*) malloc(размер_памяти_в_байтах);
21. free(указатель_на_данное_типа_void) освобождает область памяти, адресуемую указателем, полученную ранее ранее посред- ством malloc().
206


5.4
Пятое домашнее задание (второй семестр).
Решения задач оформить на ФОРТРАНе с использованием размещае- мого массива
• ФОРТРАН-решения задач с 1 по 7 должны подключать модуль sort, содержащий описания всех используемых в них процедур.
• ФОРТРАН-решения задач с 8 по 9 должны подключать модуль quadra (из предыдущего домашнего задания c описанием функции rectan), дополненный функциями trap (задача 8) и sim (задача 9).
• Инициировать запуск каждой программы должен make- файл, который наряду с целями компоновки исполнимого кода,
получения объектных кодов единиц компиляции и запуска исполни- мого кода, содержит и цель вывода результата из файла на экран.
1. Разработать функцию, определяющую число нечётных элементов вектора целого типа, и продемонстрировать её работоспособность на тестовых примерах (все элементы чётные, один элемент нечётный,
три нечётных элемента, все элементы нечётные).
2. Разработать функцию и подпрограмму, которые объединяют два вектора в один с чередованием элементов исходных векторов целого типа, и продемонстрировать её работоспособность на тестовых при- мерах (в обоих векторах одинаковое количество элементов, больше элементов в первом векторе, больше элементов во втором векторе).
3. Разработать нерекурсивную и рекурсивную функции инвертирова- ния вектора (перестановки элементов в обратном порядке).
4. Разработать функцию циклического сдвига элементов вектора, т.е.
при сдвиге вправо содержимое k-го элемента переносится в k+1-й элемент (k=1, ... , n), причём содержимое последнего элемента переносится в первый; а при сдвиге влево содержимое k-го пере- носится в k-1-й для (k=2, ... , n), причём содержимое первого элемента переносится в последний.
5. Разработать функцию проверки вектора на упорядоченность.
207

6. Разработать нерекурсивную и рекурсивную функции поиска образ- ца в упорядоченном по величине элементов векторе.
7. Разработать функцию и процедуру объединения двух упорядочен- ных массивов в один с сохранением упорядоченности.
8. Значение интеграла по промежутку [a,b] от подинтегральной функ- ции f(x) приближенно вычисляется по формуле трапеций:
˜
S = h ·
h f (a) + f (b)
2
+
n
X
i=2
f (x i
)
i
,
где h =
b − a n
;
x i
= a + (i − 1)h i = 2, · · · n;
x
1
≡ a , x n+1
≡ b.
n – количество промежутков равномерного дробления отрезка [a,b].
Разработать функцию trap(y,a,b,n), которая по набору из n+1
значений подинтегральной функции, хранящихся в первых n+1 эле- ментах одномерного массива y вычисляет значение квадратурной суммы формулы трапеций. Описание функции trap поместить в модуль quarda (тот же самый, в который помещена и процедура rectan из предыдущего домашнего задания).
Главная программа должна подсоединять соответствующий модуль,
вводить исходные данные из файла, вызывать нужную процедуру и выводить результат +вместе с таблицей подинтегральной функции так, чтобы, получить её график, активируя цель make plot.
9. Для расчёта интегралов наряду с формулами прямоугольников и трапеций широко используется составная формула Симпсона:
˜
S =
h
3
· [f (a) + f (b) + 4 · S
1
+ 2 · S
2
]
S
1
= f
2
+ f
4
+ · · · + f n=2m
, S
2
= f
3
+ f
5
+ · · · + f n−1
f i
= f (x i
) x i
= a + h · (i − 1), i = 1, 2, · · · , (n = 2m), n + 1, h =
b − a n
причём n = 2 · m – число промежутков дробления – обязательно чётное. Учитывая требования к решению предыдущей задачи, раз- работать функцию sim(y,a,b,n).
208


6
Операции ФОРТРАНА-95 над массивами.
В ФОРТРАНе-77 доступ к элементу массива был возможен единствен- ным способом – явно через индекс элемента. Поэтому при работе с мас- сивами приходилось использовать оператор цикла.
В современном ФОРТРАНе есть альтернативная возможность работы со всем массивом или его частью (см. [2, 3]). Сравните:
do i=1,n a=b и
a(i)=b(i)
enddo
6.1
Инициализация элементов массива
Дополним примеры инициализации массива из раздела 3.2:
program sect0; implicit none; integer i,j
! ФОРТРАН-инициализация integer
A(5,7) / 11,12,13,14,15,
21,22,23,24,25,& ! массива через
31,32,33,34,35,
41,42,43,44,45,& ! 1) оператор
51,52,53,54,55,
61,62,63,64,65,
71,72,73,74,75 / !
описания типа;
integer B(5,7)
data
B
/ 11,12,13,14,15,
21,22,23,24,25,&
! 2) оператор DATA;
31,32,33,34,35,
41,42,43,44,45,&
!
51,52,53,54,55,
61,62,63,64,65, 71,72,73,74,75 /
integer :: C(5,7)= reshape(&
! 3) конструктор
(/ ((10*j+i, i=1,5),j=1,7) /),&
!
массива и shape=(/5,7/))
!
функцию reshape integer, dimension (5,7) :: D= reshape( &
!
(/ ((10*j+i, i=1,5),j=1,7) /), shape=(/5,7/)) !
real E; dimension E(5); data E /1.0,2.0,3.0,4.0,5.0/! Так dimension F(5); real F /1,2,3,4,5 /
!
и real, dimension(5) :: G=(/ 1,2,3,4,5 /)
! так можно!!!
!
real P; dimension P(5) / 1,2,3,4,5/
!
/
Но так
!
real, dimension :: Q(5)=(/1,2,3,4,5/)
!
\
НЕЛЬЗЯ!!!
write(*,’(5i6)’) A
! A, B, C, или D хранятся в памяти одинаково.
write(*,’(5f7.2)’) E, F, G
! E, F и G
хранятся в памяти одинаково.
end program sect0
Замечание
При инициализации многомерного массива через конструк- тор помним, что в ФОРТРАНе конструктор массива — всегда одно- мерный массив. Для преобразования последнего в многомерный удоб- на функция reshape, которая через своё имя возвращает массив тре- буемой аргументом shape формы, заполненной данными из исходного массива, который в приведённом примере является одномерным.
209

11 12 13 14 15
//
Результат работы sect0:
21 22 23 24 25
//
31 32 33 34 35
// Обратим внимание, что содержимое
41 42 43 44 45
// каждого столбца матрицы A
51 52 53 54 55
// выводится в строку, т.е. в порядке
61 62 63 64 65
// расположения элементов матрицы в
71 72 73 74 75
// оперативной памяти. Поэтому на
1.00 2.00 3.00 4.00 5.00
// выводе видим транспонированную
1.00 2.00 3.00 4.00 5.00
// матрицу.
1.00 2.00 3.00 4.00 5.00
reshape — указывает форму массива и способ его заполнения.
6.2
Секция (сечение) массива.
Cекцией массива (или его сечением) называют некоторое подмноже- ство его элементов. Задаётся секция именем массива и списком индексов секции. Секция массива сама является массивом и может состоять как из последовательно расположенных элементов, так и из выборки эле- ментов, которые в оперативной памяти не соседствуют друг с другом.
Например,
real, dimension :: A(5,7)=(/((10*j+i,i=1,5),j=1,7)/), U(4), W(5)
A(4,:)
! четвёртая строка матрицы A
A(:,K)
! k-й столбец матрицы A.
!
A(N1:N2, K1:K2:2)
! прямоугольная секция матрицы, состоящая из
! строк с N1-й по N2-ю, и из столбцов c индексами
! одинаковой четности, начиная c K1-го по K2-ой.
!
K1:K2:2 - пример индексного триплета.
!
U=(/4, 2, 1, 7/)
! Смысловая нагрузка элементов вектора U -
! возможные номера столбцов матрицы, для которой
! хотим построить сечение.
A(4,U)
! Например, для матрицы А оно состоит из элементов
! четвёртой строки, находящихся в 4, 2, 1, и 7
! столбцах (сечение представляет собой вектор).
!
U - пример векторного индекса.
Индексом секции массива могут быть:
1) скалярный индекс; 2) индексный триплет; 3) векторный индекс.
210


Скалярный индекс — обычный индекс массива. Если все индексы секции скалярны, то сама секция — просто элемент массива.
Индексный триплет – символическое обозначение последователь- ности индексов, задаваемой в общем случае тремя характеристиками:
[нижний_индекс_триплета] : [верхний_индекс_триплета] [: шаг_по_индексу]
1. квадратные скобки – не синтаксический элемент, а напоминание о том, что заключённое в них, если нужно, можно не указывать.
2. При записи секции массива индексным триплетом из всех эле- ментов массива выбираются лишь те, индексы которых заключены в указанном диапазоне триплета при учёте шага по индексу.
3. Если шаг не указан, то он полагается равным единице.
4. Если нижний индекс триплета не указан, то полагается, что он равен нижнему индексу соответствующего экстента массива.
program test1; implicit none; integer a(-5:14), i a=5
! Всем элементам массива присваивается 5
a(-3:13:2)=6
! Элементы секции с нечетными индексами a(-2:12:2)=0
! 3, 5, 7, 9, 11, 13 переопределяются на 6,
write(*,’(20i3)’) (i,i=-5,14) ! а элементы секции с четными индексами write(*,’(20i3)’) a
! -2, 0, 2, 4, 6, 8, 10, 12 обнуляются.
end program test1
-5 -4 -3 -2 -1 0
1 2
3 4
5 6
7 8
9 10 11 12 13 14 5
5 6
0 6
0 6
0 6
0 6
0 6
0 6
0 6
0 6
5
Векторный индекс — целочисленный вектор, значениями элементов которого служат индексы элементов массива, попадающих в секцию.
1. Векторный индекс в отличие от индексного триплета позволяет включить в секцию произвольное множество элементов.
2. Значения индексов в векторном индексе могут:
(a) располагаться в любом порядке (cм. ниже программу vec0);
(b) повторяться, т.е. секция может содержать по несколько экзем- пляров одного и того же элемента (см. ниже программу vec1). В
этом случае она не должна встречаться в левой части оператора присваивания
3. Размер секции массива равен нулю, если векторный индекс — массив нулевого размера.
211
program vec0
implicit none real p(10), q(7,10); integer :: vi(3), vj(5)
integer i, j vi=(/7,1,5/);
vj=(/2,4,6,8,10/)
p=1;
q=2;
p(vi)=3.0
write(*,’(19x,"Массив p после модификации по векторному индексу vi")’)
write(*,’(" Индекс массива p:",
10i5)’)(i,i=1,10)
write(*,’(" Содержимое p: ",10f5.0)’) (p(i),i=1,10)
write(*,’(" Индексы секции
:",3i5)’) vi write(*,’(" Содержимое секции p(vi): ",3f5.0)’) p(vi)
q(2,vj)=4.0
write(*,’(19x,"Матрица q после модификации по векторному индексу vj")’)
write(*,’(" Номер столбца:",
10(i5))’)(i,i=1,10)
write(*,’(" Номер строки")’)
write(*,’(5x,i2,9x,10f5.0)’) (i,(q(i,j),j=1,10),i=1,7);
q(vi,vj)=5.0
write(*,’(19x,"Матрица q после модификации по векторным индексам vi и vj")’)
write(*,’(" Номер столбца:",
10(i5))’)(i,i=1,10)
write(*,’(" Номер строки")’)
write(*,’(5x,i2,9x,10f5.0)’) (i,(q(i,j),j=1,10),i=1,7);
end
Массив p после модификации по векторному индексу vi
Индекс массива p:
1 2
3 4
5 6
7 8
9 10
Содержимое p:
3.
1.
1.
1.
3.
1.
3.
1.
1.
1.
Индексы секции
:
7 1
5
Содержимое секции p(vi):
3.
3.
3.
Матрица q после модификации по векторному индексу vj
Номер столбца:
1 2
3 4
5 6
7 8
9 10
Номер строки
1 2.
2.
2.
2.
2.
2.
2.
2.
2.
2.
2 2.
4.
2.
4.
2.
4.
2.
4.
2.
4.
3 2.
2.
2.
2.
2.
2.
2.
2.
2.
2.
4 2.
2.
2.
2.
2.
2.
2.
2.
2.
2.
5 2.
2.
2.
2.
2.
2.
2.
2.
2.
2.
6 2.
2.
2.
2.
2.
2.
2.
2.
2.
2.
7 2.
2.
2.
2.
2.
2.
2.
2.
2.
2.
Матрица q после модификации по векторным индексам vi и vj
Номер столбца:
1 2
3 4
5 6
7 8
9 10
Номер строки
1 2.
5.
2.
5.
2.
5.
2.
5.
2.
5.
2 2.
4.
2.
4.
2.
4.
2.
4.
2.
4.
3 2.
2.
2.
2.
2.
2.
2.
2.
2.
2.
4 2.
2.
2.
2.
2.
2.
2.
2.
2.
2.
5 2.
5.
2.
5.
2.
5.
2.
5.
2.
5.
6 2.
2.
2.
2.
2.
2.
2.
2.
2.
2.
7 2.
5.
2.
5.
2.
5.
2.
5.
2.
5.
212