Массивы в языке Си

Язык Си / Массивы в языке Си

 

При решении задач с большим количеством данных одинакового типа использование переменных с различными именами, не упорядоченных по адресам памяти, затрудняет программирование. В подобных случаях в языке Си используют объекты, называемые массивами.

Массив — это непрерывный участок памяти, содержащий последовательность объектов одинакового типа, обозначаемый одним именем.

Массив характеризуется следующими основными понятиями:

Элемент массива (значение элемента массива) – значение, хранящееся в определенной ячейке памяти, расположенной в пределах массива, а также адрес этой ячейки памяти.
Каждый элемент массива характеризуется тремя величинами:

  • адресом элемента — адресом начальной ячейки памяти, в которой расположен этот элемент;
  • индексом элемента (порядковым номером элемента в массиве);
  • значением элемента.

 
Адрес массива – адрес начального элемента массива.

Имя массива – идентификатор, используемый для обращения к элементам массива.

Размер массива – количество элементов массива

Размер элемента – количество байт, занимаемых одним элементом массива.

Графически расположение массива в памяти компьютера можно представить в виде непрерывной ленты адресов.
Расположение массива в памяти

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

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

n+k·i

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

Длина массива – количество байт, отводимое в памяти для хранения всех элементов массива.

ДлинаМассива = РазмерЭлемента * КоличествоЭлементов

Для определения размера элемента массива может использоваться функция

 
int sizeof(тип);


Например,
 
 
 
 
sizeof(char) = 1;
sizeof(int) = 4;
sizeof(float) = 4;
sizeof(double) = 8;

Объявление и инициализация массивов

Для объявления массива в языке Си используется следующий синтаксис:

тип имя[размерность]={инициализация};

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

 
int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};  // массив a из 10 целых чисел


Если количество инициализирующих значений, указанных в фигурных скобках, меньше, чем количество элементов массива, указанное в квадратных скобках, то все оставшиеся элементы в массиве (для которых не хватило инициализирующих значений) будут равны нулю. Это свойство удобно использовать для задания нулевых значений всем элементам массива.
 
int b[10] = {0}; // массив b из 10 элементов, инициализированных 0

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

 
int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};


При обращении к элементам массива индекс требуемого элемента указывается в квадратных скобках [].

Пример на Си
1
2
3
4
5
6
7
8
#include <stdio.h>
int main()
{
  int a[] = { 5, 4, 3, 2, 1 }; // массив a содержит 5 элементов
  printf("%d %d %d %d %d\n", a[0], a[1], a[2], a[3], a[4]);
  getchar();
  return 0;
}

Результат выполнения программы:
Массив из 5 элементов

Однако часто требуется задавать значения элементов массива в процессе выполнения программы. При этом используется объявление массива без инициализации. В таком случае указание количества элементов в квадратных скобках обязательно.

 
int a[10];


Для задания начальных значений элементов массива очень часто используется параметрический цикл:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
  int a[5]; // объявлен массив a из 5 элементов
  int i;
  // Ввод элементов массива
  for (i = 0; i<5; i++) 
  {
    printf("a[%d] = ", i);
    scanf("%d", &a[i]); // &a[i] - адрес i-го элемента массива
  }
  // Вывод элементов массива
  for (i = 0; i<5; i++)
    printf("%d ", a[i]); // пробел в формате печати обязателен
  getchar(); getchar();
  return 0;
}

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

Многомерные массивы

В языке Си могут быть также объявлены многомерные массивы. Отличие многомерного массива от одномерного состоит в том, что в одномерном массиве положение элемента определяется одним индексом, а в многомерном — несколькими. Примером многомерного массива является матрица.

Общая форма объявления многомерного массива

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


Элементы многомерного массива располагаются в последовательных ячейках оперативной памяти по возрастанию адресов. В памяти компьютера элементы многомерного массива располагаются подряд, например массив, имеющий 2 строки и 3 столбца,
 
int a[2][3];

будет расположен в памяти следующим образом

Двумерный массив

Общее количество элементов в приведенном двумерном массиве определится как

КоличествоСтрок * КоличествоСтолбцов = 2 * 3 = 6.

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

КоличествоЭлементов * РазмерЭлемента = 6 * 4 = 24 байта.

Инициализация многомерных массивов

Значения элементов многомерного массива, как и в одномерном случае, могут быть заданы константными значениями при объявлении, заключенными в фигурные скобки {}. Однако в этом случае указание количества элементов в строках и столбцах должно быть обязательно указано в квадратных скобках [].

Пример на Си

