Локальные метки
__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 хорошо делать для исполнения "шитого кода".
Отправить комментарий