Автор работы: Пользователь скрыл имя, 20 Марта 2011 в 00:49, лекция
Между массивами и указателями существует очень тесная связь, поэтому обычно их рассматривают вместе. Но, прежде чем исследовать эту связь, давайте проверим наши знания о массивах и пополним их, а уж после этого перейдем к изучению связи между массивами и указателями.
Массивы
Инициализация массивов и классы памяти
Указатели массивов
Функции, массивы и указатели
Использование указателей при работе с массивами
Операции с указателями
Многомерные массивы
Инициализация двумерного массива
Указатели и многомерные массивы
Функции и многомерные массивы
PR(ptr2);
printf(“ptr2 – ptr1 = %u\n", ptr2 – ptr1);
}
В
результате работы программы получены
следующие результаты:
ptr1 = 18, *ptr1 = 100, &ptr1 = 55990
ptr1 = 20, *ptr1 = 200, &ptr1 = 55990
ptr2 = 22, *ptr2 = 300, &ptr2 = 55992
ptr2 = 24, *ptr2 = 29808, &ptr2 -= 55992
ptr2
– ptr1 = 2
Программа
демонстрирует пять основных операций,
которые можно выполнять над переменными
типа указатель.
Аналогичным образом можно и уменьшить указатель.
Однако
при этом следует соблюдать
Кроме
того, оператор увеличения можно использовать
для переменной типа указатель, но не для
констант этого типа подобно тому, как
вы не можете применять оператор увеличения
для обычных констант. Для переменных
и констант типа указатель можно использовать
простое сложение:
Правильно
ptr1 ++ ; urn++ ;
х++ ; 3++;
ptr2 = ptr1 + 2; ptr2 = urn++;
ptr2 = urn + 1; x = у + 3++;
Перечисленные
выше операции открывают большие
возможности. Программисты на языке
Си создают массивы указателей, указатели
на функции, массивы указателей на указатели,
массивы указателей на функции и т. д. Мы
будем придерживаться основных применений,
которые уже упоминались. Первое из них
— передача информации в функцию и из
нее. Мы использовали указатели, когда
хотели, чтобы функция изменила переменные,
находящиеся в вызывающей программе. Второе
— использование указателей в функциях,
работающих с многомерными массивами.
Темпест Клауд, метеоролог, занимающаяся явлением перистости облаков, хочет проанализировать данные о ежемесячном количестве осадков на протяжении пяти лет. В самом начале она должна решить, как представлять данные. Можно использовать 60 переменных, по одной на каждый месяц. (Мы уже упоминали о таком подходе ранее, но в данном случае он также неудачен.) Лучше было бы взять массив, состоящий из 60 элементов, но это устроило бы нас только до тех пор, пока можно хранить раздельно данные за каждый год. Мы могли бы также использовать S массивов по 12 элементов каждый, но это очень примитивно и может создать действительно большие неудобства, если Темпест решит изучать данные о количестве осадков за 50 лет вместо пяти. Нужно придумать что-нибудь получше.
Хорошо
было бы использовать массив массивов.
Основной массив состоял бы тогда
из 5 элементов, каждый из которых в свою
очередь был бы массивом из 12 элементов.
Вот как это записывается:
static
float rain[5][12];
Можно также представить массив rain в виде двумерного массива состоящего из 5 строк и 12 столбцов.
При
изменении второго индекса на
единицу мы передвигаемся вдоль строки,
а при изменении первого индекса на единицу,
передвигаемся вертикально вдоль столбца.
В нашем примере второй индекс дает нам
месяцы, а первый — годы.
Используем
этот двумерный массив в метеорологической
программе. Цель нашей программы — найти
общее количество осадков для каждого
года, среднегодовое количество осадков
и среднее количество осадков за каждый
месяц. Для получения общего количества
осадков за год следует сложить все данные,
находящиеся в нужной строке. Чтобы найти
среднее количество осадков за данный
месяц, мы сначала складываем все данные
в указанном столбце. Двумерный массив
позволяет легко представить и выполнить
эти действия.
/* найти общее
количество осадков для
/* годовое, среднемесячное количество осадков, за несколько лет */
#define TWLV 12 /* число месяцев в году */
#define YRS 5 /* число лет */
main ()
{
static float rain [YRS][TWLV] = {
{10.2, 8.1, 6.8, 4.2, 2.1, 1.8, 0.2, 0.3, 1.1, 2.3, 6.1, 7.4},
{9.2, 9.8, 4.4, 3.3, 2.2, 0.8, 0.4, 0.0,0.6, 1.7, 4.3, 5.2},
{6.6, 5.5, 3.8, 2.8, 1.6, 0.2, 0.0, 0.0,0.0, 1.3, 2.6, 4.2},
{4.3, 4.3, 4.3, 3.0, 2.0, 1.0, 0.2, 0.2,0.4, 2.4, 3.5, 6.6},
{8.5, 8.2, 1.2, 1.6, 2.4, 0.0, 5.2, 0.9,0.3, 0.9, 1.4, 7.2}
};
/* инициализация данных по количеству осадков за 1970—1974 */
int year, month;
float subtot, total;
printf(" ГОД
КОЛИЧЕСТВО ОСАДКОВ (дюймы)\n\
for (year = 0, total = 0; year < YRS; year++ )
{
/* для каждого года, суммируем количество осадков
для каждого месяца */
for (month = 0, subtot = 0; month < TWLV; month++ )
subtot += rain [year] [month);
printf("%5d %15.1f\n", 1970 + year, subtot);
total + = subtot; /* общее для всех лет */
}
printf(" \n среднегодовое количество осадков составляет
%.1f дюймов.\n\n" , total/YRS );
printf(" Янв. Фев. Map. Апр.Май Июн. Июл. Авг.Сент.");
printf(" Окт. Нояб. Дек\n");
for (month = 0; month < TWLV; month++ )
{
/* для каждого месяца, суммируем количество осадков за все годы */
for (year = 0, subtot = 0; year < YRS; year++ )
subtot += rain [year] [month];
printf(" %4.1f", subtot/YRS);
}
printf(" \n");
}
ГОД КОЛИЧЕСТВО ОСАДКОВ (дюймы)
1970 50.6
1971 41.9
1972 28.6
1973 32.3
1974 37.8
Среднегодовое
количество осадков составляет 38.2 дюйма.
ЕЖЕМЕСЯЧНОЕ КОЛИЧЕСТВО:
Янв. Фев. Мар. Апр. Май Июн. Июл. Авг. Сен. Окт. Нояб. Дек.
7.8
7.2 4.1 3.0 2.1 0.8 1.2 0.3 0.5 1.7 3.6 6.1
В этой программе следует отметить два основных момента: инициализацию и вычисления. Инициализация сложнее, поэтому мы сначала рассмотрим вычисления.
Чтобы найти общее количество осадков за год, мы не изменяем year, а заставляем переменную month пройти все свои значения. Так выполняется внутренний цикл for, находящийся в первой части программы. Затем мы повторяем процесс для следующего значения year. Это внешний цикл первой части программы. Структура вложенного цикла, подобная описанной, подходит для работы с двумерным массивом. Один цикл управляет одним индексом, а второй цикл — другим.
Вторая
часть программы имеет такую
же структуру, но теперь мы изменяем year
во внутреннем цикле, a month во внешнем. Помните,
что при однократном
Для инициализации массива мы взяли пять заключенных в скобки последовательностей чисел, а все эти данные еще раз заключили в скобки. Данные, находящиеся в первых внутренних скобках, присваиваются первой строке массива, данные во второй внутренней последовательности — второй строке и т. д. Правила, которые мы обсуждали раньше, о несоответствии между размером массива и данных применяются здесь для каждой строки. Если первая последовательность в скобках включает десять чисел, то только первым десяти элементам первой строки будут присвоены значения. Последние два элемента в этой строке будут, как обычно, инициализированы нулем по умолчанию. Если чисел больше, чем нужно, то это считается ошибкой; перехода к следующей строке не произойдет.
Мы
могли бы опустить все внутренние
скобки и оставить только две самые внешние.
До тех пор пока мы будем давать правильное
количество входных данных, результат
будет тем же самым. Однако, если данных
меньше, чем нужно, массив заполняется
последовательно (не обращается внимание
на разделение по строкам), пока не кончатся
все данные. Затем оставшимся элементам
будут присвоены нулевые значения. Смотри
рисунок.
Все,
что мы сказали о двумерных
массивах, можно распространить и
на трехмерные массивы и т. д. Трехмерный
массив описывается следующим образом:
int
solido[10][20][30];
Вы
можете представить его в виде
десяти двумерных массивов (каждый
20x30), поставленных друг на друга, или
в виде массива из массивов. То есть
это массив из 10 элементов, и каждый
его элемент также является массивом.
Все эти массивы в свою очередь имеют по
20 элементов, каждый из которых состоит
из 30 элементов. Преимущество этого второго
подхода состоит в том, что Можно довольно
просто перейти к массивам большей размерности,
если окажется, что вы не можете представить
наглядно четырехмерный объект! Мы же
останемся верны двум измерениям.
Как
создать указатели для
Предположим,
что у нас есть описания
int zippo[4][2]; /* массив типа int из 4 строк и 2 столбцов */
int
*pri; /* указатель на целый тип */
Тогда
на что
pri
= zippo;
указывает?
На первый столбец первой строки:
zippo
== &zippo[0][0]
А
на что указывает pri + 1? На zippo[0][1], т. е.
на 1-ю строку 2-го столбца? Или на zippo [1][0],
элемент, находящийся во второй строке
первого столбца? Чтобы ответить на поставленный
вопрос, нужно знать, как располагается
в памяти двумерный массив. Он размещается,
подобно одномерным массивам, занимая
последовательные ячейки памяти. Порядок
элементов определяется тем, что самый
правый индекс массива изменяется первым,
т. е. элементы массива располагаются следующим
образом:
zippo[0][0]
zippo[0][1] zippo[1][0] zippo[1][1] zippo[2][0]