1
2
3
4
5
6
7
8
9
#include <stdio.h>
int main()
{
  int a[2][3] = { 1, 2, 3, 4, 5, 6 };
  printf("%d %d %d\n", a[0][0], a[0][1], a[0][2]);
  printf("%d %d %d\n", a[1][0], a[1][1], a[1][2]);
  getchar();
  return 0;
}

Результат выполнения
Матрица 2x3

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

Пример на Си

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 <stdio.h>
int main()
{
  int a[2][3]; // массив из 2 строк и 3 столбцов
  int i, j;
  // Ввод элементов массива
  for (i = 0; i<2; i++)  // цикл по строкам
  {
    for (j = 0; j<3; j++) // цикл по столбцам
    {
      printf("a[%d][%d] = ", i, j);
      scanf("%d", &a[i][j]);
    }
  }
  // Вывод элементов массива
  for (i = 0; i<2; i++)  // цикл по строкам
  {
    for (j = 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 <stdio.h>
// Функция обмена
void change(int *x, int n)
{
  // x - указатель на массив (адрес массива)
  // n - размер массива
  int i;
  int max, index;
  max = x[0];
  index = 0;
  // Поиск максимального элемента
  for (i = 1; i<n; i++)
  {
    if (x[i]>max)
    {
      max = x[i];
      index = i;
    }
  }
  // Обмен
  x[index] = x[0];
  x[0] = max;
}
// Главная функция
int main()
{
  int a[10];
  int i;
  for (i = 0; i<10; i++)
  {
    printf("a[%d] = ", i);
    scanf("%d", &a[i]);
  }
  change(a, 10);  // вызов функции обмена
          // Вывод элементов массива
  for (i = 0; i<10; i++)
    printf("%d ", a[i]);
  getchar();
  getchar();
  return 0;
}

Результат выполнения
Поменять местами наибольший и первый элементы массива

Пример на Си Дан массив размерности n. Вычислить произведение четных элементов

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
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
// Функция вычисления произведения чётных элементов
int func(int *x, int n)  // произведение четных элементов
{
  int p = 1;  // начальное значение произведения
  int i;
  for (i = 0; i<n; i++) 
  {
    if (x[i] % 2 == 0)  // остаток от деления на 2 равен 0?
      p = p * x[i];
  }
  return p;
}
// Главная функция
int main() 
{
  int a[5]; // объявлен массив a из 5 элементов
  int i;
  int pr;
  // Ввод элементов массива
  for (i = 0; i<5; i++) 
  {
    printf("a[%d] = ", i);
    scanf("%d", &a[i]); // &a[i] - адрес i-го элемента массива
  }
  pr = func(a, 5); // вычисление произведения
  printf("\n pr = %d", pr); // вывод произведения четных элементов
  getchar();   getchar();
  return 0;
}

Результат выполнения
Произведение четных элементов массива


Назад: Язык Си

Комментариев к записи: 22

  • Владимир
    Здравствуйте Елена могли бы пожалуйста разъяснить следующую программу. Tут 6 семей дали свою оценку для анкеты,программа посчитала частоту оценок.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #include <stdio.h>
    #define SEMYA 6
    #define OCENKI 6

    int main()
    {
      
      int j, i; 
      int otvet[SEMYA] ={2,2,1,3,3,3}; 
      int freq[OCENKI]={};
      for(i=0; i<=SEMYA-1; i++)
      {
        freq[otvet[i]] = freq[otvet[i]]+1; 
      }
      printf(" ocenka:    ferq:\t\n");
      for (j=0;j<=OCENKI-1; j++)
      {
        printf(" \t%d \t%d\n", j, freq[j]);
      }

      return (0);
    }

    • Елена Вставская
      В массиве freq сохраняются значения на 1 больше соответствующих значений массива otvet. Затем значения из массива freq выводятся.

      • Александр
        Вы уверены? Вроде в массиве freq сохраняется частота (freq от frequency частота) встречающихся оценок. То есть "2" встречается 2 раза, "1" только один раз, "3" встречается 3 раза. Если бы массив otvet был бы {5,4,5,4,5,4,2,5}, то результат freq содержал {0,0,1,0,3,4}. Еще раз, otvet содержит сколькотвсего получили оценок "0","1","2","3","4","5". Понятно, почему массив otvet объявлен как otvet[6], оценки то могут быть от 0 до 5

  • Здравствуйте! Можете, пожалуйста, привести пример функции, в которой числа в массиве генерируются случайным образом генератором случайных чисел. Имеется ввиду, чтобы пользователь ввел количество чисел в массиве, а они уже сгенерировались сами. Буду очень признателен! ответ прошу прислать на мой e-mail: mnuriyev101@gmail.com


    • Елена Вставская
      Видимо, через перезаписываемые элементов на новые места. Задачу не совсем поняла.

  • Дмитрий
    Использование getchar или system( "pause" ) - это убогий костыль, призванный оставить открытым окошко консоли в среде программирования под Windows. Если бы программа запускалась непосредственно из консоли (в винде это приложение "Командная строка"), то этот костыль был бы не нужен. И еще дружеский совет: не используйте scanf (а лучше просто забудьте про ее существование). При использовании этой функции очень сложно сделать адекватную обработку ошибок. Для ввода лучше использовать fgets, а затем разбирать полученный результат. Для преобразования чисел в stdlib.h есть отличные функции типа strtol, strtof и т.п.

    • Вот речь не мальчика, но МУЖА!!!! (профи с двух слов положил всех, молодец!!!)

  • Дмитрий
    "Пример Дан массив размерности n. Вычислить произведение четных элементов" Вы тут дважды неправильно поняли задание. Размерность - количество измерений массива, а не его длина. Четных элементов - значит каждого второго элемента, а не всех элементов, делящихся на два без остатка.  

    • Елена Вставская
      Отчасти согласна - условие задачи не должно иметь двоякого толкования. Но, как правило, нужно, чтобы кто-то указал на эту неоднозначность, поскольку самостоятельно ее можно не увидеть. Если понимать задачу в Вашей интерпретации, то как тогда в многомерном массиве находить элементы с четными индексами? И всё-таки, я считаю, что четный элемент подразумевает четность значения элемента. А если речь идет об элементах, расположенных на четных местах в массиве, то это - элементы с четными индексами.

  • Здравствуйте, а что хранится в массиве в случае объявления массива без инициализации?

    • Елена Вставская
      В случае объявления массива без инициализации в массиве хранится мусор. До инициализации значения элементов массива использовать нельзя.

      • Спасибо за ответ, правильно я понимаю, "мусор" - это то, что было в оперативной памяти на данный момент?

        • Елена Вставская
          Не могу утверждать на 100%, что это то, что хранилось в оперативной памяти на данный момент. Мусор - это неизвестные пользователю значения.

      • Имеется ввиду нельзя брать значения из массива мусорные? А, записывать в ячейки массива ведь можно неициализированного? Ну записал, я к примеру, сроку, надо же просто быть уверенным, что длины массива хватит, это раз, а во вторых что нуль-терминатор в конце стоит (если массив символов). Так же? А что там дальше после нуль-терминатора нам же на важно по идее для чтения потом уже этой строки, я правильно понимаю или все таки это важно иницилизировать нулями весь массив в любом случае надо при первом использовании? И еще. Если ли замена стандартной функции копирования строки в массив, чтобы она проверяла все таки что в массиве хватает места для записи? Или это все самому надо в программе проверять прописывать всегда перед записью в массив?

        • Елена Вставская
          Если мы введем часть массива символов, но не все значения, то при выводе может обнаружиться мусор, потому что вывод осуществляется до символа, равного нулю. То, что стоит после нуль-символа, нас действительно не интересует. Стандартная функция strcpy() не следит за выделенной областью памяти, поэтому желательно это проверять самостоятельно или использовать защищённую функцию strcpy_s()


  • Владимир
    Здравствуйте, для чего пишется getchar 2-жды в конце каждой программы?

    • Елена Вставская
      Владимир, getchar() дважды пишется в программах, которые используют ввод с помощью scanf() или cin. Дело в том, что когда пользователь при вводе нажимает Enter, генерируются сразу два символа - перевод строки и возврат каретки. Если в конце программы написать один getchar(), то программа прекратит свое выполнение (закроет консоль), поскольку оставшийся в буфере символ возврата каретки будет воспринят функцией getchar() как введенный. Поэтому есть простое правило: если в программе использовался ввод с помощью scanf() или cin, в конце нужно дважды вызвать функцию getchar(). В тех программах, где нет ввода, можно использовать один getchar(), например здесь. Альтернативой функции getchar() также может служить вызов system("pause"); из библиотеки stdlib.h. При этом пользователю будет выведено сообщение "Для продолжения нажмите любую клавишу...".

    • Подскажите,как СИ знает длину массива? Где хранится длина создаваемого массива? Ведь её тоже НАДО ГДЕ ТО ХРАНИТЬ! Например, я знаю, что языке Forth правилом хорошего тона является прописывание в начальной ячейке массива длины данного массива, чтобы ты, зайдя в первую ячейку смог считать (узнать) длину всего массива. А в языке СИ где хранится длина массива? В какой то другой области памяти или в какой то переменной типа int?

      • Елена Вставская
        В другой области памяти для динамического массива, для статического задаётся константой.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *