Массивы указателей.

Массивы могут содержать указатели. Типичным использованием такой структуры данных является формирование массива строк. Каждый элемент такого массива – строка, но в С строка является, по существу, указателем на ее первый символ. Таким образом, каждый элемент в массиве строк в действительности является указателем на первый символ строки. Рассмотрим объявление массива строк students, который будет полезен для решения нашей задачи.

char *students[4] = {“Ivanov”, “Velnikovskiy”, “Nilov”, “Petrov”};

Элемент объявления student[4] указывает массив из 4 элементов. элемент объявления char* указывает, что тип каждого элемента массива students – указатель на char . Четыре значения, размещаемые в массиве – это “Ivanov”, “Velnikovskiy”, “Nilov”, “Petrov”. Каждое из них хранится в памяти как строка, завершающаяся нулевым символом, которая на один символ длиннее, чем число символов текста, указанного в кавычках. Эти четыре строки имеют длину 6, 12, 5 и 6 символов соответственно. Хотя это выглядит так, словно сами строки помещены в массив students , на самом деле в массиве хранятся лишь указатели (см. рисунок ниже). Каждый из них указывает на первый символ соответствующей ему строки. Таким образом, хотя размер массива students фиксирован, он обеспечивает доступ к строкам символов любой длины. Эта гибкость – один из примеров мощных возможностей структурирования данных в С.

‘I’

‘v’

‘a’

‘n’

‘o’

‘v’

‘\0’

students [0]

‘V’

‘e’

‘l’

‘n’

‘i’

‘k’

‘o’

‘v’

‘s’

‘k’

‘i’

‘y’

‘\0’

students [1]

‘N’

‘i’

‘l’

‘o’

‘v’

‘\0’

students [2]

‘P’

‘e’

‘t’

‘r’

‘o’

‘v’

‘\0’

students [3]

Строки символов могли бы быть размещены в двумерном массиве, в котором каждая строка представляет собой одну фамилию, а каждый столбец представляет одну из букв фамилии. Такая структура должна иметь фиксированное количество столбцов на строку и это количество должно быть таким большим, как самая длинная строка. Поэтому затраты памяти на хранение большого количества строк, большинство из которых короче, чем самая длинная строка, будут значительными.

Решение задачи

Итак, наша программа должна выполнять несколько типовых операций над массивом studentGrades размером 4 на 5. Каждая строка представляет номер студента, (соответствие этого номера имени будет храниться в массиве students )? а каждый столбец представляет оценку на каждом из четырех экзаменов. манипуляции с массивом выполняются при помощи четырех функций . Функция minimum определяет наихудшую оценку всех студентов в течение семестра. Функция maximum определяет наилучшую оценку всех студентов в течение семестра.. Функция average определяет среднесеместровую оценку отдельного студента. Функция printArray выводит двумерный массив в табличном виде.

Каждая из функций minimum, maximum и printArray получает три аргумента – массив studentGrades (называемый grades в каждой функции), количество студентов (число строк массива) и количество экзаменов (количество столбцов массива). Каждая функция циклически обрабатывает массив grades, используя вложенные структуры for. Приведем пример вложенной структуры for из описания функции minimum:

for (int i = 0; i<pupils; i++)

for (int j = 0; j< tests; j++)

if (grades[i][j]< lowGrade)

lowGrade = grades[i][j];

Теперь сама программа:

// Пример использования двумерного массива

#include <stdio.h>

const int studentsNumber = 3; //количество студентов

const int exams = 4; // количество экзаменов

char *students[4] = {“Ivanov”, “Velnikovskiy”, “Nilov”, “Petrov”};

int minimum(int [] [exams], int, int);

int maximum (int [] [exams], int, int);

float average (int [], int, int);

void printArray (int [] [exams], int, int);

main()

