ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 06.12.2023
Просмотров: 334
Скачиваний: 6
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
0x7fff07a6ed28 0x7fff07a6ed30 0x7fff07a6ed38 // только на 2 байта ???
171
18. Функция форматного вывода printf позволяет отпечатать адрес, по которому хранится значение переменной (в частности, и элемента массива, расположенного в оперативной памяти). Для вывода адре- са используется спецификатор формата %p (от pointer – указатель):
#include
// Файл matra.c int main()
{ int a[3][4]
={ { 0,1,2,3}, { 10, 11, 12, 13},
{20, 21, 22, 23} };
int i, j;
// Если a - имя массива, то обозначение printf("a= %p
&a=%p\n",a, &a);
// &a тождественно обозначению a и означает printf("%s \n","
Индекс ");
// адрес начального элемента массива.
printf("%s
%s
%s\n","i-й строки","
&a[i]
", "
a[i]
");
for (i=0;i<3; i++)
// Если a - матрица, то &a[i]
printf("%5i
%17p
%13p\n",i,&(a[i]),a[i]); // тождественно a[i] и означает
// адрес элемента a[i] [0].
printf("\n%10s
%15s
%15s
%15s
%15s\n","
\\
j
",
"0-й столбец","1-й столбец", "2-й столбец","3-й столбец");
printf("%7s
%17s
%15s %16s %16s\n","i
\\ ",
"
&
a[i][j]","
&
a[i][j]", "
&
a[i][j]","
&
a[i][j]");
printf("%7s
\n", "
\\");
for (i=0;i<3;i++)
// Пример вывода адресов элементов матрицы и
{ printf("%d-я строка",i); // содержимого этих элементов:
for (j=0;j<4;j++) printf("
%12p
%2d ", &a[i][j], a[i][j]); printf("\n");
}
printf("\n &i=%p i=%d\n",&i,i); // Пример вывода адресов и содержимого printf(
" &j=%p j=%d\n",&j,j); // простых переменных.
return 0;
}
Результат работы этой программы:
a= 0xfee3f290
&a=0xfee3f290
Индекс i-й строки
&a[i]
a[i]
0 0xfee3f290 0xfee3f290 1
0xfee3f2a0 0xfee3f2a0 2
0xfee3f2b0 0xfee3f2b0
\
j
0-й столбец
1-й столбец
2-й столбец
3-й столбец i
\
&
a[i][j]
&
a[i][j]
&
a[i][j]
&
a[i][j]
\
0-я строка
0xfee3f290 0
0xfee3f294 1
0xfee3f298 2
0xfee3f29c
3 1-я строка
0xfee3f2a0 10 0xfee3f2a4 11 0xfee3f2a8 12 0xfee3f2ac
13 2-я строка
0xfee3f2b0 20 0xfee3f2b4 21 0xfee3f2b8 22 0xfee3f2bc
23
&i=0xfee3f28c i=3
&j=0xfee3f288
j=4
Значения адресов, приведённые здесь, — четырёхбайтовые, которые использовались на ранних версиях компиляторов.
172
19. Различие в очередности расположения в оперативной памяти эле- ментов многомерных ФОРТРАН- и СИ-массивов надо учитывать при совмещённом программировании. ФОРТРАН-функция reshape позволяет изменять форму массива (в частности, преобразовать оче- рёдность ФОРТРАН-расположения в СИ-расположение.
program test3d2; implicit none; integer a(4,3,2), c(2,3,4), b (4,3,2)
integer i,j,k
! Обратите внимание на удобство forall (i=1:4,j=1:3,k=1:2)
! использования оператора a(i,j,k)=100*i+10*j+k
! forall вместо написания endforall
! трёхкратного вложенного цикла write(*,*) ’ ФОРТРАН-расположение
:’; write(*,’(4i4)’) a write(*,*) ’ СИ-расположение из ФОРТРАНа:’
c=reshape(a, (/2,3,4/),order=(/3,2,1/));
write(*,’(6i4)’) c write(*,*) ’ ФОРТРАН-расположение из
СИ:’
b=reshape(c,(/4,3,2/), order=(/3,2,1/));
write(*,’(4i4)’) b end
20. В зависимости от момента выделения памяти под массив (на эта- пе компиляции или во время выполнения программы) различают статические и динамические массивы соответственно.
173
4.2
Одномерные массивы (простые примеры)
1. Описания ФОРТРАН-массива и варианты его вывода.
2. Варианты описания одномерного массива в СИ.
3. Изменение диапазона индексов ФОРТРАН-массива.
4.2.1
Описание и вывод одномерного ФОРТРАН-массива.
program exarr1
! В программе описывается статический implicit none
! массив k из четырех элементов целого integer k(4) / 40, 30, 20, 10 /
! типа. k(1)=40, ..., k(4)=10.
integer i
! Удобно при выводе:
write(*,*) k
!
массива в одну строку;
write(*,*) (k(i),i=1,4)
!
линейной выборки из массива;
write(*,*) (i,k(i),i=4,1,-1)
!
элементов в обратном порядке;
write(*,1000)
write(*,1001) (i, k(i), i=1,4,1) ! Удобно при форматированном выводе.
write(*,1000)
do i=4,1,-1
! Удобно, если между заголовком цикла и write(*,1001) i, k(i)
! оператором вывода есть расчетная часть enddo
1000 format(5x,’
индекс значение’/5x,’
i k[i]
’)
1001 format(1x,’
’,i7,i14)
end
Результат работы exarr1.out
!
Задать число элементов в описании массива
40 30 20 10
!
можно не только целым числом, но и имено-
40 30 20 10
!
ванной константой целого типа, например:
4 10 3 20 2 30 1 40
!
integer n индекс значение
!
parameter (n=4)
i k[i]
!
real a(n), b(n), c(n)
1 40
!
2 30
!
Ясно, что изменить число элементов проще
3 20
!
один раз в операторе именования константы.
4 10
!
Если же оно встречается и в теле программы индекс значение
!
то выигрыш от именованной константы очевиден i
k[i]
!
4 10
!
ФОРТРАН-95 позволяет описать и задать
3 20
!
именованную константу одним оператором:
2 30
!
1 40
!
integer, parameter :: n=4 1. Число в круглых скобках после имени ФОРТРАН-массива при его описании, равно и количеству элементов массива, и индексу послед- него элемента массива, так как по умолчанию номер начального элемента равен единице.
174
2. При описании одномерного массива в СИ число в квадратных скоб- ках тоже трактуется как число элементов, но индекс начального элемента всегда полагается равным нулю (поэтому, в частности, С- индекс последнего элемента на единицу меньше числа элементов).
3. В ФОРТРАНе задать начальные значения элементов массива мож- но не только операторами описания, но и посредством оператора data, который выполняется только при компиляции.
program exarr2
!
Задать начальные значения элементам массива implicit none
! можно и посредством невыполняемого при работе integer n
! программы оператора data.
parameter (n=5000)
integer k(n), i data k / n*10 /
write(*,1000); write(*,1001) (i, k(i), i=1,4,1)
1000 format(5x,’
индекс значение’/5x,’
i k[i]
’)
1001 format(1x,’
’,i7,i14)
end
! Правда, при этом размер исполнимого индекс значение
! кода в байтах окажется равным:
i k[i]
!
n g77
gfortran
1 10
!
5000 26470 26115 2
10
!
10000 48390 48067 3
10
!
20000 88390 88067 4
10
!
40000 166470 166115 4.
program exarr3; implicit none ! При компиляции без инициализации integer n
! размер исполнимого кода МЕНЬШЕ:
parameter (n=5000)
!
n g77
gfortran integer k(n), i
!
5000 6546 6113
do i=1,n
!
10000 8390 8039
k(i)=i
!
20000 8390 8039
enddo
!
40000 6534 6127
write(*,1000); write(*,1001) (i, k(i), i=1,4,1)
1000 format(5x,’
индекс значение’/5x,’
i k[i]
’)
1001 format(1x,’
’,i7,i14)
end
5.
program exarr4
! При "лобовой" попытке описать массив, размер implicit none
! которого должен задаваться не константой, а integer n, i
! переменной, ФОРТРАН-компиляторы сообщат об ошибке!!!
write(*,*) ’Введи размер массива’
read (*,*) n
! In file rdrarr0c.f90:6
integer k(n)
! integer k(n)
end
!
1
Unexpected data declaration statement at (1)
175
6. Массив можно описать не только посредством операторов описания типа, но и оператором dimension:
integer k(4)
! Вектор целого типа из четырех элементов real a(10)
! Вещ. вектор из 10 элементов одинарной точности logical b(-5:5)
! Вектор из 11 элементов булева типа real(8) w(0:100)
! Вещ. вектор из 101 элемента удвоенной точности double precision v(50) ! --"--"--"--"--
50 --"--"--"--"--"--"--"-- character(33) c(13)
! Каждый из 13 элементов вмещает до 33 литер dimension q(153)
! Все 153 элемента массива q типа REAL, т.к. в
!
данном контексте действует правило умолчания
Служебное слово dimension предназначено для явного описания именно структуры (формы) массива, не касаясь типа его элементов.
7. Современный ФОРТРАН допускает в одном операторе описания размещение через запятую всех атрибутов, указывающих свойства массива. Cписок атрибутов завершается двойным двоеточием
::
,
за которым следуют имена описываемых объектов.
program ar95; implicit none
!
Конструкция вида:
integer, parameter
:: n=4
! (/ список значений /)
real, dimension (n) :: b=(/2.1,2.2,2.3,2.4/)
!
называется integer, dimension(3), parameter :: a=(/3, 2, 1 /) ! конструктором массива.
integer c(5), i
! Встроенный DO-цикл для указания в операторе data (c(i),i=1,3) /3*1/
! DATA значений НЕ ВСЕХ элементов массива.
integer d(2:1), e(5,1000,3:2) ! ФОРТРАН-95 допускает массивы нулевой
! длины, т.е. не содержащие ни одного
! элемента, если хотя бы по одному
! измерению нижняя граница больше верхней.
write(*,*) ’a=’,a; write(*,*) ’b=’,b; write(*,*) ’c=’,c write(*,*) ’ Размер массива c равен’,size(c)
write(*,*) ’ Размеры массивов d и e равны:’,size(d), size(e)
write(*,*) ’ Cодержимое массива d и e:’, d, e end a=
3 2
1
! Результат b=
2.100000 2.200000 2.300000 2.400000
! работы c=
1 1
1 0
0 ! программы
Размер массива c равен
5
! ar95
Размеры массивов d и e равны:
0 0
!
Cодержимое массива d и e:
!
Оператор DATA без встроенного DO-цикла по индексу с необходимостью требует указания значений всех элементов массива.
176
4.2.2
Описание и вывод одномерного массива в СИ.
1.
#include
int main()
{int k[4]={40, 30, 20, 10}, i; // Квадратные скобки - конструктор массива printf(" k:
%i\n",*k);
printf(" k:
%3i %3i %3i %3i\n",k[0],k[1],k[2],k[3]);
printf("%3s индекс значение значение\n"," ");
printf("%3s i
k[i]
*(k+i) \n"," ");
for (i=3;i>=0;i--) printf(" %8i %1s %8i %1s %8i\n",i," ",k[i]," ",*(k+i));
printf("%3s индекс значение значение\n"," ");
printf("%3s i
k[i]
*(k+i) \n"," ");
for (i=0;i<=3;i++) printf(" %8i %1s %8i %1s %8i\n",i," ",k[i]," ",*(k+i));
return 0;
}
k:
40
//
Результат работы этой программы.
k:
40 30 20 10
//
индекс значение значение
//
i k[i]
*(k+i)
//
Помним, что в СИ по умолчанию
3 10 10
//
идентификатор массива -- это
2 20 20
//
указатель на его начальный
1 30 30
//
элемент с индексом 0 0
40 40
//
Поэтому синтаксически доступ к индекс значение значение
//
нужному элементу массива можно i
k[i]
*(k+i)
//
оформить двумя способами:
0 40 40
//
1) по традиции -- через k[i]
1 30 30
//
или
2 20 20
//
2) через операцию разыменования
3 10 10
//
указателя
*(k+i)
Запись *(k+i) означает:
(a) извлечь адрес &k[0] начального элемента переменной k;
(b) увеличить его на i* sizeof(int) – число байт под i элементов типа int, находя адрес k[i], и получить доступ
(c) через операцию разыменования *(k+i) к содержимому k[i].
Функция printf позволяет указать в списке вывода через опера- цию разыменования идентификатор массива *k. Однако в отличие от ФОРТРАНа, который выводил весь массив, отпечатается толь- ко один нулевой элемент. Для вывода n элементов массива нужно написать функцию, например:
void prtfor(int *a, int n)
{ int i;
for (i=0;i}
177
2. Проинициализировать массив при его C-описании (т.е. задать на- чальные значения его элементов) можно двумя способами:
• явно указывая число элементов массива в квадратных скобках
(как в предыдущей программе) перед списком инициализации;
• не указывая в квадратных скобках (при их наличии) количество элементов массива (в этом случае число элементов определяется компилятором по списку инициализации). Например,
#include
int main()
{int k[]={40, 30, 20, 10};
// Описан массив k из 4 элементов.
int i, num, num1;
printf(" k:
%i\n",*k);
printf(" k:
%3i %3i %3i %3i\n",k[0],k[1],k[2],k[3]);
printf("%3s Индекс значение значение\n"," ");
printf("%3s i
k[i]
*(k+i) \n"," ");
for (i=3;i>=0;i--) printf("%8i
%8i
%8i\n",i, k[i], *(k+i));
printf(" %8i %8i \n", k[6], *(k+6)); // Элемента k[6] в массиве НЕТ.
printf(" Одно int-значение размещается в %d байтах.\n",sizeof(int));
printf(" Массив занимает
%d байт памяти.\n", sizeof(k));
num=sizeof(k)/sizeof(int);
printf(" В массиве %d элементов типа int.\n", sizeof(k)/sizeof(int));
num1=num-1;
printf(" Индекс последнего элемента = %d.\n", num1);
printf("%3s i
k[i]
*(k+i) \n"," ");
for (i=num1;i>=0;i--) printf("%8i
%8i
%8i\n",i, k[i], *(k+i));
return 0; }
k:
40
// CИ для НЕ КОНТРОЛИРУЕТ значения k:
40 30 20 10
// индексов массива на предмет их индекс значение значение // выхода за пределы, допускаемые i
k[i]
*(k+i)
// описанием. Поэтому есть шанс
3 10 10
// заставить программу извлечь
2 20 20
// содержимое несуществующего
1 30 30
// элемента, например, k[6].
0 40 40
//
-17991432 -17991432
// <-- это его значение - СЛУЧАЙНО
//
и может меняться от пропуска
Одно int-значение размещается в 4 байтах.// к пропуску. Для выяснения
Массив занимает
16 байт памяти.
// числа элементов в массиве
Массив состоит из 4 элементов типа int.
// удобно использовать
Индекс последнего элемента = 3.
// операцию i
k[i]
*(k+i)
//
sizeof(имя массива).
3 10 10
// Она в случае применения
2 20 20
// к имени массива возвращает
1 30 30
// число байт памяти, занятое
0 40 40
// компилятором под массив.
178
3. В прикладных задачах иногда желательно объявить массив, размер которого задается переменной, вычисляемой внутри тела програм- мы. Компилятор gcc предоставляет такую возможность:
#include
int main()
{ int n, i;
printf("Введи число коэффициентов полинома:\n");
scanf ("%d", &n); printf(" n=%d\n",n);
double a[n];
printf(" double-значение занимает %d байтов.\n", sizeof(double));
printf(" double-массив занимает %d байтов.\n", sizeof(a));
printf(" double-массив состоит из %d элементов типа double.\n",
sizeof(a)/sizeof(double));
for (i=0;i{ printf("введи %d коэффициент\n",i); scanf("%le", &a[i]);}
printf(" i a[i]\n");
for (i=0;iВведи число коэффициентов полинома:
3
n=3
double-значение размещается в 8 байтах.
double-массив занимает
24 байт памяти.
double-массив состоит из
3 элементов типа double.
введи 0 коэффициент
1
// В отличие от предыдущих примеров массив (a)
введи 1 коэффициент
// в данном случае -- динамический, поскольку
1.3
// его размер определяется во время работы введи 2 коэффициент
// программы. Правда, освободить память, занятую
2.5e-2
// им таким образом, можно только i
a[i]
//
0 1.0000000000000000e+00 // заключив описание double a[n] с операторами,
1 1.3000000000000000e+00 // использующими его в фигурные скобки, то есть
2 2.5000000000000001e-02 // оформляя блок:
#include
int main()
{ int n, i;
printf("Введи число элементов массива:\n");
scanf ("%d", &n);
printf(" n=%d\n",n);
{ double a[n];
printf(" double-значение занимает %d байтов.\n", sizeof(double));
printf(" double-массив занимает %d байтов.\n", sizeof(a));
printf(" double-массив состоит из %d элементов типа double.\n",
sizeof(a)/sizeof(double));
}
double a[n+5]; printf(" double-значение : %d байтов.\n", sizeof(double));
printf(" double-массив
: %d байт памяти.\n", sizeof(a));
printf(" double-массив из
%d элементов.\n", sizeof(a)/sizeof(double));
return 0; }
179
Введи число элементов массива:
// Результат работы
3
// последней программы:
n=3
double-значение размещается в 8 байтах.
double-массив занимает
24 байт памяти.
double-массив состоит из
3 элементов типа double.
double-значение размещается в 8 байтах.
double-массив занимает
64 байт памяти.
double-массив состоит из
8 элементов типа double.
Без организации блока компилятор выдаст сообщения:
rdrarr0b1.c:12: error: redeclaration of ’a’ with no linkage rdrarr0b1.c:7: error: previous declaration of ’a’ was here
Вообще говоря, для организации динамических массивов в СИ ис- пользуются функции malloc() и free() (#include; cм.,
например, man malloc; подробнее см.12.2).
4. Как и в случае ФОРТРАНа, инициализация массива во время ком- пиляции приводит к значительному увеличению исполнимого кода.
Например,
int main()
//
n gcc c++
{ int k[5000]={1,2,3,4,5,
//
5000 24740 24804 6,7,8,9,10};
//
10000 44740 44804
//
20000 84740 84804
return 0;
//
40000 164740 164804
}
Отсутствие инициализации при компиляции приводит к принципи- ально меньшему размеру исполнимого кода:
int main()
//
n gcc c++
{ int k[5000];
//
5000 4622 4686
//
10000 4622 4686
//
20000 4622 4686
return 0;
//
40000 4622 4686
}
5. С++ наряду с процедурно-ориентированными возможностями язы- ка C, для работы с одномерными массивами предоставляет класс vector, который входит в библиотеку стандартных шаблонов, опре- деляя динамический массив как класс (подробнее см., например,
[9]).
180
4.2.3
Изменение диапазона индексов ФОРТРАН-массива.
В С и С++ индекс начального элемента массива всегда нулевой.
ФОРТРАН позволяет изменять диапазон допустимых индексов массива:
program rdrarr1; implicit none real(8) a(-2:2) / 1.1, 1.2, 1.3, 1.4, 1.5/;
integer i write(*,1000); write(*,1001) (i, a(i), i=-2,2)
1000 format(5x,’
индекс значение’/5x,’
i a[i]
’)
1001 format(1x,’
’,i7,d23.16)
end индекс значение
! Как следует исправить i
a[i]
! текст исходной программы,
-2 0.1100000023841858E+01
! чтобы верными в пределах
-1 0.1200000047683716E+01
! шестнадцати значащих цифр
0 0.1299999952316284E+01
! оказались значения всех
1 0.1399999976158142E+01
! заданных элементов массива, а
2 0.1500000000000000E+01
! не только a[2] ???
#include // CИ-аналог ФОРТРАН-программы rdrarr1 и его int main()
//
результаты.
{double a[5]={1.1, 1.2, 1.3, 1.4, 1.5};
int i;
printf("%3s индекс %5s значение %15s значение\n"," "," "," ");
printf("%3s i
%5s a[i]
%15s
*(a+i)
\n"," "," "," ");
for (i=0;i<=4;i++)
printf(" %8i %1s %23.16e %1s %23.16e\n",i," ", a[i]," ", *(a+i));
return 0;
}
индекс значение значение
// Помним, что i
a[i]
*(a+i)
// по умолчанию
0 1.1000000000000001e+00 1.1000000000000001e+00
// С-константы
1 1.2000000000000000e+00 1.2000000000000000e+00
// задаются
2 1.3000000000000000e+00 1.3000000000000000e+00
// с удвоенной
3 1.3999999999999999e+00 1.3999999999999999e+00
// точностью.
4 1.5000000000000000e+00 1.5000000000000000e+00
Поэтому, если придётся С-программу, ориентированную на задачу вы- числительного характера, переводить на ФОРТРАН (в котором обычно вещественные константы по умолчанию полагаются четырёхбайтовыми),
то НЕ будем игнорировать соответствующий ФОРТРАН-синтаксис. На- пример, не real(8), parameter :: x=1.3
а real(8), parameter :: x=1.3d0 или real(8),parameter :: x=1.3_8.
181
171
18. Функция форматного вывода printf позволяет отпечатать адрес, по которому хранится значение переменной (в частности, и элемента массива, расположенного в оперативной памяти). Для вывода адре- са используется спецификатор формата %p (от pointer – указатель):
#include
// Файл matra.c int main()
{ int a[3][4]
={ { 0,1,2,3}, { 10, 11, 12, 13},
{20, 21, 22, 23} };
int i, j;
// Если a - имя массива, то обозначение printf("a= %p
&a=%p\n",a, &a);
// &a тождественно обозначению a и означает printf("%s \n","
Индекс ");
// адрес начального элемента массива.
printf("%s
%s
%s\n","i-й строки","
&a[i]
", "
a[i]
");
for (i=0;i<3; i++)
// Если a - матрица, то &a[i]
printf("%5i
%17p
%13p\n",i,&(a[i]),a[i]); // тождественно a[i] и означает
// адрес элемента a[i] [0].
printf("\n%10s
%15s
%15s
%15s
%15s\n","
\\
j
",
"0-й столбец","1-й столбец", "2-й столбец","3-й столбец");
printf("%7s
%17s
%15s %16s %16s\n","i
\\ ",
"
&
a[i][j]","
&
a[i][j]", "
&
a[i][j]","
&
a[i][j]");
printf("%7s
\n", "
\\");
for (i=0;i<3;i++)
// Пример вывода адресов элементов матрицы и
{ printf("%d-я строка",i); // содержимого этих элементов:
for (j=0;j<4;j++) printf("
%12p
%2d ", &a[i][j], a[i][j]); printf("\n");
}
printf("\n &i=%p i=%d\n",&i,i); // Пример вывода адресов и содержимого printf(
" &j=%p j=%d\n",&j,j); // простых переменных.
return 0;
}
Результат работы этой программы:
a= 0xfee3f290
&a=0xfee3f290
Индекс i-й строки
&a[i]
a[i]
0 0xfee3f290 0xfee3f290 1
0xfee3f2a0 0xfee3f2a0 2
0xfee3f2b0 0xfee3f2b0
\
j
0-й столбец
1-й столбец
2-й столбец
3-й столбец i
\
&
a[i][j]
&
a[i][j]
&
a[i][j]
&
a[i][j]
\
0-я строка
0xfee3f290 0
0xfee3f294 1
0xfee3f298 2
0xfee3f29c
3 1-я строка
0xfee3f2a0 10 0xfee3f2a4 11 0xfee3f2a8 12 0xfee3f2ac
13 2-я строка
0xfee3f2b0 20 0xfee3f2b4 21 0xfee3f2b8 22 0xfee3f2bc
23
&i=0xfee3f28c i=3
&j=0xfee3f288
j=4
Значения адресов, приведённые здесь, — четырёхбайтовые, которые использовались на ранних версиях компиляторов.
172
19. Различие в очередности расположения в оперативной памяти эле- ментов многомерных ФОРТРАН- и СИ-массивов надо учитывать при совмещённом программировании. ФОРТРАН-функция reshape позволяет изменять форму массива (в частности, преобразовать оче- рёдность ФОРТРАН-расположения в СИ-расположение.
program test3d2; implicit none; integer a(4,3,2), c(2,3,4), b (4,3,2)
integer i,j,k
! Обратите внимание на удобство forall (i=1:4,j=1:3,k=1:2)
! использования оператора a(i,j,k)=100*i+10*j+k
! forall вместо написания endforall
! трёхкратного вложенного цикла write(*,*) ’ ФОРТРАН-расположение
:’; write(*,’(4i4)’) a write(*,*) ’ СИ-расположение из ФОРТРАНа:’
c=reshape(a, (/2,3,4/),order=(/3,2,1/));
write(*,’(6i4)’) c write(*,*) ’ ФОРТРАН-расположение из
СИ:’
b=reshape(c,(/4,3,2/), order=(/3,2,1/));
write(*,’(4i4)’) b end
20. В зависимости от момента выделения памяти под массив (на эта- пе компиляции или во время выполнения программы) различают статические и динамические массивы соответственно.
173
4.2
Одномерные массивы (простые примеры)
1. Описания ФОРТРАН-массива и варианты его вывода.
2. Варианты описания одномерного массива в СИ.
3. Изменение диапазона индексов ФОРТРАН-массива.
4.2.1
Описание и вывод одномерного ФОРТРАН-массива.
program exarr1
! В программе описывается статический implicit none
! массив k из четырех элементов целого integer k(4) / 40, 30, 20, 10 /
! типа. k(1)=40, ..., k(4)=10.
integer i
! Удобно при выводе:
write(*,*) k
!
массива в одну строку;
write(*,*) (k(i),i=1,4)
!
линейной выборки из массива;
write(*,*) (i,k(i),i=4,1,-1)
!
элементов в обратном порядке;
write(*,1000)
write(*,1001) (i, k(i), i=1,4,1) ! Удобно при форматированном выводе.
write(*,1000)
do i=4,1,-1
! Удобно, если между заголовком цикла и write(*,1001) i, k(i)
! оператором вывода есть расчетная часть enddo
1000 format(5x,’
индекс значение’/5x,’
i k[i]
’)
1001 format(1x,’
’,i7,i14)
end
Результат работы exarr1.out
!
Задать число элементов в описании массива
40 30 20 10
!
можно не только целым числом, но и имено-
40 30 20 10
!
ванной константой целого типа, например:
4 10 3 20 2 30 1 40
!
integer n индекс значение
!
parameter (n=4)
i k[i]
!
real a(n), b(n), c(n)
1 40
!
2 30
!
Ясно, что изменить число элементов проще
3 20
!
один раз в операторе именования константы.
4 10
!
Если же оно встречается и в теле программы индекс значение
!
то выигрыш от именованной константы очевиден i
k[i]
!
4 10
!
ФОРТРАН-95 позволяет описать и задать
3 20
!
именованную константу одним оператором:
2 30
!
1 40
!
integer, parameter :: n=4 1. Число в круглых скобках после имени ФОРТРАН-массива при его описании, равно и количеству элементов массива, и индексу послед- него элемента массива, так как по умолчанию номер начального элемента равен единице.
174
2. При описании одномерного массива в СИ число в квадратных скоб- ках тоже трактуется как число элементов, но индекс начального элемента всегда полагается равным нулю (поэтому, в частности, С- индекс последнего элемента на единицу меньше числа элементов).
3. В ФОРТРАНе задать начальные значения элементов массива мож- но не только операторами описания, но и посредством оператора data, который выполняется только при компиляции.
program exarr2
!
Задать начальные значения элементам массива implicit none
! можно и посредством невыполняемого при работе integer n
! программы оператора data.
parameter (n=5000)
integer k(n), i data k / n*10 /
write(*,1000); write(*,1001) (i, k(i), i=1,4,1)
1000 format(5x,’
индекс значение’/5x,’
i k[i]
’)
1001 format(1x,’
’,i7,i14)
end
! Правда, при этом размер исполнимого индекс значение
! кода в байтах окажется равным:
i k[i]
!
n g77
gfortran
1 10
!
5000 26470 26115 2
10
!
10000 48390 48067 3
10
!
20000 88390 88067 4
10
!
40000 166470 166115 4.
program exarr3; implicit none ! При компиляции без инициализации integer n
! размер исполнимого кода МЕНЬШЕ:
parameter (n=5000)
!
n g77
gfortran integer k(n), i
!
5000 6546 6113
do i=1,n
!
10000 8390 8039
k(i)=i
!
20000 8390 8039
enddo
!
40000 6534 6127
write(*,1000); write(*,1001) (i, k(i), i=1,4,1)
1000 format(5x,’
индекс значение’/5x,’
i k[i]
’)
1001 format(1x,’
’,i7,i14)
end
5.
program exarr4
! При "лобовой" попытке описать массив, размер implicit none
! которого должен задаваться не константой, а integer n, i
! переменной, ФОРТРАН-компиляторы сообщат об ошибке!!!
write(*,*) ’Введи размер массива’
read (*,*) n
! In file rdrarr0c.f90:6
integer k(n)
! integer k(n)
end
!
1
Unexpected data declaration statement at (1)
175
6. Массив можно описать не только посредством операторов описания типа, но и оператором dimension:
integer k(4)
! Вектор целого типа из четырех элементов real a(10)
! Вещ. вектор из 10 элементов одинарной точности logical b(-5:5)
! Вектор из 11 элементов булева типа real(8) w(0:100)
! Вещ. вектор из 101 элемента удвоенной точности double precision v(50) ! --"--"--"--"--
50 --"--"--"--"--"--"--"-- character(33) c(13)
! Каждый из 13 элементов вмещает до 33 литер dimension q(153)
! Все 153 элемента массива q типа REAL, т.к. в
!
данном контексте действует правило умолчания
Служебное слово dimension предназначено для явного описания именно структуры (формы) массива, не касаясь типа его элементов.
7. Современный ФОРТРАН допускает в одном операторе описания размещение через запятую всех атрибутов, указывающих свойства массива. Cписок атрибутов завершается двойным двоеточием
::
,
за которым следуют имена описываемых объектов.
program ar95; implicit none
!
Конструкция вида:
integer, parameter
:: n=4
! (/ список значений /)
real, dimension (n) :: b=(/2.1,2.2,2.3,2.4/)
!
называется integer, dimension(3), parameter :: a=(/3, 2, 1 /) ! конструктором массива.
integer c(5), i
! Встроенный DO-цикл для указания в операторе data (c(i),i=1,3) /3*1/
! DATA значений НЕ ВСЕХ элементов массива.
integer d(2:1), e(5,1000,3:2) ! ФОРТРАН-95 допускает массивы нулевой
! длины, т.е. не содержащие ни одного
! элемента, если хотя бы по одному
! измерению нижняя граница больше верхней.
write(*,*) ’a=’,a; write(*,*) ’b=’,b; write(*,*) ’c=’,c write(*,*) ’ Размер массива c равен’,size(c)
write(*,*) ’ Размеры массивов d и e равны:’,size(d), size(e)
write(*,*) ’ Cодержимое массива d и e:’, d, e end a=
3 2
1
! Результат b=
2.100000 2.200000 2.300000 2.400000
! работы c=
1 1
1 0
0 ! программы
Размер массива c равен
5
! ar95
Размеры массивов d и e равны:
0 0
!
Cодержимое массива d и e:
!
Оператор DATA без встроенного DO-цикла по индексу с необходимостью требует указания значений всех элементов массива.
176
4.2.2
Описание и вывод одномерного массива в СИ.
1.
#include
int main()
{int k[4]={40, 30, 20, 10}, i; // Квадратные скобки - конструктор массива printf(" k:
%i\n",*k);
printf(" k:
%3i %3i %3i %3i\n",k[0],k[1],k[2],k[3]);
printf("%3s индекс значение значение\n"," ");
printf("%3s i
k[i]
*(k+i) \n"," ");
for (i=3;i>=0;i--) printf(" %8i %1s %8i %1s %8i\n",i," ",k[i]," ",*(k+i));
printf("%3s индекс значение значение\n"," ");
printf("%3s i
k[i]
*(k+i) \n"," ");
for (i=0;i<=3;i++) printf(" %8i %1s %8i %1s %8i\n",i," ",k[i]," ",*(k+i));
return 0;
}
k:
40
//
Результат работы этой программы.
k:
40 30 20 10
//
индекс значение значение
//
i k[i]
*(k+i)
//
Помним, что в СИ по умолчанию
3 10 10
//
идентификатор массива -- это
2 20 20
//
указатель на его начальный
1 30 30
//
элемент с индексом 0 0
40 40
//
Поэтому синтаксически доступ к индекс значение значение
//
нужному элементу массива можно i
k[i]
*(k+i)
//
оформить двумя способами:
0 40 40
//
1) по традиции -- через k[i]
1 30 30
//
или
2 20 20
//
2) через операцию разыменования
3 10 10
//
указателя
*(k+i)
Запись *(k+i) означает:
(a) извлечь адрес &k[0] начального элемента переменной k;
(b) увеличить его на i* sizeof(int) – число байт под i элементов типа int, находя адрес k[i], и получить доступ
(c) через операцию разыменования *(k+i) к содержимому k[i].
Функция printf позволяет указать в списке вывода через опера- цию разыменования идентификатор массива *k. Однако в отличие от ФОРТРАНа, который выводил весь массив, отпечатается толь- ко один нулевой элемент. Для вывода n элементов массива нужно написать функцию, например:
void prtfor(int *a, int n)
{ int i;
for (i=0;i
177
2. Проинициализировать массив при его C-описании (т.е. задать на- чальные значения его элементов) можно двумя способами:
• явно указывая число элементов массива в квадратных скобках
(как в предыдущей программе) перед списком инициализации;
• не указывая в квадратных скобках (при их наличии) количество элементов массива (в этом случае число элементов определяется компилятором по списку инициализации). Например,
#include
int main()
{int k[]={40, 30, 20, 10};
// Описан массив k из 4 элементов.
int i, num, num1;
printf(" k:
%i\n",*k);
printf(" k:
%3i %3i %3i %3i\n",k[0],k[1],k[2],k[3]);
printf("%3s Индекс значение значение\n"," ");
printf("%3s i
k[i]
*(k+i) \n"," ");
for (i=3;i>=0;i--) printf("%8i
%8i
%8i\n",i, k[i], *(k+i));
printf(" %8i %8i \n", k[6], *(k+6)); // Элемента k[6] в массиве НЕТ.
printf(" Одно int-значение размещается в %d байтах.\n",sizeof(int));
printf(" Массив занимает
%d байт памяти.\n", sizeof(k));
num=sizeof(k)/sizeof(int);
printf(" В массиве %d элементов типа int.\n", sizeof(k)/sizeof(int));
num1=num-1;
printf(" Индекс последнего элемента = %d.\n", num1);
printf("%3s i
k[i]
*(k+i) \n"," ");
for (i=num1;i>=0;i--) printf("%8i
%8i
%8i\n",i, k[i], *(k+i));
return 0; }
k:
40
// CИ для НЕ КОНТРОЛИРУЕТ значения k:
40 30 20 10
// индексов массива на предмет их индекс значение значение // выхода за пределы, допускаемые i
k[i]
*(k+i)
// описанием. Поэтому есть шанс
3 10 10
// заставить программу извлечь
2 20 20
// содержимое несуществующего
1 30 30
// элемента, например, k[6].
0 40 40
//
-17991432 -17991432
// <-- это его значение - СЛУЧАЙНО
//
и может меняться от пропуска
Одно int-значение размещается в 4 байтах.// к пропуску. Для выяснения
Массив занимает
16 байт памяти.
// числа элементов в массиве
Массив состоит из 4 элементов типа int.
// удобно использовать
Индекс последнего элемента = 3.
// операцию i
k[i]
*(k+i)
//
sizeof(имя массива).
3 10 10
// Она в случае применения
2 20 20
// к имени массива возвращает
1 30 30
// число байт памяти, занятое
0 40 40
// компилятором под массив.
178
3. В прикладных задачах иногда желательно объявить массив, размер которого задается переменной, вычисляемой внутри тела програм- мы. Компилятор gcc предоставляет такую возможность:
#include
int main()
{ int n, i;
printf("Введи число коэффициентов полинома:\n");
scanf ("%d", &n); printf(" n=%d\n",n);
double a[n];
printf(" double-значение занимает %d байтов.\n", sizeof(double));
printf(" double-массив занимает %d байтов.\n", sizeof(a));
printf(" double-массив состоит из %d элементов типа double.\n",
sizeof(a)/sizeof(double));
for (i=0;i
printf(" i a[i]\n");
for (i=0;i
3
n=3
double-значение размещается в 8 байтах.
double-массив занимает
24 байт памяти.
double-массив состоит из
3 элементов типа double.
введи 0 коэффициент
1
// В отличие от предыдущих примеров массив (a)
введи 1 коэффициент
// в данном случае -- динамический, поскольку
1.3
// его размер определяется во время работы введи 2 коэффициент
// программы. Правда, освободить память, занятую
2.5e-2
// им таким образом, можно только i
a[i]
//
0 1.0000000000000000e+00 // заключив описание double a[n] с операторами,
1 1.3000000000000000e+00 // использующими его в фигурные скобки, то есть
2 2.5000000000000001e-02 // оформляя блок:
#include
int main()
{ int n, i;
printf("Введи число элементов массива:\n");
scanf ("%d", &n);
printf(" n=%d\n",n);
{ double a[n];
printf(" double-значение занимает %d байтов.\n", sizeof(double));
printf(" double-массив занимает %d байтов.\n", sizeof(a));
printf(" double-массив состоит из %d элементов типа double.\n",
sizeof(a)/sizeof(double));
}
double a[n+5]; printf(" double-значение : %d байтов.\n", sizeof(double));
printf(" double-массив
: %d байт памяти.\n", sizeof(a));
printf(" double-массив из
%d элементов.\n", sizeof(a)/sizeof(double));
return 0; }
179
Введи число элементов массива:
// Результат работы
3
// последней программы:
n=3
double-значение размещается в 8 байтах.
double-массив занимает
24 байт памяти.
double-массив состоит из
3 элементов типа double.
double-значение размещается в 8 байтах.
double-массив занимает
64 байт памяти.
double-массив состоит из
8 элементов типа double.
Без организации блока компилятор выдаст сообщения:
rdrarr0b1.c:12: error: redeclaration of ’a’ with no linkage rdrarr0b1.c:7: error: previous declaration of ’a’ was here
Вообще говоря, для организации динамических массивов в СИ ис- пользуются функции malloc() и free() (#include
например, man malloc; подробнее см.12.2).
4. Как и в случае ФОРТРАНа, инициализация массива во время ком- пиляции приводит к значительному увеличению исполнимого кода.
Например,
int main()
//
n gcc c++
{ int k[5000]={1,2,3,4,5,
//
5000 24740 24804 6,7,8,9,10};
//
10000 44740 44804
//
20000 84740 84804
return 0;
//
40000 164740 164804
}
Отсутствие инициализации при компиляции приводит к принципи- ально меньшему размеру исполнимого кода:
int main()
//
n gcc c++
{ int k[5000];
//
5000 4622 4686
//
10000 4622 4686
//
20000 4622 4686
return 0;
//
40000 4622 4686
}
5. С++ наряду с процедурно-ориентированными возможностями язы- ка C, для работы с одномерными массивами предоставляет класс vector, который входит в библиотеку стандартных шаблонов, опре- деляя динамический массив как класс (подробнее см., например,
[9]).
180
4.2.3
Изменение диапазона индексов ФОРТРАН-массива.
В С и С++ индекс начального элемента массива всегда нулевой.
ФОРТРАН позволяет изменять диапазон допустимых индексов массива:
program rdrarr1; implicit none real(8) a(-2:2) / 1.1, 1.2, 1.3, 1.4, 1.5/;
integer i write(*,1000); write(*,1001) (i, a(i), i=-2,2)
1000 format(5x,’
индекс значение’/5x,’
i a[i]
’)
1001 format(1x,’
’,i7,d23.16)
end индекс значение
! Как следует исправить i
a[i]
! текст исходной программы,
-2 0.1100000023841858E+01
! чтобы верными в пределах
-1 0.1200000047683716E+01
! шестнадцати значащих цифр
0 0.1299999952316284E+01
! оказались значения всех
1 0.1399999976158142E+01
! заданных элементов массива, а
2 0.1500000000000000E+01
! не только a[2] ???
#include
//
результаты.
{double a[5]={1.1, 1.2, 1.3, 1.4, 1.5};
int i;
printf("%3s индекс %5s значение %15s значение\n"," "," "," ");
printf("%3s i
%5s a[i]
%15s
*(a+i)
\n"," "," "," ");
for (i=0;i<=4;i++)
printf(" %8i %1s %23.16e %1s %23.16e\n",i," ", a[i]," ", *(a+i));
return 0;
}
индекс значение значение
// Помним, что i
a[i]
*(a+i)
// по умолчанию
0 1.1000000000000001e+00 1.1000000000000001e+00
// С-константы
1 1.2000000000000000e+00 1.2000000000000000e+00
// задаются
2 1.3000000000000000e+00 1.3000000000000000e+00
// с удвоенной
3 1.3999999999999999e+00 1.3999999999999999e+00
// точностью.
4 1.5000000000000000e+00 1.5000000000000000e+00
Поэтому, если придётся С-программу, ориентированную на задачу вы- числительного характера, переводить на ФОРТРАН (в котором обычно вещественные константы по умолчанию полагаются четырёхбайтовыми),
то НЕ будем игнорировать соответствующий ФОРТРАН-синтаксис. На- пример, не real(8), parameter :: x=1.3
а real(8), parameter :: x=1.3d0 или real(8),parameter :: x=1.3_8.
181