Выборочная инициализация массивов и структур

При всей своей примитивности язык C иногда преподносит приятные сюрпризы. Например, в совершенный восторг меня приводит способ инициализации структур и массивов под названием designated initializers (я предпочитаю переводить это как «выборочная инициализация»). Суть в том, что при инициализации переменной агрегатного типа можно явно указать, какие части структуры данных мы инициализируем, а какие — предоставляем обнулить компилятору. Звучит немного заумно, поэтому сразу приведу пару простых примеров.

Выборочная инициализация массива (работает в C, но не в C++!):

int a[] = {
    [2] = 2,
    [7] = 7
};

Выборочная инициализация структуры:

struct {
    int a;
    int b;
    int c;
} s = { .a = 1, .c = 2 };

Нетрудно догадаться, что здесь происходит. В первом случае мы получаем массив из 8 элементов, два из которых инициализированы, а остальным присваивается 0. Со структурой еще проще: поля a и c получают значения, а поле b обнуляется. Рассмотрим примеры посложнее.

struct {
    int a;
    struct {
        char c;
        int  d;
    } b;
    int c;
} s = {
    .a = 2,
    .b.d = 3
};
static short grid[3] [4] = { [0][0]=8, [0][1]=6,
                             [0][2]=4, [0][3]=1,
                             [2][0]=9, [2][1]=3,
                             [2][2]=1, [2][3]=1 };
int a[] = {2, 4, [8]=9, 10}

Если первые два случая еще как-то интуитивно понятны, то последний поначалу заставляет задуматься. Здесь действует простое правило, такое же, как и при объявлении enum. По умолчанию индексы в инициализаторе начинаются с 0 и с каждым новым значением увеличиваются на единицу. Но если указать индекс явно, он становится новым значением по умолчанию. Приведенный выше пример раскрывается в следующее:

int a[] = {[0]=2, [1]=4, [8]=9, [9]=10}

Таким образом, получим массив из 10 элементов.

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

int a[] = {2, [3] = 3, [2] = 2, 5};

Могу сказать сразу, что синтаксически конструкция безупречна.

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

#define SIGNAME(s) [s] = #s
const char * const sigNames[] = {
    SIGNAME(SIGHUP),
    SIGNAME(SIGINT),
    SIGNAME(SIGQUIT),
    /* ... */
};
#undef SIGNAME

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

УжасноПлохоНормальноХорошоОтлично (4 голосов, средний: 5,00 из 5)
Loading ... Loading ...

5 комментариев

Оставьте свой отзыв

Или введите OpenId:

XHTML: Можно использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">