Архив за месяц: Октябрь 2013

Занятие 19. Двумерные массивы.


Добрый день друзья.
Сегодня затронем новую тему. Двумерные массивы. Или их еще называют матрицы.   

Прочитайте улучшенную версию этого урока "Двумерные массивы".

В новой версии:

  • Ещё более доступное объяснение
  • Дополнительные материалы
  • 10 задач на программирование с автоматической проверкой решения

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

Двумерный массив это прямоугольная таблица с определенным количеством строк и столбиков.  Каждая строка и каждый столбик имеют свой порядковый номер. На рисунке изображен двумерный массив, который имеет 6 строк и 7 столбцов.
Рис.1. Двумерный массив. Общий вид.

Обратите внимание, что столбцы и строки нумеруются, начиная с нуля, как и в одномерных массивах.
Сразу же покажу, как объявить двумерный массив. В сущности, ничего нового. Синтаксис прост и очень похож на объявление обычного массива и любой другой переменной.  Сначала указываем тип данных, затем имя массива, а после в квадратных скобках его размерность, то есть количество строк и количество столбиков.  На рисунке ниже мы объявили двумерный массив размерностью 6 на 7.
Рис.2 Двумерный массив, объявление.

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

Следующий естественный вопрос:

Как работать с двумерным массивом?

Как и другие переменные, двумерные массивы мы можем инициализировать  при их объявлении. Для этого нам нужно указать все элементы массива. Делается это следующим образом.

Листинг 19.1
int arr [2][4] = {{1,2,4,29},{3,4,6,1}};

Записанный выше код создает массив с двумя строчками и четырьмя столбцами, и присваивает каждому элементу определенные значения. Получится вот такой массив.
Рис.3. Двумерный массив инициализированный при объявлении


Кроме того, мы можем задать только некоторые элементы массива, тогда остальные будут заполнены нулями. Например:

Листинг 19.2
int arr [2][4] = {{1,2,4},{3,4}};

При таком объявлении мы получим массив, который изображен на следующем рисунке.
Рис.4. Двумерный массив, инициализированный не полностью.


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

Как работать с отдельным элементом массива.

Понятно, что любой элемент задается номер строки и столбца, на пересечении которых он расположен.  Посмотрите на следующий рисунок. На нем мы выбрали элемент, который находится во второй строке и третьем столбике (в естественной нумерации).

Рис.5. Обращение к элементу двумерного массива.

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

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

Листинг19.3
#include <stdio.h> 
int main (){ 
      int arr [2][4] = {{1,2,4},{3,4}}; 
      for (int i=0; i<2; i++){ // 1 цикл 
            for (int j=0; j<4; j++) // 2 цикл 
                  printf("%d\t",arr[i][j]); 
            printf("\n"); 
      } 
return 0; 
}  
Данная программа, выводит последовательно все элементы массива. Результат её работы, представлен на следующем рисунке.
Рис.6. Двумерный массив. Поэлементный вывод на экран.

Как  работает вложенный цикл, вам уже должно быть понятно.
Сначала переменной i присваивается значение нуль, проверяется условие 0<2, и так как оно выполняется, программа начинает выполнять тело первого цикла. В теле первого цикла программа опять попадает в цикл, теперь уже второй. Переменной j присваивается значение 0 и проверяется условие 0<4. Оно истинно, поэтому выполняется тело второго цикла. Оно состоит из одной инструкции вывода на экран элемента arr[i][j]. Так как на данном шаге у нас i=0 j=0, то выводится значение элемент из нулевой строки и нулевого столбика. В нашем примере это элемент число 1. Тело второго цикла закончилось, происходит увеличение j на единицу j=1.
Проверка условия 1<4. Выполнение тела второго цикла: вывод на экран элемента arr[0][1] в нашем случае это 2. И так далее …
Используя вложенные циклы, мы можем и заполнять двумерные массивы.  

Зачем нужны двумерные массивы?

