Задати матрицю в сі. Заповнення матриці символами - C (СІ). Визначення автоматичних багатовимірних масивів

анотація: Наводяться правильні і неправильні способи реалізації матриць і багатовимірних масивів на мові Сі. Робота з матрицями ілюструється на прикладі приведення матриці до ступінчастого вигляду методом Гаусса. Розглядаються методи роботи з файлами, які використовують функції введення-виведення зі стандартної бібліотеки ANSI. Наводяться способи роботи з символами і текстовими рядками за допомогою функцій стандартної бібліотеки. Матеріал ілюструється прикладами, що включають програму "wc" підрахунку символів, слів і рядків у файлі і програму "Нотатки", яка дозволяє знаходити телефон людини по його імені, а також зберігати і модифікувати вміст книжки.

Подання матриць і багатовимірних масивів

Спеціального типу даних матриця або багатовимірний масив в Сі немає, проте, можна використовувати масив елементів типу масив. Наприклад, змінна a представляє матрицю розміру 3x3 з речовими елементами:

Елементи матриці розташовуються в пам'яті послідовно по рядках: спочатку йдуть елементи рядка з індексом 0, потім рядки з індексом 1, в кінці рядка з індексом 2 (в програмуванні відлік індексів завжди починається з нуля, а не з одиниці!). При цьому вираз

де i - ціла змінна, представляє собою покажчик на початковий елемент i -го рядка і має тип double *.

Для звернення до елемента матриці треба записати його індекси в квадратних дужках, наприклад, вираз

є елементом матриці a в рядку з індексом i і стовпці з індексом j. Елемент матриці можна використовувати в будь-якому вираженні як звичайну змінну (наприклад, можна читати його значення або привласнювати нове).

Така реалізація матриці зручна і максимально ефективна з точки зору часу доступу до елементам. У неї тільки один істотний недолік: так можна реалізувати тільки матрицю, розмір якої відомий заздалегідь. Мова Сі не дозволяє описувати масиви змінного розміру, розмір масиву повинен бути відомий до початку роботи програми ще на стадії компіляції.

Нехай потрібна матриця, розмір якої визначається під час роботи програми. Тоді простір під неї треба захоплювати в динамічної пам'яті за допомогою функції malloc мови Сі або оператора new мови C ++. При цьому в динамічній пам'яті захоплюється лінійний масив і повертається покажчик на нього. Розглянемо матеріальну матрицю розміром m рядків на n стовпців. Захоплення пам'яті виконується за допомогою функції malloc мови Сі

double * a; . . . a \u003d (double *) malloc (m * n * sizeof (double));

або за допомогою оператора new мови C ++:

double * a; int m, n; . . . a \u003d new double;

При цьому вважається, що елементи матриці будуть розташовуватися в масиві таким чином: спочатку йдуть елементи рядка з індексом 0, потім елементи рядка з індексом 1 і т.д., останніми йдуть елементи рядка з індексом m - 1. Кожен рядок складається з n елементів, отже, індекс елемента рядка i та стовпця j в лінійному масиві дорівнює

(Дійсно, оскільки індекси починаються з нуля, то i дорівнює кількості рядків, які потрібно пропустити, i * n - сумарна кількість елементів в пропускаються рядках; число j одно зміщення всередині останнього рядка). Таким чином, елементу матриці в рядку i і стовпці j відповідає вираз

Цей спосіб представлення матриці зручний і ефективний. Його основна перевага полягає в тому, що елементи матриці зберігаються в безперервному відрізку пам'яті. По-перше, це дозволяє оптимізуючий компілятор перетворювати текст програми, домагаючись максимального швидкодії; по-друге, при виконанні програми максимально використовується механізм кеш-пам'яті, що зводить до мінімуму звернення до пам'яті і значно прискорює роботу програми.

У деяких книгах по Сі рекомендується реалізовувати матрицю як масив покажчиків на її рядки, при цьому пам'ять під кожен рядок захоплюється окремо в динамічної пам'яті:

double ** a; // Адреса масиву покажчиків int m, n; // Розміри матриці: m рядків, n стовпців int i; . . . // Захоплюється пам'ять під масив покажчиків a \u003d (double **) malloc (m * sizeof (double *)); for (i \u003d 0; i< m; ++i) { // Захватывается память под строку с индексом i a[i] = (double *) malloc(n * sizeof(double)); }

Після цього до елементу a ij можна звертатися за допомогою виразу

