Лекция 6. Массивы

#include <stdio.h>
int main(void)
{
    int a[5]; // Массив из 5 чисел
    printf("Введите 5 чисел\n");
    int i;
    for(i = 0; i < 5; ++i) {
        // стандартная идиома для прохода по индексам массива
        scanf("%d", &a[i]);
    }
    for(i = 0; i < 5; ++i) {
        printf("%d", a[i]);
    }
    return 0;
}

Определение переменной содержащей массив, выглядит так:

тип имя[размер];

тип - тип элемента массива

имя - имя переменной

размер - число элементов массива

Примеры:

double  a[10];
int b[5], c, d[55]; // массив, число, массив.

В массиве из N элементов допустимыми индексами являются 0,...,(N-1) включительно. При доступе по индексам -1, -2, ..., N, N+1... возможен доступ к переменным программы.

int a[5];
a[0] - первый элемент
a[4] - последний элемент

По умолчанию массивы не инициализируются и в них лежит мусор, как и в переменных:

int a[4]; // Значения не инициализированы.

Но в языке Си существует специальный синтаксис для заполнения массивов значениями, известными при компиляции:

int a[4] = {10, 6, 7, 8};

Данный код эквивалентен следующему:

int a[4];
a[0] = 10;
a[1] = 6;
a[2] = 7
a[3] = 8;

Довольно часто бывает необходимо проинициализировать массив нулями, это можно сделать так:

int c[10] = {}; // Весь массив будет заполнен нулями

Размер памяти под массив из N элементов некоторого типа равен N * sizeof(тип) байт. Оператор sizeof(тип) возвращает размер типа в байтах.

тип имя[размер];

Чтение и запись элементов массива

В принципе, мы уже читали и записывали элементы массивов, но давайте ещё раз явно обсудим это:

int a[3] = {4, 5, 6};
int x = a[0]; // Чтение значения и присваивание его.
printf("%d\n", a[1]) // Чтение значения и передача в функцию.

a[1] = 100; // Запись значения.

Директива препроцесора #define

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

#include <stdio.h>
#define N 5 // Определяем замену идентификатора N на 5
// NB: в #define нет ни =, ни ;

int main(void)
{
    int a[N];
    int i;
    for(i = 0; i < N; ++i) {
        scanf("%d", &a[i]);
    }
    int sum = 0;
    for(i = 0; i < N; ++i) {
        sum += a[i];
    }
    printf("sum of N elements is %d.\n", sum);
    return 0;
}
#define A 1+1

...
printf("%d\n", A*A);
...

Как вы думаете, что выведет этот код?..

Правильно, тройку! Препроцессор проведёт текстовые подстановки идентификатора A, и в итоге компилятор будет компилировать строку

printf("%d\n", 1+1*1+1);

Чтобы заставить компилятор трактовать всю константу A как единое целое, текст для замены нужно взять в скобки:

#define A (1+1)

Кроме того, с помощью директивы #define можно подставлять не только константы, но и целые куски кода:

#define HELLO printf("Hello, world!\n");

int main(void)
{
    HELLO
    return 0;
}

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