Двумерные массивы используются для хранения данных одинакового типа и одинаковой структуры. Приведу пример. В одном из практических заданий к прошлым урокам,  необходимо было составить программу, которая хранила оценки ученика по разным предметам. Для их хранения в памяти компьютера мы использовали несколько одномерных массивов. Это было не очень удобно. Для этих же целей, мы могли использовать двумерный массив. Например, нам нужно хранить оценки ученика по русскому, математике, физике и информатике, за целый месяц. Для этого мы создадим массив с 4 строками и 30 столбиками (или наоборот, 30 строк и 4 столбика, кому как больше нравится), и в каждую отдельную строчку будем записывать оценки по какому либо предмету. Это гораздо удобнее и практичнее.
Например, рассмотрим такую задачку. Пусть нам нужно было бы найти среднее арифметическое всех оценок по всем предметам.  Если бы у нас было четыре одномерных массива, то нам пришлось бы писать четыре цикла, в каждом из них считать среднее арифметическое оценок по одному предмету, потом  находить среднее арифметическое этих четырех чисел. Если оценки у нас хранятся в двумерном массиве, нам достаточно будет просто пройтись по всем элементам массива, используя два вложенных цикла for, и найти среднее арифметическое.
Но это еще не всё. Представьте, что вы хотите добавить один предмет и снова посчитать среднюю арифметическую оценку для всех предметов.   Сколько мороки будет, если вы храните оценки в одномерных массивах. Вам придется кучу изменений вносить, дописывать еще один цикл (а вдруг добавилось 7 предметов?). Потом еще среднее искать между этими цифрами.
Другое дело если вы храните оценки в двумерном массиве. Всего лишь изменить условие в одном из циклов. Поменять одно число, представляете как удобно?


Напишите в комментариях пожалуйста, понятен ли вам этот пример, или лучше его подробно расписать?

Если этот материал кажется вам полезным, расскажите о нем друзьям используя кнопки основных социальных сетей, расположенные ниже.

Практическое задание.

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

Готовое решение пользователя с ником "Дмитрий". За проявленное упорство и трудолюбие, и как первый выполнивший правильно практическое задание, Дмитрий награждается печенькой:

Занятие 19. Двумерные массивы.


Добрый день друзья.
Сегодня затронем новую тему. Двумерные массивы. Или их еще называют матрицы.   

Прочитайте улучшенную версию этого урока "Двумерные массивы".

В новой версии:

  • Ещё более доступное объяснение
  • Дополнительные материалы
  • 10 задач на программирование с автоматической проверкой решения

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

Двумерный массив это прямоугольная таблица с определенным количеством строк и столбиков.  Каждая строка и каждый столбик имеют свой порядковый номер. На рисунке изображен двумерный массив, который имеет 6 строк и 7 столбцов.
Рис.1. Двумерный массив. Общий вид.

Обратите внимание, что столбцы и строки нумеруются, начиная с нуля, как и в одномерных массивах.
Сразу же покажу, как объявить двумерный массив. В сущности, ничего нового. Синтаксис прост и очень похож на объявление обычного массива и любой другой переменной.  Сначала указываем тип данных, затем имя массива, а после в квадратных скобках его размерность, то есть количество строк и количество столбиков.  На рисунке ниже мы объявили двумерный массив размерностью 6 на 7.
Рис.2 Двумерный массив, объявление.

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

Следующий естественный вопрос:

Как работать с двумерным массивом?

Как и другие переменные, двумерные массивы мы можем инициализировать  при их объявлении. Для этого нам нужно указать все элементы массива. Делается это следующим образом.

Листинг 19.1
int arr [2][4] = {{1,2,4,29},{3,4,6,1}};

Записанный выше код создает массив с двумя строчками и четырьмя столбцами, и присваивает каждому элементу определенные значения. Получится вот такой массив.
Рис.3. Двумерный массив инициализированный при объявлении


Кроме того, мы можем задать только некоторые элементы массива, тогда остальные будут заполнены нулями. Например:

Листинг 19.2
int arr [2][4] = {{1,2,4},{3,4}};

При таком объявлении мы получим массив, который изображен на следующем рисунке.
Рис.4. Двумерный массив, инициализированный не полностью.


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

Как работать с отдельным элементом массива.

Понятно, что любой элемент задается номер строки и столбца, на пересечении которых он расположен.  Посмотрите на следующий рисунок. На нем мы выбрали элемент, который находится во второй строке и третьем столбике (в естественной нумерации).

Рис.5. Обращение к элементу двумерного массива.

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

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

Листинг19.3
#include <stdio.h> 
int main (){ 
      int arr [2][4] = {{1,2,4},{3,4}}; 
      for (int i=0; i<2; i++){ // 1 цикл 
            for (int j=0; j<4; j++) // 2 цикл 
                  printf("%d\t",arr[i][j]); 
            printf("\n"); 
      } 
return 0; 
}  
Данная программа, выводит последовательно все элементы массива. Результат её работы, представлен на следующем рисунке.
Рис.6. Двумерный массив. Поэлементный вывод на экран.

Как  работает вложенный цикл, вам уже должно быть понятно.
Сначала переменной i присваивается значение нуль, проверяется условие 0<2, и так как оно выполняется, программа начинает выполнять тело первого цикла. В теле первого цикла программа опять попадает в цикл, теперь уже второй. Переменной j присваивается значение 0 и проверяется условие 0<4. Оно истинно, поэтому выполняется тело второго цикла. Оно состоит из одной инструкции вывода на экран элемента arr[i][j]. Так как на данном шаге у нас i=0 j=0, то выводится значение элемент из нулевой строки и нулевого столбика. В нашем примере это элемент число 1. Тело второго цикла закончилось, происходит увеличение j на единицу j=1.
Проверка условия 1<4. Выполнение тела второго цикла: вывод на экран элемента arr[0][1] в нашем случае это 2. И так далее …
Используя вложенные циклы, мы можем и заполнять двумерные массивы.  

Зачем нужны двумерные массивы?

Двумерные массивы используются для хранения данных одинакового типа и одинаковой структуры. Приведу пример. В одном из практических заданий к прошлым урокам,  необходимо было составить программу, которая хранила оценки ученика по разным предметам. Для их хранения в памяти компьютера мы использовали несколько одномерных массивов. Это было не очень удобно. Для этих же целей, мы могли использовать двумерный массив. Например, нам нужно хранить оценки ученика по русскому, математике, физике и информатике, за целый месяц. Для этого мы создадим массив с 4 строками и 30 столбиками (или наоборот, 30 строк и 4 столбика, кому как больше нравится), и в каждую отдельную строчку будем записывать оценки по какому либо предмету. Это гораздо удобнее и практичнее.
Например, рассмотрим такую задачку. Пусть нам нужно было бы найти среднее арифметическое всех оценок по всем предметам.  Если бы у нас было четыре одномерных массива, то нам пришлось бы писать четыре цикла, в каждом из них считать среднее арифметическое оценок по одному предмету, потом  находить среднее арифметическое этих четырех чисел. Если оценки у нас хранятся в двумерном массиве, нам достаточно будет просто пройтись по всем элементам массива, используя два вложенных цикла for, и найти среднее арифметическое.
Но это еще не всё. Представьте, что вы хотите добавить один предмет и снова посчитать среднюю арифметическую оценку для всех предметов.   Сколько мороки будет, если вы храните оценки в одномерных массивах. Вам придется кучу изменений вносить, дописывать еще один цикл (а вдруг добавилось 7 предметов?). Потом еще среднее искать между этими цифрами.
Другое дело если вы храните оценки в двумерном массиве. Всего лишь изменить условие в одном из циклов. Поменять одно число, представляете как удобно?


Напишите в комментариях пожалуйста, понятен ли вам этот пример, или лучше его подробно расписать?

Если этот материал кажется вам полезным, расскажите о нем друзьям используя кнопки основных социальных сетей, расположенные ниже.

Практическое задание.

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

Готовое решение пользователя с ником "Дмитрий". За проявленное упорство и трудолюбие, и как первый выполнивший правильно практическое задание, Дмитрий награждается печенькой:

1c 8.1 Как найти файлы во вложеных каталогах

Как найти все файлы во вложеных папках? Есть простой способ:



Процедура НайтиВсеФайлы(Путь)
НайденныеФайлы
= НайтиФайлы(Путь, "*.*");
Для каждого НайденныйФайл Из НайденныеФайлы Цикл
Если НайденныйФайл.ЭтоКаталог() Тогда
// Ищем все файлы в найденном каталоге
НайтиВсеФайлы
(НайденныйФайл.ПолноеИмя);
Иначе
// Это файл, выведем его полное имя
Сообщить
(НайденныйФайл.ПолноеИмя);
НовыйЭлемент
= Справочники.Файлы.СоздатьЭлемент();
НовыйЭлемент
.Наименование = НайденныйФайл.Имя;
НовыйЭлемент
.Путь = НайденныйФайл.Путь;
НовыйЭлемент
.РазмерФайла = (НайденныйФайл.Размер())/1000;
НовыйЭлемент
.ПолноеНаименование = НайденныйФайл.ПолноеИмя;
НовыйЭлемент
.Записать();
КонецЕсли;
КонецЦикла;
КонецПроцедуры