Незважаючи на всю складність цього рішення, ніякого виграшу немає, навпаки, програма програє в швидкості! Причина полягає в тому, що матриця не зберігається в безперервному ділянці пам'яті, це заважає як оптимізації програми, так і ефективному використанню кеш-пам'яті. Так що краще не застосовувати такий метод подання матриці.

Багатовимірні масиви реалізуються аналогічно матрицями. Наприклад, речовий тривимірний масив розміру 4 x 4 x 2 описується як

звернення до його елементу з індексами x, y, z здійснюється за допомогою виразу

Багатовимірні масиви змінного розміру з числом індексів великим двох зустрічаються в програмах досить рідко, але ніяких проблем з їх реалізацією немає: вони реалізуються аналогічно матрицями. Наприклад, нехай треба реалізувати тривимірний матеріальний масив розміру m x n x k. Захоплюється лінійний масив дійсних чисел розміром m * n * k:

double * a; . . . a \u003d (double *) malloc (m * n * k * sizeof (double));

Доступ до елементу з індексами x, y, z здійснюється за допомогою виразу

a [(x * n + y) * k + z]

Приклад: приведення матриці до ступінчастого вигляду методом Гаусса

Як приклад роботи з матрицями розглянемо алгоритм Гаусса приведення матриці до ступінчастого вигляду. Метод Гаусса - один з основних результатів лінійної алгебри та аналітичної геометрії, до нього зводяться безліч інших теорем і методів лінійної алгебри (теорія і обчислення визначників, рішення систем лінійних рівнянь, обчислення рангу матриці і оберненої матриці, теорія базисів скінченновимірних векторних просторів і т.д .).

Нагадаємо, що матриця A з елементами a ij називається ступінчастою, якщо вона володіє наступними двома властивостями:

  1. якщо в матриці є нульова рядок, то всі рядки нижче неї також нульові;
  2. нехай a ij нерівний 0 - перший ненульовий елемент в рядку з індексом i, тобто елементи a il \u003d 0 при l< j . Тогда все элементы в j -м столбце ниже элемента a ij равны нулю, и все элементы левее и ниже a ij также равны нулю: a kl = 0 при k > i і l \u003d< j .

Ступінчаста матриця виглядає приблизно так:

тут темними квадратиками відзначені перші ненульові елементи рядків матриці. Білим кольором зображуються нульові елементи, сірим кольором - довільні елементи.

Алгоритм Гауса використовує елементарні перетворення матриці двох типів.

  • Перетворення першого роду: Два рядки матриці міняються місцями, і при цьому знаки всіх елементів однієї з рядків змінюються на протилежні.
  • Перетворення другого роду: До одного рядка матриці додається інший рядок, помножена на довільне число.

Елементарні перетворення зберігають визначник і ранг матриці, а також безліч рішень лінійної системи. Алгоритм Гауса призводить довільну матрицю елементарними перетвореннями до ступінчастого вигляду. Для ступінчастою квадратної матриці визначник дорівнює добутку діагональних елементів, а ранг - числу ненульових рядків (рангом за визначенням називається розмірність лінійної оболонки рядків матриці).

Метод Гаусса в математичному варіанті полягає в наступному:

  1. шукаємо спочатку ненульовий елемент в першому стовпці. Якщо всі елементи першого стовпчика нульові, то переходимо на дві колонки, і так далі. Якщо знайшли ненульовий елемент в k -му рядку, то за допомогою елементарного перетворення першого роду міняємо місцями першу і k -у рядки, домагаючись того, щоб перший елемент першого рядка був відмінний від нуля;
  2. використовуючи елементарні перетворення другого роду, Обнуляємо всі елементи першого стовпчика, починаючи з другого елементу. Для цього від рядка з номером k віднімаємо перший рядок, помножену на коефіцієнт a k1 / a 11.
  3. переходимо на дві колонки (або j -му, якщо всі елементи першого стовпчика були нульовими), і в подальшому розглядаємо тільки частина матриці, починаючи з другого рядка і нижче. Знову повторюємо пункти 1) і 2) до тих пір, поки не приведемо матрицю до ступінчастого вигляду.

Програмістський варіант методу Гаусса має три відмінності від математичного:

r \u003d -a kj / a ij. a k \u003d a k + r * a i

Така схема працює нормально тільки тоді, коли коефіцієнт r по абсолютній величині не перевищує одиниці. В іншому випадку, помилки округлення множаться на великий коефіцієнт і, таким чином, експоненціально зростають. Математики називають це явище нестійкістю обчислювальної схеми. Якщо обчислювальна схема нестійка, то отримані з її допомогою результати не мають ніякого відношення до вихідної задачі. У нашому випадку схема стійка, коли коефіцієнт r \u003d -a kj / a ij не перевищує по модулю одиниці. Для цього має виконуватися нерівність