{

int studentGrades [studentsNumber][exams] = {{77,68, 86, 73, 70},{96,87,89,78,65},{70,90,86,81,75},{75,67,96,86,82}};

printf("\nMassiv:");

printArray(studentGrades, sudentsNumber, exams);

printf ("\n The baddest mark: %i", minimum(studentsGrades, studentsNumber, exams));

printf ("\n The goodest mark: %i", maximum(studentsGrades, studentsNumber, exams));

for (int person = 0; person < studentsNumber; person++)

printf("\n The middle mark of %s is %f ",average(studentGrades[person], exams));

return 0;

}

// Поиск минимальной оценки

int minimum(int grades[][exams], int pupilus, int tests)

{

int lowGrade = 100;

for (int i = 0; i<pupils; i++)

for (int j = 0; j< tests; j++)

if (grades[i][j]< lowGrade)

lowGrade = grades[i][j];

return lowGrade;

}

// Поиск максимальной оценки

int maximum(int grades[][exams], int pupilus, int tests)

{

int highGrade = 0;

for (int i = 0; i<pupils; i++)

for (int j = 0; j< tests; j++)

if (grades[i][j]< lowGrade)

highGrade = grades[i][j]

returm highGrade;

}

// определение средней оценки для каждого студента

float average(int setofGrades[], int tests)

{

int total = 0;

for (int i = 0; i< tests; i++)

total += setOfGrades[i];

return (float) total/ tests;

}

// Печать массива

void printArray(int grades [] [exams], int pupils. int tests)

{

for (int i = 0; i< pupils; i++)

{

printf("\n Marks of student %s", students[i]);

for (int j = 0; j< tests; j++)

printf("%i ", grades[i][j]);

}

}

Внешняя структура for начинается с установки i, (т.е. индекса строки) равным нулю, чтобы элементы первой строки можно было сравнивать с переменной lowGrade в теле внутренней структуры for. Внутренняя структура for циклически обрабатывает пять оценок каждой строки и сравнивает каждую оценку с lowGrade. Если оценка меньше, чем lowGrade, lowGrade устанавливается равной этой оценке. Затем внешняя структура for увеличивает индекс строки до значения 1. Элементы второй строки сравниваются с переменной lowGrade. Затем внешняя структура for увеличивает индекс строки до значения 2, и так далее. Когда выполнение вложенной структуры заканчивается, lowGrade содержит минимальную оценку в двумерном массиве. Функция максимум работает аналогично.

Функция average принимает два аргумента – одномерный массив результатов экзаменов для одного студента и количество результатов экзаменов в массиве. При вызове average первый аргумент – это studentGrades[studentNumber], который указывает, что в average передается отдельная строка двумерного массива studentGrades. Например, аргумент studentGrades[1] представляет собой четыре значения (одномерный массив оценок), хранимый во второй строке двумерного массива studentGrades. Двумерный массив можно рассматривать как массив с элементами, представляющими собой одномерные массивы. Функция average подсчитывает сумму элементов массива, делит ее на количество результатов экзаменов и возвращает результат в форме с плавающей запятой.

Типичные ошибки программирования

  1. Забывают присвоить начальные значения элементам массива, требующим такого присваивания.

  2. Задание в списке начальных значений большего числа значений, чем имеется элементов в массиве, является синтаксической ошибкой.

  3. Ссылка на элемент. находящийся вне границ массива.

  4. Неправильная ссылка на элемент двумерного массива a[x] [y] как a[x,y]. На самом деле, a[x,y] воспринимается как a[y], потому что С оценивает выражение (содержащее операцию посдледования - запятую) x, y просто как y (последнее из разделенных запятыми выражений).

Контрольные вопросы

  1. Что такое двумерный массив?

  2. В чем принципиальное отличие двумерного массива от массива указателей?

  3. Укажите, верны ли следующие утверждения:

  • Массив может хранить много различных типов данных

  • Индексы массива обычно имеют тип float.

  • Если количество начальных значений вы списке инициализации меньше, чем количество элементов массива, оставшиеся элементы автоматически получают в качестве начальных значений последние значения из списка инициализации

  • Если список инициализации содержит начальных значений больше, чем элементов массива, то это – ошибка.

  • Отдельный элемент массива, который передается функции и модифицируется в этой функции, будет содержать модифицированное значение после завершения выполнения модифицируемой функции.