четверг, 22 января 2009 г.

Расширения GCC

Довольно интересная глава в книжке Red Hat Enterprise Linux 4: Using the GNU Compiler Collection (GCC). Большинство вещей широко известны, но некоторые из них показались мне очень любопытными, чтобы о них написать.

Локальные метки
__label__ label;
или
__label__ label1, label2, /* … */;
Метка становится локальной для того блока, в котором она объявлена, например, из ядра:

#define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; })

Здесь мы видим, кстати, еще один интересный трюк.

Переопределяемые метки
Можно взять адрес метки с помощью оператора &&. Тип результата будет void*. Значение является константным и его можно использовать везде, где допускается константа этого типа. Один из примеров был приведен выше. Другой пример -- использование с goto, например:

void *ptr;
/* … */
ptr = &&foo;
goto *ptr;

Или еще хуже:

static void *array[] = { &&foo, &&bar, &&hack };
goto *array[i];

Нас предупреждают, что переходы между метками разных функций непредсказуемы.
Следующий пример, иллюстрирует адресную арифметику.

static const int array[] = { &&foo - &&foo, &&bar - &&foo,
&&hack - &&foo };
goto *(&&foo + array[i]);

Вложенные функции
Оказывается, gcc позволяет определять вложенные функции. Например:
hack (int *array, int size)
{
void store (int index, int value)
{ array[index] = value; }

intermediate (store, size);
}

Массивы с нулевым размером
Широко используется в качестве последнего элемента структуры (размер которой может варьироваться). Например:

struct line {
int length;
char contents[0];
};
struct line *thisline = (struct line *)
malloc (sizeof (struct line) + this_length);
thisline->length = this_length;

Важным является то, что в стандарте ISO C90 приходится использовать размер массива равным 1, что затрудняет вычисления правильного размера структура, а в стандарте ISO C99 нужно использовать массив с неопределенным числом элементов ([]).

Автоматические массивы с переменным размером
Пример.

FILE *
concat_fopen (char *s1, char *s2, char *mode)
{
char str[strlen (s1) + strlen (s2) + 1];
strcpy (str, s1);
strcat (str, s2);
return fopen (str, mode);
}

Или,даже, так:

struct entry
tester (int len, char data[len][len])
{
/* … */
}

Макросы с переменным числом параметров
По стандарту ISO C99:

#define debug(format, ...) fprintf (stderr, format, __VA_ARGS__)

Другой вариант, давно поддерживаемый gcc:

#define debug(format, args...) fprintf (stderr, format, args)

Правда, по стандарту C нельзя опустить третий параметр, и, например запись: debug ("A message") -- будет ошибочной. Чтобы решить эту проблему, можно использовать ##:

#define debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__)

Инициализаторы
Инициализация массивов по ISO C99 (GCC позволяет делать так и в C89)

int a[6] = { [4] = 29, [2] = 15 };

Синоним:

int a[6] = { 0, 0, 15, 0, 29, 0 };

Еще одно расширение GNU:

int widths[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 };

Почти как meta tables в Lua. :)

Диапазоны в case
А это вообще убойная вещь, например:

case 'A' ... 'Z':
case 1 ... 5:

Пробелы вокруг точек обязательны.

Если эти примеры показались для вас интересными, рекомендую прочитать всю главу.

1 комментарий:

Анонимный комментирует...

Вычисляемые goto хорошо делать для исполнения "шитого кода".

Архив блога