| A ij | \u003e \u003d | A kj | при k\u003e i

Звідси випливає, що при пошуку дозволяє елемента в j -му стовпці необхідно знайти не перший-ліпший ненульовий елемент, а максимальний по абсолютній величині. Якщо він по модулю не перевищує, то вважаємо, що всі елементи стовпця нульові; інакше міняємо місцями рядки, ставлячи його на вершину стовпчика, і потім Обнуляємо стовпець елементарними перетвореннями другого роду.

Нижче дан повний текст програми на Сі, що приводить речову матрицю до ступінчастого вигляду. Функція, що реалізує метод Гаусса, одночасно підраховує і ранг матриці. Програма вводить розміри матриці і її елементи з клавіатури і викликає функцію приведення до ступінчастого вигляду. Потім програма друкує ступінчастий вигляд матриці і її ранг. У разі квадратної матриці також обчислюється і друкується визначник матриці, Що дорівнює добутку діагональних елементів ступінчастою матриці.

При реалізації методу Гаусса використовується схема побудови циклу з допомогою інваріанта, див. 1.5.2. У циклі змінюються дві змінні - індекс рядка i, 0 \u003d< i < m - 1 , и индекс столбца j , 0 =< j < n - 1 . Инвариантом цикла является утверждение о том, что часть матрицы (математики говорят мінор) В шпальтах 0,1, ... j - 1 приведена до ступінчастому увазі і що перший ненульовий елемент в рядку i - 1 стоїть в стовпці з індексом меншим j. У тілі циклу розглядається тільки мінор матриці в рядках i, ..., m - 1 і шпальтах j, ..., n - 1. Спочатку шукається максимальний по модулю елемент в j -му стовпці. Якщо він за абсолютною величиною не перевищує то j збільшується на одиницю (вважається, що стовпець нульової). Інакше перестановкою рядків дозволяє елемент ставиться на вершину j-го стовпця мінору, і потім стовпець обнуляється елементарними перетвореннями другого роду. Після цього обидва індекси i і j збільшуються на одиницю. Алгоритм завершується, коли або i \u003d m, або j \u003d n. Після закінчення алгоритму значення змінної i дорівнює числу ненульових рядків ступінчастою матриці, тобто рангу вихідної матриці.

Для обчислення абсолютної величини дійсного числа x типу double ми користуємося стандартною математичної функцією fabs (x), Описаної в стандартному заголовному файлі "math .h.

#include // Описи функцій вводу-виводу #include // Описи математичних функцій #include // Описи функцій malloc і free // Прототип функції приведення матриці // до ступінчастого вигляду. // Функція повертає ранг матриці int gaussMethod (int m, // Число рядків матриці int n, // Число стовпців матриці double * a, // Адреса масиву елементів матриці double eps // Точність обчислень); int main () (int m, n, i, j, rank; double * a; double eps, det; printf ( "Введіть розміри матриці m, n:"); scanf ( "% d% d", & m, & n ); // Захоплюємо пам'ять під елементи матриці a \u003d (double *) malloc (m * n * sizeof (double)); printf ( "Введіть елементи матриці: \\ n"); for (i \u003d 0; i< m; ++i) { for (j = 0; j < n; ++j) { // Вводим элемент с индексами i, j scanf("%lf", &(a)); } } printf("Введите точность вычислений eps: "); scanf("%lf", &eps); // Вызываем метод Гаусса rank = gaussMethod(m, n, a, eps); // Печатаем ступенчатую матрицу printf("Ступенчатый вид матрицы:\n"); for (i = 0; i < m; ++i) { // Печатаем i-ю строку матрицы for (j = 0; j < n; ++j) { printf(// Формат %10.3lf означает 10 "%10.3lf ", // позиций на печать числа, a // 3 знака после точки); } printf("\n"); // Перевести строку } // Печатаем ранг матрицы printf("Ранг матрицы = %d\n", rank); if (m == n) { // Для квадратной матрицы вычисляем и печатаем // ее определитель det = 1.0; for (i = 0; i < m; ++i) { det *= a; } printf("Определитель матрицы = %.3lf\n", det); } free(a); // Освобождаем память return 0; // Успешное завершение программы } // Приведение вещественной матрицы // к ступенчатому виду методом Гаусса с выбором // максимального разрешающего элемента в столбце. // Функция возвращает ранг матрицы int gaussMethod(int m, // Число строк матрицы int n, // Число столбцов матрицы double *a, // Адрес массива элементов матрицы double eps // Точность вычислений) { int i, j, k, l; double r; i = 0; j = 0; while (i < m && j < n) { // Инвариант: минор матрицы в столбцах 0..j-1 // уже приведен к ступенчатому виду, и строка // с индексом i-1 содержит ненулевой эл-т // в столбце с номером, меньшим чем j // Ищем максимальный элемент в j-м столбце, // начиная с i-й строки r = 0.0; for (k = i; k < m; ++k) { if (fabs(a) > r) (l \u003d k; // Запам'ятаємо номер рядка r \u003d fabs (a); // і макс. ел-т)) if (r<= eps) { // Все элементы j-го столбца по абсолютной // величине не превосходят eps. // Обнулим столбец, начиная с i-й строки for (k = i; k < m; ++k) { a = 0.0; } ++j; // Увеличим индекс столбца continue; // Переходим к следующей итерации } if (l != i) { // Меняем местами i-ю и l-ю строки for (k = j; k < n; ++k) { r = a; a = a; a = (-r); // Меняем знак строки } } // Утверждение: fabs(a) > eps // Обнуляємо j-й стовпець, починаючи з рядка i + 1, // застосовуючи елем. перетворення другого роду for (k \u003d i + 1; k< m; ++k) { r = (-a / a); // К k-й строке прибавляем i-ю, умноженную на r a = 0.0; for (l = j+1; l < n; ++l) { a += r * a; } } ++i; ++j; // Переходим к следующему минору } return i; // Возвращаем число ненулевых строк }

