Advertisement
kiraventom

Untitled

Apr 29th, 2023 (edited)
628
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 14.69 KB | None | 0 0
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. int main() {
  5.     int line, column;
  6.  
  7.     printf("Enter matrix size: ");
  8.     int n;
  9.     scanf("%d", &n);
  10.  
  11.     // Для хранения массива целых чисел в языке Си используется тип int*.
  12.     // int* означает "указатель на целое число".
  13.     // Указатель -- это тоже целое число, которое буквально означает адрес в памяти компьютера.
  14.     // Т.е., если мы посмотрим реальное значение переменной типа int*, там будет, например, 46485.
  15.     // Это означает "46485-ая ячейка памяти компьютера".
  16.     // Массивы хранятся последовательно, т.е., элементы лежат в памяти друг за другом.
  17.     // Если наш массив состоит из трех элементов, то первый элемент у нас лежит в 46485-ой ячейке памяти.
  18.     // Второй будет лежать в 46486-ой, третий -- в 46487-ой.
  19.     // Значит, для доступа ко всему массиву нам достаточно иметь адрес памяти, в котором лежит первый элемент
  20.     // (т.е., указатель на первый элемент), и дальше просто прибавлять к этому адресу 1, 2 и так далее.
  21.     // Именно поэтому массивы и хранятся в переменной типа int* -- там лежит просто указатель на первый элемент,
  22.     // и оперировать остальными элементами мы можем, просто прибавляя 1, 2, 3 к этому значению.
  23.     // Например, создадим массив целых чисел на 5 элементов.
  24.     // int* array = (int*)malloc(5 * sizeof(int));
  25.     // Функция malloc принимает на вход количество байтов, которые надо выделить и возвращает адрес
  26.     // первого байта выделенной памяти.
  27.     // Функция sizeof принимает на вход тип и возвращает количество байтов, которое он занимает.
  28.     // Следовательно, для того, чтобы выделить достаточно памяти для 5 целых чисел, нам надо
  29.     // узнать, сколько байтов займет одно целое число -- для этого мы вызываем sizeof(int).
  30.     // После этого мы берем это количество байтов столько раз, сколько нам нужно элементов: 5 * sizeof(int),
  31.     // и передаем функции malloc. Она выделяет нам нужное количество памяти и возвращает указатель на первый байт.
  32.     // Из-за того, что malloc не знает, для чего именно она выделяет память (int, float или что-то еще), он возвращает
  33.     // специальный "универсальный" указатель void*. Его мы записать в переменную типа int* не можем, поэтому
  34.     // конвертируем его в int* -- (int*)...
  35.     // Конвертировать значение в другой тип (в терминологии программирования корректно говорить "привести тип")
  36.     // можно так -- перед значением в скобках указывается новый тип. Это мы и делаем, записывая перед malloc в скобках
  37.     // новый тип (int*) -- таким образом void* будет "приведен" к int* и его можно будет спокойно записать
  38.     // в переменную array.
  39.     //
  40.     // С одномерными массивами разобрались, перейдем к матрицам (двумерным массивам).
  41.     // Матрица -- это массив указателей, т.е. массив, у которого каждый из элементов -- указатель на
  42.     // первый элемент другого массива. Еще матрицу называют "массивом массивов", так легче представлять ее в голове.
  43.     // Важно! "Массив массивов" не означает, что каждый элемент массива -- массив. Каждый элемент массива массивов --
  44.     // всего лишь указатель на первый элемент другого массива.
  45.     //
  46.     // Если массив целых чисел хранится как указатель на первое целое число (т.е., int*),
  47.     // то массив массивов целых чисел будет храниться как указатель на указатель на первое целое число.
  48.     // Это будет записываться как int**.
  49.     // Для того, чтобы выделить память под этот самый массив массивов, мы вызываем
  50.     // тот же самый malloc, но теперь нам надо выделить память не на N чисел, а на N указателей на целое число.
  51.     // Соответственно, мы спрашиваем у sizeof размер не целого числа -- sizeof(int),
  52.     // а размер указателя на целое число -- sizeof(int*);
  53.  
  54.     int** matrix = (int**)malloc(n * sizeof(int*));
  55.  
  56.     // Мы выделили память для массива массивов, но так как массив массивов -- это всего лишь массив указателей,
  57.     // никто еще не выделил память для самих массивов, на которые эти указатели показывают. На самом деле, самих
  58.     // массивов пока еще не существует, и в данный момент содержимое нашего массива массивов -- это указатели
  59.     // неизвестно куда.
  60.     //
  61.     // Чтобы это исправить, мы N раз выделяем память для массива, который выполняет роль "строки" в нашей матрице
  62.     // и записываем ее в очередной элемент.
  63.     for (line = 0; line < n; ++line) {
  64.         int* line_arr = (int*)malloc(n * sizeof(int));
  65.  
  66.         // Ранее для записи значения в массив по индексу мы использовали
  67.         // квадратные скобки, примерно таким образом:
  68.         // array[i] = value;
  69.         // Однако запись через квадратные скобки -- лишь синтаксический сахар.
  70.         // По сути, когда нам надо записать значение в элемент массива под индексом i, нам достаточно просто
  71.         // взять указатель на первый элемент массива и прибавить к нему это i.
  72.         // То есть, хотим записать в первый элемент массива (т.е., по индексу 0) -- пишем прямо по адресу array.
  73.         // Хотим записать во второй элемент (по индексу 1) -- пишем по адресу array + 1.
  74.         // Но как нам перейти от указателя к самому значению, на которое он указывает? Тут появляется новая
  75.         // операция -- "разыменование" указателя.
  76.         // Разыменование указателя -- это просто-напросто обращение к той самой памяти, на которую он указывает.
  77.         // Производится оно следующим образом: указывается сам указатель и перед ним ставится звездочка.
  78.         // Например, этот код запишет в переменную value значение, которое находится в массиве arr по индексу 0:
  79.         // int value = *arr;
  80.         // Этот код запишет в переменную value значение, которое находится в массиве arr по индексу 2:
  81.         // int value = *(arr + 2);
  82.         // А этот код запишет в массив arr по индексу 2 цифру 3:
  83.         // *(arr + 2) = 3;
  84.         //
  85.         // Код через разыменование и код через квадратные скобки эквивалентен. Например, три строчки ниже делают
  86.         // абсолютно одно и то же:
  87.         // arr[0] = 3;
  88.         // *(arr + 0) = 3;
  89.         // *arr = 3;
  90.         //
  91.         // Теперь, когда мы это все знаем, мы легко можем понять, что строчка ниже просто напросто берет указатель
  92.         // на свежесозданный массив и записывает его в наш массив массивов по очередному индексу.
  93.         *(matrix + line) = line_arr;
  94.     }
  95.  
  96.     for (line = 0; line < n; ++line) {
  97.         for (column = 0; column < n; ++column) {
  98.             printf("Enter value at line %d, column %d: ", line, column);
  99.  
  100.             // scanf требует на вход указатель на элемент. Обычно для этого мы пишем что-то вроде &n
  101.             // (как, например, в этом файле на строке 9). & (амперсанд) перед именем переменной возвращает указатель
  102.             // на нее. В нашем случае вместо этого мы просто не разыменовываем указатель на элемент и передаем в scanf
  103.             // сразу его.
  104.             scanf("%d", (*(matrix + line) + column));
  105.             printf("\n");
  106.         }
  107.     }
  108.  
  109.     // Выводим исходную матрицу.
  110.     printf("Исходная матрица:\n");
  111.     for (line = 0; line < n; ++line) {
  112.         for (column = 0; column < n; ++column) {
  113.             printf("%3d ", *(*(matrix + line) + column));
  114.         }
  115.  
  116.         printf("\n");
  117.     }
  118.  
  119.     // Количество уникальных элементов в матрице заранее известно и вычисляется по этой формуле.
  120.     int unique_values_count = (n * n) / 4;
  121.  
  122.     // Нам нужно следить, сколько уникальных элементов мы уже нашли, чтобы знать, по какому индексу их записывать
  123.     // в массив, где мы храним все уникальные значения.
  124.     int found_values_count = 0;
  125.  
  126.     // Массив, в котором хранятся все уникальные значения. Из него мы будем собирать новую матрицу.
  127.     int* unique_values = (int*)malloc(unique_values_count * sizeof(int));
  128.     int unique_values_index;
  129.  
  130.     // Перебираем все элементы матрицы
  131.     for (line = 0; line < n; ++line) {
  132.         // Если мы уже нашли все уникальные значения, дальше перебирать матрицу смысла нет.
  133.         if (found_values_count == unique_values_count)
  134.             break;
  135.  
  136.         int* line_arr = *(matrix + line);
  137.         for (column = 0; column < n; ++column) {
  138.  
  139.             // Проверка аналогична проверке выше, необходимо указать и здесь, чтобы не перебирать строчку до конца.
  140.             if (found_values_count == unique_values_count)
  141.                 break;
  142.  
  143.             int value = *(line_arr + column);
  144.  
  145.             for (unique_values_index = 0; unique_values_index < found_values_count; ++unique_values_index) {
  146.                 int found_value = *(unique_values + unique_values_index);
  147.  
  148.                 // Если элемент нашелся в массиве уникальных элементов, он "неуникальный" и мы переходим к другому.
  149.                 if (found_value == value) {
  150.                     break;
  151.                 }
  152.             }
  153.  
  154.             // Если мы пришли сюда, значит, break на 140 строке не выполнился и элемент уникальный.
  155.             // Записываем его в массив уникальных значений.
  156.             *(unique_values + found_values_count) = value;
  157.             ++found_values_count;
  158.         }
  159.     }
  160.  
  161.     // Создаем новую матрицу размера n/2 на n/2.
  162.     int new_matrix_line;
  163.     int** new_matrix = (int**)malloc((n / 2) * sizeof(int*));
  164.     for (new_matrix_line = 0; new_matrix_line < n / 2; ++new_matrix_line) {
  165.         *(new_matrix + new_matrix_line) = (int*)malloc((n / 2) * sizeof(int));
  166.     }
  167.  
  168.     // По условию задания мы заполняем новую матрицу не как обычно, по строкам, а по столбцам
  169.     // (т.е. берем каждый столбец и перебираем номер строки).
  170.     new_matrix_line = 0;
  171.     int new_matrix_column = 0;
  172.     for (unique_values_index = 0; unique_values_index < unique_values_count; ++unique_values_index) {
  173.         // Перебираем по очереди элементы массива уникальных значений и записываем их в матрицу,
  174.         // каждый раз увеличивая номер строки
  175.         *(*(new_matrix + new_matrix_line) + new_matrix_column) = *(unique_values + unique_values_index);
  176.         ++new_matrix_line;
  177.  
  178.         // Если после этого new_matrix_line стала равна размеру новой матрицы (т.е., все строчки текущего столбца
  179.         // заполнены), мы обнуляем ее и увеличиваем номер столбца на 1, чтобы приступить к следующему столбцу.
  180.         if (new_matrix_line == n / 2) {
  181.             ++new_matrix_column;
  182.             new_matrix_line = 0;
  183.         }
  184.     }
  185.  
  186.     // Выводим получившуюся матрицу.
  187.     printf("Итоговая матрица:\n");
  188.     for (new_matrix_line = 0; new_matrix_line < n / 2; ++new_matrix_line) {
  189.         for (new_matrix_column = 0; new_matrix_column < n / 2; ++new_matrix_column) {
  190.             printf("%3d ", *(*(new_matrix + new_matrix_line) + new_matrix_column));
  191.         }
  192.  
  193.         printf("\n");
  194.     }
  195. }
  196.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement