![]() |
||||||||||||||||||||||||||||||||||
![]() |
||||||||||||||||||||||||||||||||||
![]() |
Инженерная методика адаптации приложения к гибридному кластеру с ускорителями на ПЛИСЧасть 1. Запись работы с многомерными массивами.С. С. Андреев, С. А. Дбар, А. О. Лацис, Е. А. Плоткина
Нам предстоит писать программу, в которой интенсивно используются многомерные массивы вещественных чисел. В языке C, в отличие, например, от Фортрана, не предусмотрено стандартного, простого и общепринятого способа записи для многих часто используемых действий с многомерными массивами. Например, не вполне очевидно, как можно передать в функцию в качестве аргумента многомерный массив вместе с его размерами, не известными во время компиляции. Разные программисты используют, при работе с многомерными массивами неизвестного заранее размера в программах на C, те или иные персональные ухищрения, которые совсем не делают их программы простыми и понятными. Чтобы не усложнять самим себе жизнь утомительными второстепенными деталями, условимся раз и навсегда о том, как именно мы будем далее записывать работу с многомерными массивами. Предлагаемый способ основан на возможностях языка C99, которые поддерживаются всеми известными авторам компиляторами C. Как известно, многомерный массив в C – это одномерный массив массивов на единицу меньшей размерности. Например, двумерный массив, объявленный как double da[100][30] — это, в действительности, одномерный массив длиной 100, элементами которого являются строки - одномерные массивы вещественных чисел длиной 30. Допустим, мы не знаем в момент написания программы, какой именно длины массив строк нам потребуется. Тогда нам придется объявить указатель на тип данных, элемент которых — это строка, то есть массив вещественных чисел длиной 30. Это делается не совсем очевидным способом. Запись: double (*da)[30]объявляет da как указатель на строки длиной по 30 вещественных чисел. Теперь мы можем выделить память обращением к malloc(), присвоить адрес выделенной памяти указателю da, а затем индексировать его как двумерный массив обычным порядком: da = (typeof(da))malloc( m*sizeof(*da)); .............. if ( (i < m) && (j < 30) ) f = da[i][j]; .............. Чтобы не путаться впредь со скобками и звездочками в объявлении указателей вроде da, напишем впрок макросы объявления указателей на массивы для наиболее употребительных размерностей, поместим их в файле dimension.h, и будем использовать при необходимости: #define DIM2( basetype, name, w1 ) basetype (*name)[w1] #define DIM3( basetype, name, w1, w2 ) basetype (*name)[w1][w2] #define DIM4( basetype, name, w1, w2, w3 ) basetype (*name)[w1][w2][w3] #define DIM5( basetype, name, w1, w2, w3, w4 ) basetype (*name)[w1][w2][w3][w4] .........и так далее до DIM7 Таким образом, макрос DIM2 служит для объявления указателей на строки двумерного массива, как мы продемонстрировали только что, макрос DIM3 – для объявления указателей на слои трехмерного массива, и т. д. Вместо приведенной выше записи double (*da)[30]мы теперь можем написать, не рискуя по ошибке поместить звездочку вне скобок: DIM2( double, da, 30 ) В реальной практике программирования, скорее всего, не только число строк в массиве, но и размер строки (число 30 в приведенных только что примерах) будет задаваться переменной, значение которой неизвестно в момент написания программы. Возможности языка C99, о которых мы говорили выше, позволяют нам, тем не менее, объявлять типы и указатели на них даже в этом случае, если в момент, когда все размеры станут известны, открыть новый блок: int main( int argc, char *argv[] ) { ......... m = 10000; n = 5000; { // Мы открыли новый блок, чтобы в пределах блока объявления // располагались, как положено, до исполняемых операторов: DIM2( double, da, n ); da = (typeof(da))malloc( m*sizeof(*da) ); .......... if ( (i < m) && (j < n) ) f = da[i][j]; .......... Мы научились объявлять указатели на многомерные массивы переменной (не известной во время компиляции) длины, индексировать обращения к элементам таких массивов традиционным для многомерных массивов способом, и все это — безо всяких специальных приемов, которые часто можно встретить в программах вычислительного характера на C, вроде массивов указателей на строки, или специальных макросов доступа к элементу массива. Отсутствие специальных приемов важно не только с точки зрения эффективности, но и с точки зрения простоты передачи данных в сопроцессор, который ни о каких специальных приемах ничего не знает. Нам осталось научиться передавать указатели на массивы переменных размеров в функции в качестве параметров, причем вместе с известными в момент вызова функции (но не во время компиляции!) размерами. С учетом рассказанного выше, задача решается тривиально. Указатель надо передать как void*, а при входе в функцию присвоить объявленному здесь же указателю на двумерный массив: void myfunc( void *pda, int m, int n ) { DIM2( double, da, n ) = (typeof(da))pda; .......... if ( (i < m) && (j<n) ) f = da[i][j]; .......... Мы показали, как пользоваться в программах на C многомерными массивами переменных размеров в рамках базовых возможностей языка, не прибегая к специальным приемам организации массивов и записи обращений к их элементам. Во всех приводимых далее примерах мы будем поступать именно таким способом, за исключением случаев, когда нам в явном виде захочется (или потребуется) поработать с многомерным массивом как с одномерным. Более подробный разбор предложенных здесь правил обращения с многомерными массивами можно найти в «Десяти простых шагах», в Шаге 2. ◄ Введение Часть 2 ► |
![]() |
||||||||||||||||||||||||||||||||
Тел. +7(499)220-79-72; E-mail: inform@kiam.ru |