Оголошення двовимірного масиву в СІ має наступний синтаксис:
тип ім'я [розмір №1] [розмір №2];
Розміри двовимірного масиву в СІ вказуються в окремих парних квадратних дужках після імені і можуть бути будь-якими позитивними цілочисельними значеннями. На практиці прийнято значення першої розмірності називати рядками, а другий - стовпцями. Як і в разі одновимірного масиву, в стандарті С89 регламентується, що розміри двовимірного масиву повинні бути цілочисельними константами.
Стандарт С99 допускає оголошення динамічних двовимірних масивів шляхом використання виразів при вказівці розмірів матриці, якщо в цей вислів входять значення певних раніше змінних (вираз має мати позитивний цілочисельний результат). наприклад:
int n, m;
printf ( "Введіть розміри матриці:");
scanf ( "% d% d", & ampn, & ampm);
double a [n] [m];
при оголошенні двовимірного масиву в СІ допускається проводити ініціалізацію значень елементів матриці:
тип ім'я [розмір №1] [розмір №2] \u003d (
(Значення № 11, ... значення № 1N),
   ...
(Значення № M1, ... значення № MN)
  };
Приклади оголошень з ініціалізацією:
int a \u003d (// Оголошено двовимірний масив
  {1,2,3,4}, // 1 2 3 4
  {5,6}}; // 5 6 0 0

Double b \u003d (// Оголошено двовимірний масив
   {1.0, 2.0, 3.0, 4.0, 5.0}, // 1 2 3 4 5
   {6.0, 7.0} // 6 7 0 0 0
  }; // 0 0 0 0 0

Пропускати значення ініціалізації рядків не можна. Наприклад, наступний фрагмент коду програми неправильний:
int a \u003d ((1,2,3,4,5), (6,7,8,9,0)); Допускається не вказувати кількість рядків в двовимірному масиві (вказуються порожні квадратні дужки). У такому випадку розмір масиву буде визначено за кількістю ініціюючих значень рядків. Кількість стовпців матриці завжди необхідно вказувати. наприклад:
double b \u003d ((1,2,3,4), (5,6,7,8)); Оголошення константних матриць (значення їх елементів змінити не можна) починається з ключового слова const, за яким слідує оголошення матриці з ініціалізацією. приклад:
const int matrix \u003d (
   {1,2,3,4,5},
   {6,7,8,9}
  };
Звернення до елементу матриці здійснюється шляхом зазначення імені матриці, а після імені в окремих парних квадратних дужках індекси елемента (рядок і стовпець):
ім'я [рядок] [стовпець] Індексація в мові СІ починається з нуля, тому для матриці розміром, наприклад, п'ять рядків і десять стовпців правильними будуть індекси рядків від нуля до чотирьох, а стовпців - від нуля до дев'яти включно.
Кожен окремий елемент матриці може розглядатися як проста змінна і, відповідно, виступати в виразах як RValue або LValue значень.
Введення і виведення матриць в мові СІ здійснюється поелементно. Так як матриця має подвійну розмірність, то введення і виведення здійснюється у вкладених циклах. наприклад:
double a;
for (int i \u003d 0; ii ++)
for (int j \u003d 0; jj ++)
scanf ( "% lf", & ampa [i] [j]);
  ...
