Расширения GCC: Как использовать метки в качестве значений
К оглавлению
|
||
Автор: Andrey Rjavskov(Rzhavskov) as rjaan <rjaan@yandex.ru> Для получения адреса метки, заданной в текущей позиции(или вернее содержащейся
внутри функции), применяется унарный оператор `&&', а значение передаваемое по этому
адресу имеет тип void *. Оно является константой и может быть использовано везде,
в пределах области определения функции. Для примера: void* foo_func( const char *name ) { void *ptr; ... ptr = &&foo_Case; /* Для перехода по значению foo_Case, применятеся оператор goto со следующем выражением: goto *exp */ goto *ptr; ... foo_Case: ... return NULL;
Самым распространенным способом использования константных значений является
инициализацией статического массива, который будет работать как
таблица переходов(jump table): static void *array[] = { &&foo_Case, &&bar_Case, &&hack_Case }; После чего можно, выбирать метку, используя индиксацию массива. goto *array[i];
Помните, что в языке программирования Си, никогда не проверяются границы
индиксируемого массива.
Другое использование таблицы переходов со значениями меток является интерпретация
кода потоков(threaded code). Для повышения быстроты диспетчеризации, в пределах
интерпретируемой функции-потока, метки могут быть сохранены в её коде.
Когда Вы использует этот метод для перехода по коду в различных функциях помните,
что способ избежать всяких несуразностей – сохранение адреса значения метки
только в автоматически создаваемых переменных и ни в коим случае не передавать
значение этих меток в качестве аргумента, вызваемой функции. /* The file jump_table_test.c * * build: gcc jump_table_test.c -o jump_table_test */ #include <stdio.h> #include <stdlib.h> #include <string.h> /* Проверка на предопределения макроса __GNUC__ */ #ifndef __GNUC__ #error "It's nonsense!The macros __GNUC__ is not predefined." #else #warning "The macros __GNUC__ is always predefined by the GNU CC preprocessor!" #endif #define NKEYS 5 #define KEY_ONE "key1" #define KEY_TWO "key2" #define KEY_THREE "key3" #define KEY_FOUR "key4" #define KEY_NOTFOUND "Key not found" typedef struct jump_table_struct { const char *name; /* имя длинной опции командной строки */ const void *jump; /* Указатель на константу -- метки перехода */ } jump_table_struct_t; /* Функция bsearch() будет вызывать по указателю compare * функцию compare_jump_table_structs() для сравнения каждого элемента массива, * передаваемого по указателю ch_p с ключом-значением по указателю array_p */ static int compare_jump_table_structs(const void *ch_p, const void *array_p)
{
return strcmp(((const jump_table_struct_t*)ch_p)->name,
((const jump_table_struct_t*)array_p)->name);
}
static const char *gnucc_ext_labels_as_values_test( const char buf[] ) { jump_table_struct_t *found; /* текущая метка перехода*/ /* Таблица перехода */ static const jump_table_struct_t jump_table[] = { { KEY_ONE, &&case_Key1 }, /* Ключ 1 */ { KEY_TWO, &&case_Key2 }, /* Ключ 2 */ { KEY_THREE, &&case_Key3 }, /* Ключ 3 */ { KEY_FOUR, &&case_Key4 }, /* Ключ 4 */ }; /* Рассчет количества меток перехода */ const int jump_table_count = sizeof(jump_table)/sizeof(jump_table_struct_t); jump_table_struct_t findme = { buf, NULL }; /* Поиск метки перехода */ found = bsearch(&findme, jump_table, jump_table_count, sizeof(jump_table_struct_t), compare_jump_table_structs ); if(!found) return KEY_NOTFOUND; /* Переход по адресу значения метки, если она была найдена * по ключу, переданного в переменной buf[] данной функции */ goto *(found->jump);
case_Key1: return found->name; /* Ключ, переданный по указателю key_p, соответсвует "Key1" */
case_Key2: return found->name; /* Ключ, переданный по указателю key_p, соответсвует "Key2" */ case_Key3: return found->name;/* Ключ, переданный по указателю key_p, соответсвует "Key3" */ case_Key4: return found->name;/* Ключ, переданный по указателю key_p, соответсвует "Key4" */ } int main( int argc, char **argv ) {. char *result = NULL; const char *key_p = *(argv+1); fprintf(stdout,"The programm jump_table_test, version 0.1\n" ); fprintf(stdout,"This test was written by Andrey Rjavskov(Rzhavskov)\n" ); if( argc != 2 ) { fprintf (stderr,"Usage: jump_table_test fflush(stderr); return EXIT_FAILURE; } result=(char*)gnucc_ext_labels_as_values_test(key_p); return fprintf(stdout, "You have entered next key: %s\n%s.\n", result, !strcmp(result,KEY_NOTFOUND) ? "failure" : "done" ); }
/*eof*/ user@home:~/Projects/tests$ gcc jump_table_test.c -o jump_table_test jump_table_test.c:25:2: warning: #warning "The macros __GNUC__ is always predefined by the GNU CC preprocessor!" user@home:~/Projects/tests$ ./jump_table_test key12 The programm jump_table_test, version 0.1 This test was written by Andrey Rjavskov(Rzhavskov) You have entered next key: Key not found failure. user@home:~/Projects/tests$ ./jump_table_test key1 The programm jump_table_test, version 0.1 This test was written by Andrey Rjavskov(Rzhavskov) You have entered next key: key1 done.
| ||
Copyright © 2010 rjaan as Andrey Rjavskov(Rzhavskov) <rjaan@yandex.ru> <arjavskov@gmail.com> |