источник

1c 8.1 Как найти файлы во вложеных каталогах

Как найти все файлы во вложеных папках? Есть простой способ:



Процедура НайтиВсеФайлы(Путь)
НайденныеФайлы
= НайтиФайлы(Путь, "*.*");
Для каждого НайденныйФайл Из НайденныеФайлы Цикл
Если НайденныйФайл.ЭтоКаталог() Тогда
// Ищем все файлы в найденном каталоге
НайтиВсеФайлы
(НайденныйФайл.ПолноеИмя);
Иначе
// Это файл, выведем его полное имя
Сообщить
(НайденныйФайл.ПолноеИмя);
НовыйЭлемент
= Справочники.Файлы.СоздатьЭлемент();
НовыйЭлемент
.Наименование = НайденныйФайл.Имя;
НовыйЭлемент
.Путь = НайденныйФайл.Путь;
НовыйЭлемент
.РазмерФайла = (НайденныйФайл.Размер())/1000;
НовыйЭлемент
.ПолноеНаименование = НайденныйФайл.ПолноеИмя;
НовыйЭлемент
.Записать();
КонецЕсли;
КонецЦикла;
КонецПроцедуры

источник

1c 8.1: Как выгрузить конфигурацию в файл

Иногда спрашивают как в 1с 8.1 можно выгрузить отдельно конфигурацию в файл, не выгружая базу.
Вариант выгрузки такой:


1. Выбираем в меню конфигуратора  - Сохранить конфигурацию в файл


1с 8. Как сохранить конфигурацию в файл

Придумать имя файла. Например, текущая дата, название базы.


1с 8 Диалог. Сохранить как . Сохранение конфигурации в файл

После процедуры сохранения появится надпись:

1с 8.х Сохранение конфигурации в файл завершено

1c 8.1: Как выгрузить конфигурацию в файл

Иногда спрашивают как в 1с 8.1 можно выгрузить отдельно конфигурацию в файл, не выгружая базу.
Вариант выгрузки такой:


1. Выбираем в меню конфигуратора  - Сохранить конфигурацию в файл


1с 8. Как сохранить конфигурацию в файл

Придумать имя файла. Например, текущая дата, название базы.


1с 8 Диалог. Сохранить как . Сохранение конфигурации в файл

После процедуры сохранения появится надпись:

1с 8.х Сохранение конфигурации в файл завершено

Занятие 18. Передача одномерных массивов в функцию. Возвращение массива из функции.

Добрый вечер друзья. Продолжаем изучать работу с массивами и указателями.

Сегодня научимся передавать массив в функцию и возвращать массив из функции.

Прочитайте улучшенную версию этого урока "Передача аргументов в функцию".

В новой версии:

  • Ещё более доступное объяснение
  • Дополнительные материалы
  • 12 задач на программирование с автоматической проверкой решения


Итак, начнем с первого пункта. Пусть нам необходимо передать массив в функцию и там его обработать. Ну допустим вывести его элементы на экран. Тут возможны два варианта.
1. У нас статический массив.
2. У нас динамический массив.

В зависимости от этого и нужно плясать.

Первый случай. Передача в функцию статического массива. 

Для определенности будем передавать массив символов. Напишем функцию, которая принимает строку и выводит её на экран.

Листинг 18.1


void print_arr(char a[], int n){
      for (int i=0; i<n; i++)
            printf("%c",a[i]);
      printf("\n");
}


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

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

Напишем самую простую программу, которая будет использовать эту функцию.

Листинг 18.2


#include <stdio.h>
#include <string.h>
void print_arr(char a[], int n){
      for (int i=0; i<n; i++)
            printf("%c",a[i]);
      printf("\n");

}
int main(){
      char arr[]="kirill";
      int t=strlen(arr);
      print_arr(arr,t);
      return 0;
}


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

Второй случай. Передача в функцию динамического массива.