for (int i \u003d 0; ii ++) (
for (int j \u003d 0; jj ++)
printf ( "% 8.2lf \\ t", a [i] [j]);
printf ( "\\ n");
  }
Присвоєння матриці матриці також здійснюється поелементно. Наприклад, необхідно присвоїти целочисленную матрицю x целочисленной матриці y. Фрагмент програми:
int x, y;
  ...
for (int i \u003d 0; ii ++)
for (int j \u003d 0; jj ++)
y [i] [j] \u003d x [i] [j];
  ...
У мові СІ допускається створення масивів розмірністю три і більше (тобто тривимірних, чотиривимірних і т.д.). Наприклад, оголошення тривимірного целочисленного масиву з ініціалізацією матиме вигляд:
int a \u003d (// це тривимірний масив
   {{1,2},{3,4}},
   {{5,6},{7,8}}
  };
Введення, висновок та інша обробка такого масиву здійснюється аналогічно обробці двовимірного масиву, тільки вже в трьох вкладених

При вирішенні завдань з великою кількістю даних однакового типу використання змінних з різними іменами, які не впорядкованих за адресами пам'яті, ускладнює програмування. У подібних випадках в мові Сі використовують об'єкти, звані масивами.

- це безперервний ділянку пам'яті, що містить послідовність об'єктів однакового типу, що позначається одним ім'ям.

Масив характеризується наступними основними поняттями:

Елемент масиву (значення елемента масиву) - значення, яке зберігається в певній комірці пам'яті, розташованої в межах масиву, а також адреса цього осередку пам'яті.
Кожен елемент масиву характеризується трьома величинами:

  • адресою елемента - адресою початкової комірки пам'яті, в якій розташований цей елемент;
  • індексом елемента (порядковим номером елемента в масиві);
  • значенням елемента.

Адреса масиву - адреса початкового елемента масиву.

Ім'я масиву - ідентифікатор, який використовується для звернення до елементів масиву.

Розмір масиву - кількість елементів масиву

Розмір елемента - кількість байт, займаних одним елементом масиву.

Графічно розташування масиву в пам'яті комп'ютера можна представити у вигляді безперервної стрічки адрес.

Представлений на малюнку масив містить q елементів з індексами від 0 до q-1. Кожен елемент займає в пам'яті комп'ютера k байт, причому розташування елементів в пам'яті послідовне.

Адреси i -го елемента масиву має значення

Адреса масиву представляє собою адресу початкового (нульового) елемента масиву. Для звернення до елементів масиву використовується порядковий номер (індекс) елемента, початкове значення якого дорівнює 0. Так, якщо масив містить q елементів, то індекси елементів масиву змінюються в межах від 0 до q-1.

Довжина масиву - кількість байт, що відводиться в пам'яті для зберігання всіх елементів масиву.

ДлінаМассіва \u003d РазмерЕлемента * КолічествоЕлементов

Для визначення розміру елемента масиву може використовуватися функція

int sizeof (тип);

наприклад,

sizeof (char) \u003d 1;
sizeof (int) \u003d 4;
sizeof (float) \u003d 4;
sizeof (double) \u003d 8;

Оголошення і ініціалізація масивів

Для оголошення масиву в мові Сі використовується наступний синтаксис:

тип ім'я [розмірність] \u003d (ініціалізація);

Ініціалізація являє собою набір початкових значень елементів масиву, зазначених в фігурних дужках, і розділених комами.

int a \u003d (0, 1, 2, 3, 4, 5, 6, 7, 8, 9); // масив a з 10 цілих чисел

Якщо кількість ініціюючих значень, зазначених в фігурних дужках, менше, ніж кількість елементів масиву, вказане в квадратних дужках, то все решта елементи в масиві (для яких не вистачило ініціюючих значень) будуть дорівнювати нулю. Це властивість зручно використовувати для завдання нульових значень всіх елементів масиву.

int b \u003d (0); // масив b з 10 елементів, ініціалізованих 0


Якщо масив ініціалізованим першим при оголошенні, то константні початкові значення його елементів вказуються через кому в фігурних дужках. У цьому випадку кількість елементів в квадратних дужках може бути опущено.

int a \u003d (1, 2, 3, 4, 5, 6, 7, 8, 9);

При зверненні до елементів масиву індекс необхідного елемента вказується в квадратних дужках.

Приклад на Сі

1
2
3
4
5
6
7
8

