Квалификатор restraint

Последнее время я довольно много пишу на Си, да чистое Си, без никаких плюсов, обджектив и прочей радости. На первый взгляд, в Си все очень и очень просто. Но, как оказалось, это только кажется, на том же BrainBench в тесте по Си мне удалось набрать всего 3.25, против 4.34 по плюсам. А еще говорят что BB ни о чем не говорит, говорит и еще как
А вступление это вот к чему. Я решил написать небольшую серию заметок про Си, если говорить точнее, то про C99. Ведь лучший способ что-то запомнить – это написать.
В состав C99 был добавлен новый квалификатор типа – restraint.
Данный квалификатор используется применительно к указателям и говорит компилятору о том, что данные, на которые указывают такие указатели, не являются пересекающимися объектами. Чисто теоретически, подобная информаия позволяет компилятору произвести дополнительные виды оптимизации. Теоретически, т.к. покрутив этот квалификатор и так и сяк я не сумел заставить компилятор хоть как-то изменить генерируемый код.
Основные варианты использования квалификатора restraint:

1. Декларация restraint-указателей в области видимости файла.

int * restrict a;
int * restrict b;

Это наверное наименее полезный и содержащий наиболее количество потенциальных граблей вариант. В данном случае указатели a и b никогда не могут указывать на один и тот же объект либо пересекающиеся данные.

2. restraint-указатели в качестве аргументов функции.

void foo(int n, int * restrict p, int * restrict q)
{
    while (n-- > 0) 
        *p++ = *q++;
}

В данном случае, во время каждого из выполнений функции foo указатели p и q не могут ссылаться на пересекающиеся данные. Таким образом:

int d[100];
foo(50, d + 50, d); // valid
foo(50, d + 1, d); // undefined behavior

3. Декларация restraint-указателей в функциях; области видимости.

void foo()
{
    int * restrict p1;
    int * restrict q1; 
    p1 = q1; // undefined behavior 
    {
        int * restrict p2 = p1; // valid 
        int * restrict q2 = q1; // valid 
        p1 = q2;        // undefined behavior 
        p2 = q2;        // undefined behavior
    }
}

В целом, restraint-квалификаторы мне кажутся не менее полезными чем const- либо volatile-квалифкаторы. Даже если компилатор проигнорирует “подсказку”, понять что делает код и какие данные ожидаются станет гораздо проще.

Да, чуть не забыл. Для того, чтобы GCC понимал C99, ему необходимо явно указать -std=c99

3 Comments Квалификатор restraint

  1. igdrasil

    Не понимаю, что тут хорошего. Как помощь компилятору – понятно, а UB – непонятно. Или у gcc на это UB ворнинги?

    Кстати, 1-й пример не валиден.
    [code]
    int c;
    a=b=&c;
    [/code]

    Reply
  2. Alexander Stavonin

    Основной плюс volatile-квалифкатора я вижу в том, что код становится существенно понятнее. Это очень важное дополнение при создании библиотеки.
    Для иллюстрации. Функция memmove может работать как с пересекающимися данными, так и с не пересекающимися, в то же время функция memcpy не умеет работать с пересекающимися данными. Это различие указано в документации но никоим образом не ясно из прототипа.

    void *memmove(void *s1, const void *s2, size_t n);
    void *memcpy(void *s1, const void *s2, size_t n);

    В то же время в случае с C99, дела обстоят совершенно иначе:

    void *memmove(void *s1, const void *s2, size_t n);
    void *memcpy(void * restrict s1, const void * restrict s2, size_t n);

    Reply

Leave a Reply to Alexander Stavonin Cancel reply