Давайте решим такую задачу. имеется текстовый файл input.txt. Пусть в нем в первой строчке записано натуральное число N не превосходящее 100. А в следующих N строках записаны некоторые вещественные числа. Пусть мы хотим посчитать среднее арифметическое всех этих чисел и вывести.

Удобно для хранения чисел завести динамический массив. Хотя, на самом деле, в этой задаче мы могли бы просто читать числа из файла и сразу вычислять их среднее значение. Но как-то я не могу придумать пока более подходящего примера.

И так напишем требуемую программу.

Листинг 18.3
#include <stdio.h>
void sa_arr(float *a, int n){
      double summ=0;
      for (int i=0; i<n; i++)
            summ+=a[i];
      printf("srednee arifmeticheskoe: %.2f\n", summ/n);
}

int main(){
      freopen("input.txt","r",stdin);
      int N;
      scanf("%d",&N);
      float *arr = new (float [N]);
      for (int i=0; i<N; i++)
            scanf("%f", &arr[i]);
      sa_arr(arr,N);
      delete []arr;
      return 0;
}


Обратите внимание на аргументы функции. Как видите, если мы передаем динамический массив, то нам нужно явно указывать на то, что функция принимает указатель на первый элемент массива. В случае одномерного массива это не критично, и вы можете объявить аргументы как и в прошлый раз, т.е.
void sa_arr(float a[], int n)
но так делать не стоит. Приучайтесь сразу передавать динамические массивы таким образом.

Теперь вернемся к нашим отличиям а так же второму вопросу.

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

Несколько изменим наше предыдущую программу. Изменим один из элементов внутри функции. И посмотрим, что будет с исходным массивом.

Листинг 18.4


#include <stdio.h>
void sa_arr(float *a, int n){
      double summ=0;
      for (int i=0; i<n; i++)
            summ+=a[i];
      printf("srednee arifmeticheskoe: %.2f\n", summ/n);
      //изменим какой-нибудь элемент массива внтури функции
      a[2]=43.2;
} 


int main(){
      freopen("D:\\input1.txt","r",stdin);
      int N;
      scanf("%d",&N);
      float *arr = new (float [N]);
      for (int i=0; i<N; i++)
            scanf("%f", &arr[i]);
      //выводим массив
      for (int i=0; i<N; i++)
            printf("%f\t",arr[i]);
      printf("\n");
      sa_arr(arr,N);    
      // снова выводим массив на экран, после вызова функции
      // внутри которой, мы изменили один его элемент
      for (int i=0; i<N; i++)
            printf("%f\t",arr[i]);
      printf("\n");
      delete []arr;
      return 0; 
}

Результат работы программы на следующем рисунке.


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

Если же, по каким-то причинам, мы не хотим, чтобы элементы массива внутри функции менялись, то нам нужно внутри функции сделать копию переданного массива и работать уже с этой копией. Не забывайте, при использовании динамических массивов убираться за собой в памяти.


Задание для практической работы:

1. Напишите функцию, которая сортирует массив методом пузырька и выводит отсортированный массив на экран. В этом варианте программы, сделайте так, чтобы изменения не коснулись исходного массива.

2. Напишите функцию, которая ищет максимальный элемент в массиве.

3. Напишите функцию, которая принимает два массива одинаковой длинны и поэлементно перемножает их. Результат работы она записывает в элементы первого массива. Т.е. буквально она должна вернуть результат свой работы в первом массиве.
В программе, НЕ В ФУНКЦИИ, выведите результат на экран.

Если мы передаем массивы
[2,5,6]
[1,4,2]

то в результате своей работы, программа должна вывести
2 20 12

Занятие 18. Передача одномерных массивов в функцию. Возвращение массива из функции.

Добрый вечер друзья. Продолжаем изучать работу с массивами и указателями.

Сегодня научимся передавать массив в функцию и возвращать массив из функции.

Прочитайте улучшенную версию этого урока "Передача аргументов в функцию".

В новой версии:

  • Ещё более доступное объяснение
  • Дополнительные материалы
  • 12 задач на программирование с автоматической проверкой решения


Итак, начнем с первого пункта. Пусть нам необходимо передать массив в функцию и там его обработать. Ну допустим вывести его элементы на экран. Тут возможны два варианта.
1. У нас статический массив.
2. У нас динамический массив.

В зависимости от этого и нужно плясать.

Первый случай. Передача в функцию статического массива. 