#include
int main ()
{
int a \u003d (5, 4, 3, 2, 1); // масив a містить 5 елементів
printf ( "% d% d% d% d% d \\ n", a, a, a, a, a);
getchar ();
return 0;
}

Результат виконання програми:

Однак часто потрібно задавати значення елементів масиву в процесі виконання програми. При цьому використовується оголошення масиву без ініціалізації. В такому випадку зазначення кількості елементів в квадратних дужках обов'язково.

int a;

Для завдання початкових значень елементів масиву дуже часто використовується параметричний цикл:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18


#include
int main ()
{
int a;
int i;
// Введення елементів масиву
for (i \u003d 0; i<5; i++)
{
printf ( "a [% d] \u003d", i);
scanf ( "% d", & a [i]);
}
// Висновок елементів масиву
for (i \u003d 0; i<5; i++)
printf ( "% d", a [i]); // пропуск в форматі друку обов'язковий
getchar (); getchar ();
return 0;
}

Результат виконання програми

багатовимірні масиви

У мові Сі можуть бути також оголошені багатовимірні масиви. Відмінність багатовимірного масиву від одновимірного полягає в тому, що в одновимірному масиві положення елемента визначається одним індексом, а в багатовимірному - декількома. Прикладом багатовимірного масиву є матриця.

Загальна форма оголошення багатовимірного масиву

тип ім'я [размерность1] [размерность2] ... [размерностьm];

Елементи багатовимірного масиву розташовуються в послідовних комірках оперативної пам'яті по зростанню адрес. У пам'яті комп'ютера елементи багатовимірного масиву розташовуються поспіль, наприклад масив, який має 2 рядки і 3 стовпці,

int a;


буде розташований в пам'яті в такий спосіб

Загальна кількість елементів в наведеному двовимірному масиві визначиться як

КолічествоСтрок * КолічествоСтолбцов \u003d 2 * 3 \u003d 6.

Кількість байт пам'яті, необхідних для розміщення масиву, визначиться як

КолічествоЕлементов * РазмерЕлемента \u003d 6 * 4 \u003d 24 байта.

Ініціалізація багатовимірних масивів

Значення елементів багатовимірного масиву, як і в одновимірному випадку, можуть бути задані константними значеннями при оголошенні, укладених у фігурні дужки (). Однак в цьому випадку зазначення кількості елементів в рядках і стовпцях повинно бути обов'язково вказано в квадратних дужках.

Приклад на Сі

1
2
3
4
5
6
7
8
9

#include
int main ()
{
int a \u003d (1, 2, 3, 4, 5, 6);
printf ( "% d% d% d \\ n", a, a, a);
getchar ();
return 0;
}



Однак частіше потрібно вводити значення елементів багатовимірного масиву в процесі виконання програми. З цією метою зручно використовувати вкладений параметричний цикл.

Приклад на Сі

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

#define _CRT_SECURE_NO_WARNINGS
#include
int main ()
{
int a; // масив з 2 рядків і 3 стовпців
int i, j;
// Введення елементів масиву
for (i \u003d 0; i<2; i++) // цикл по рядках
{
for (j \u003d 0; j<3; j++) // цикл по стовпцях
{
printf ( "a [% d] [% d] \u003d", i, j);
scanf ( "% d", & a [i] [j]);
}
}
// Висновок елементів масиву
for (i \u003d 0; i<2; i++) // цикл по рядках
{
for (j \u003d 0; j<3; j++) // цикл по стовпцях
{
printf ( "% d", a [i] [j]);
}
printf ( "\\ n"); // переклад на новий рядок
}
getchar (); getchar ();
return 0;
}



Передача масиву в функцію

Обробку масивів зручно організовувати за допомогою спеціальних функцій. Для обробки масиву в якості аргументів функції необхідно передати

  • адреса масиву,
  • розмір масиву.

Виняток становлять функції обробки рядків, в які досить передати тільки адреса.

При передачі змінні в якості аргументів функції дані передаються як копії. Це означає, що якщо всередині функції станеться зміна значення параметра, то це ніяк не вплине на його значення всередині викликає функції.

Якщо в функцію передається адреса змінної (або адреса масиву), то всі операції, що виконуються в функції з даними, що знаходяться в межах видимості зазначеної адреси, виробляються над оригіналом даних, тому вихідний масив (або значення змінної) може бути змінено спричиненої функцією.

Приклад на Сі Дан масив з 10 елементів. Поміняти місцями найбільший та початковий елементи масиву. Для операцій пошуку максимального елемента і обміну використовувати функцію.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

