Расширения GCC: Как использовать метки в качестве значений




Сайт создан в системе uCoz
К оглавлению

 

Автор: Andrey Rjavskov(Rzhavskov) as rjaan <rjaan@yandex.ru>


1.МЕТОД ИСПОЛЬЗОВАНИЯ

Для получения адреса метки, заданной в текущей позиции(или вернее содержащейся внутри функции), применяется унарный оператор `&&', а значение передаваемое по этому адресу имеет тип 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). Для повышения быстроты диспетчеризации, в пределах интерпретируемой функции-потока, метки могут быть сохранены в её коде.

Когда Вы использует этот метод для перехода по коду в различных функциях помните, что способ избежать всяких несуразностей – сохранение адреса значения метки только в автоматически создаваемых переменных и ни в коим случае не передавать значение этих меток в качестве аргумента, вызваемой функции.

2.ПРИМЕР ИСПОЛЬЗОВАНИЯ

/* 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 \nfailure.\n");

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*/

3.СБОРКА ПРИМЕРА

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!"

4.РЕЗУЛЬТАТ ВЫПОЛНЕНИЯ

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.

5.БИБЛИОГРАФИЯ

  1. Extensions to the C Language Family: Labels as Values
  2. Common Predefined Macros
  3. Исходный код проекта PROCPS
  4. Функция bsearch

 

К оглавлению


Copyright © 2010 rjaan as Andrey Rjavskov(Rzhavskov) <rjaan@yandex.ru> <arjavskov@gmail.com>