Для определенности будем передавать массив символов. Напишем функцию, которая принимает строку и выводит её на экран.

Листинг 18.1


void print_arr(char a[], int n){
      for (int i=0; i<n; i++)
            printf("%c",a[i]);
      printf("\n");
}


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

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

Напишем самую простую программу, которая будет использовать эту функцию.

Листинг 18.2


#include <stdio.h>
#include <string.h>
void print_arr(char a[], int n){
      for (int i=0; i<n; i++)
            printf("%c",a[i]);
      printf("\n");

}
int main(){
      char arr[]="kirill";
      int t=strlen(arr);
      print_arr(arr,t);
      return 0;
}


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

Второй случай. Передача в функцию динамического массива.

Давайте решим такую задачу. имеется текстовый файл input.txt. Пусть в нем в первой строчке записано натуральное число N не превосходящее 100. А в следующих N строках записаны некоторые вещественные числа. Пусть мы хотим посчитать среднее арифметическое всех этих чисел и вывести.

Удобно для хранения чисел завести динамический массив. Хотя, на самом деле, в этой задаче мы могли бы просто читать числа из файла и сразу вычислять их среднее значение. Но как-то я не могу придумать пока более подходящего примера.

И так напишем требуемую программу.

Листинг 18.3
#include <stdio.h>
void sa_arr(float *a, int n){
      double summ=0;
      for (int i=0; i<n; i++)
            summ+=a[i];
      printf("srednee arifmeticheskoe: %.2f\n", summ/n);
}

int main(){
      freopen("input.txt","r",stdin);
      int N;
      scanf("%d",&N);
      float *arr = new (float [N]);
      for (int i=0; i<N; i++)
            scanf("%f", &arr[i]);
      sa_arr(arr,N);
      delete []arr;
      return 0;
}


Обратите внимание на аргументы функции. Как видите, если мы передаем динамический массив, то нам нужно явно указывать на то, что функция принимает указатель на первый элемент массива. В случае одномерного массива это не критично, и вы можете объявить аргументы как и в прошлый раз, т.е.
void sa_arr(float a[], int n)
но так делать не стоит. Приучайтесь сразу передавать динамические массивы таким образом.

Теперь вернемся к нашим отличиям а так же второму вопросу.

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

Несколько изменим наше предыдущую программу. Изменим один из элементов внутри функции. И посмотрим, что будет с исходным массивом.

Листинг 18.4


#include <stdio.h>
void sa_arr(float *a, int n){
      double summ=0;
      for (int i=0; i<n; i++)
            summ+=a[i];
      printf("srednee arifmeticheskoe: %.2f\n", summ/n);
      //изменим какой-нибудь элемент массива внтури функции
      a[2]=43.2;
} 


int main(){
      freopen("D:\\input1.txt","r",stdin);
      int N;
      scanf("%d",&N);
      float *arr = new (float [N]);
      for (int i=0; i<N; i++)
            scanf("%f", &arr[i]);
      //выводим массив
      for (int i=0; i<N; i++)
            printf("%f\t",arr[i]);
      printf("\n");
      sa_arr(arr,N);    
      // снова выводим массив на экран, после вызова функции
      // внутри которой, мы изменили один его элемент
      for (int i=0; i<N; i++)
            printf("%f\t",arr[i]);
      printf("\n");
      delete []arr;
      return 0; 
}

Результат работы программы на следующем рисунке.


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

Если же, по каким-то причинам, мы не хотим, чтобы элементы массива внутри функции менялись, то нам нужно внутри функции сделать копию переданного массива и работать уже с этой копией. Не забывайте, при использовании динамических массивов убираться за собой в памяти.


Задание для практической работы:

1. Напишите функцию, которая сортирует массив методом пузырька и выводит отсортированный массив на экран. В этом варианте программы, сделайте так, чтобы изменения не коснулись исходного массива.

2. Напишите функцию, которая ищет максимальный элемент в массиве.

3. Напишите функцию, которая принимает два массива одинаковой длинны и поэлементно перемножает их. Результат работы она записывает в элементы первого массива. Т.е. буквально она должна вернуть результат свой работы в первом массиве.
В программе, НЕ В ФУНКЦИИ, выведите результат на экран.

Если мы передаем массивы
[2,5,6]
[1,4,2]

то в результате своей работы, программа должна вывести
2 20 12