#define _CRT_SECURE_NO_WARNINGS
#include
// Функція обміну
void change (int * x, int n)
{
// x - покажчик на масив (адреса масиву)
// n - розмір масиву
int i;
int max, index;
max \u003d x;
index \u003d 0;
// Пошук максимального елемента
for (i \u003d 1; i {
if (x [i]\u003e max)
{
max \u003d x [i];
index \u003d i;
}
}
// Обмін
x \u003d x;
x \u003d max;
}
// Головна функція
int main ()
{
int a;
int i;
for (i \u003d 0; i<10; i++)
{
printf ( "a [% d] \u003d", i);
scanf ( "% d", & a [i]);
}
change (a, 10); // виклик функції обміну
// Висновок елементів масиву
for (i \u003d 0; i<10; i++)
printf ( "% d", a [i]);
getchar ();
getchar ();
return
p \u003d p * x [i];
}
return p;
}
// Головна функція
int main ()
{
int a; // оголошений масив a з 5 елементів
int i;
int pr;
// Введення елементів масиву
for (i \u003d 0; i<5; i++)
{
printf ( "a [% d] \u003d", i);
scanf ( "% d", & a [i]); // & a [i] - адреса i-го елемента масиву
}
pr \u003d func (a, 5); // обчислення добутку
printf ( "\\ n pr \u003d% d", pr); // вивід твори парних елементів
getchar (); getchar ();
return 0;
}



I. На кого розрахований модуль.

Даний модуль розрахований учня в старшій школі або студента. Повинні бути освоєні такі теми як робота з циклами, з покажчиками, необхідно знати, що таке одновимірний масив.

II. мотивація

Ми вже знаємо, для чого нам можуть знадобитися одномірні масиви. Наприклад, ми можемо зберегти в одновимірному масиві коефіцієнти квадратного рівняння і порахувати коріння, використовуючи дискриминант. А якщо нам потрібно порахувати багато таких рівнянь? Невже кожен раз перезаписувати нові коефіцієнти на місце старих? А якщо необхідно буде повернутися до вже порахували? Або нам потрібно вирішити систему з кількох рівнянь, тобто потрібно одночасно працювати з усіма? Якщо нам просто потрібно зберігати таблицю якихось значень, наприклад, вимірювань при досвіді, як нам це зробити, використовуючи отримані вже знання?

Для кожного з вищеназваних випадків можна просто оголосити кілька одновимірних масивів, але ось чи приємно буде працювати з такою кількістю? Адже кожен потрібно буде назвати і обробити окремо. Ситуація схожа на ту, коли ми тільки вводили поняття одновимірного масиву. Тут і приходять на допомогу масиви двовимірні.

III. Виклад матеріалу модуля

Звичайною виставою таких масивів є таблиці значень, що містять інформацію в рядках і шпальтах.Щоб визначити окремий табличний елемент, потрібно вказати два індекси: перший (за згодою) вказує номер рядка, а другий (знову ж за угодою) вказує номер стовпця.

Малюнок ілюструє двовимірний масив a. Масив містить три рядки і чотири стовпці, так що, ще кажуть, - це масив три на чотири. Взагалі, масиви з m рядками і nстовпцями називають масивами mна n.

Кожен елемент в масиві авизначається ім'ям елемента в формі a[ i ][ j ]; aце ім'я масиву, а iі j- індекси, які однозначно визначають кожен елемент в а. Зауважимо, що імена елементів першого рядка мають перший індекс 0 , Імена елементів в четвертому стовпці мають другий індекс 3 .

Типова помилка програмування

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

Багатовимірні масиви можуть отримувати початкові значення в своїх оголошеннях точно так же, як масиви з природним індексом. Наприклад, двовимірний масив bможна оголосити і дати йому початкові значення таким чином:

int b \u003d ((1,2), (3,4));

Значення групуються в рядки, укладені у фігурні дужки. Таким чином, елементи bі bотримують начальниезначенія 1 і 2, а елементи bі bотримують початкові значення 3 і 4. Якщо початкових значень в даній сходинці не вистачає для їх присвоєння всім елементам рядка, то що залишається елементам присвоюються нульові початкові значення. Таким чином, оголошення

int b \u003d ((1,), (3,4));

буде означати, що bотримує початкове значення 1, bотримує початкове значення 0.

Обсяг пам'яті в байтах, займаний двомірним масивом, обчислюється за такою формулою:

Кількість байтів \u003d размер_1-го_ізмеренія * размер_2-го_ізмеренія * sizeof (базовий тип)

Наприклад, двовимірний масив 4-байтових цілих чисел розмірністю 10 * 5 займає ділянку пам'яті об'ємом

тобто 200 байтів.

Передача масиву в функцію.

Розглянемо невелику програму виведення елементів двовимірного масиву:

#include

void printArray (int a)

for (int i \u003d 0; i<=1;i++)

for (int j \u003d 0; j<=2; j++)

printf ( "% i,", & a [i] [j]);

printf ( "\\ n");

int array \u003d ((1,2,3), (4,5,6));

printf ( "Values \u200b\u200bin array on rows:");

printgArray (array);

Values \u200b\u200bin array on rows:

Програма викликає функцію printArray для виведення елементів масиву. Зауважимо, що опис функції вказує параметр - масив як inta. Коли ми задаємо як аргумент функції одновимірний масив, дужки в списку параметрів функції порожні. Розмірність першого індексу багатовимірного масиву також не потрібно, але всі наступні розмірності індексів необхідні. Компілятор використовує розмірності цих індексів для визначення відповідних осередків пам'яті для доступу до елементів багатовимірних масивів. У пам'яті всі елементи масиву зберігаються послідовно, незалежно від кількості індексів (розмірності масиву). У двовимірному масиві перший рядок зберігається в пам'яті перед другим рядком.

Наявність размерностей індексів в оголошенні параметра дає можливість компілятору повідомити функції про те, як розташовані елементи в масиві. У двовимірному масиві кожен рядок є одновимірним масивом. Щоб визначити місце розташування елемента в деякій рядку, функція повинна точно знати, скільки елементів знаходиться в кожному рядку. Тоді функція може пропустити відповідну кількість елементів пам'яті при зверненні до масиву. Таким чином, при зверненні до aфункція знає, що для доступу до другої рядку (рядок 1) потрібно пропустити в пам'яті три елементи першого рядка, а потім звернутися до третього елементу цього рядка (елементу 2).

Багато типові операції з масивами використовують конструкцію for. Так, наступний цикл визначає суму всіх елементів масиву a:

for (row \u003d 0; row< 3; row++)

for (column \u003d 0; column< 3; column ++)

total + \u003d a;

Внутрішня структура forпідсумовує елементи одного рядка масиву. зовнішня структура for починає роботу з установки row (Тобто індексу рядка) в нуль, так що у внутрішній структурі forможуть бути підсумовані елементи другого рядка. Далі зовнішня структура forзбільшує rowдо значення 2, так що можуть бути підсумовані елементи третього рядка. Після завершення роботи вкладеної структури for друкується результат.

завдання

Учні одного з ВНЗ якось посперечалися, у кого найвищий середній бал за щойно здану сесію. Заодно захотіли з'ясувати, хто отримав найкращу та найгіршу оцінки. Для інтересу уявімо, що ми живемо в Америці, і оцінки виставляються за 100-бальною шкалою.

вказівка:

Програма повинна «знати» імена студентів, виводити їх на екран. Те ж саме з таблицею успішності. Мається на увазі, що номери стовпців еквівалентні номерам іспитів.

Роздуми про рішення задачі

Для початку давайте визначимося, що нам потрібно для вирішення цього завдання. Перше, на що ми звертаємо увагу - це те, що в умова явно сказано слово «таблиця». Значить, швидше за все, для роботи з нею ми будемо використовувати двовимірний масив. Кожен рядок в ньому - це успішність окремого студента. Як і сказано в умови, номер стовпця буде визначати номер складеного іспиту, номер рядка - студента. Саме з цим масивом ми і будемо працювати, обчислюючи середній бал, мінімум і максимум. Видно, що необхідно буде створити три функції для отримання потрібних нам даних і функцію виведення таблиці оцінок на екран. Все, що потрібно для написання цієї частини програми, ми вже знаємо.

Тепер розберемося з іменами. Непогано було б їх скомпонувати в таблицю, для того, щоб по номеру студента можна було визначити його ім'я і навпаки. Ми вже знаємо. Що рядок - це одновимірний масив символів. Значить, можна створити двовимірний масив, в якому номер рядка буде визначати номер студента, а номер стовпця - відповідну букву імені. Але тоді доведеться створити масив з найбільшою кількістю осередків по горизонталі з можливих. Тобто, якщо у нас є чотири студента:

То необхідно буде створити таблицю, подібну зображеною на малюнку:


Сірим кольором на малюнку показані порожні клітини нашого масиву. На ці клітини буде відведено місце в пам'яті, але от користуватися вони не будуть. Так навіщо вони нам потрібні? Чи немає способу уникнути такої ситуації? Виявляється, є. Про це йтиметься нижче, а потім ми повернемося до вирішення нашої задачі.