2010-05-26 2 views
1

Это мой первый пост, поэтому, пожалуйста, будьте осторожны.Объявление, распределение и назначение массива указателей на указатели функций

Я играл с C время от времени в прошлом. Теперь я дошел до того, что начал реальный проект (2D-графический движок с использованием SDL, но это не имеет значения для вопроса), чтобы иметь возможность сказать, что у меня есть реальный опыт C. Вчера, работая над системой событий, я столкнулся с проблемой, которую я не мог решить.

Там эта ЬурейеЕ,


//the void parameter is really an SDL_Event*. 
//but that is irrelevant for this question. 
typedef void (*event_callback)(void); 

который определяет сигнатуру функции должны быть вызваны в событиях двигателя.

Я хочу иметь возможность поддерживать несколько event_callbacks, поэтому массив этих обратных вызовов будет идеей, но не хочу ограничивать количество обратных вызовов, поэтому мне нужно какое-то динамическое распределение. Именно здесь возникла проблема. Моя первая попытка прошла так:


//initial size of callback vector 
static const int initial_vecsize = 32; 
//our event callback vector 
static event_callback* vec = 0; 
//size 
static unsigned int vecsize = 0; 

void register_event_callback(event_callback func) { 
    if (!vec) 
     __engine_allocate_vec(vec); 
    vec[vecsize++] = func; //error here! 
} 

static void __engine_allocate_vec(engine_callback* vec) { 
    vec = (engine_callback*) malloc(sizeof(engine_callback*) * initial_vecsize); 
} 

Прежде всего, я опустил некоторые проверки ошибок, а также код, который перераспределяет вектор обратного вызова, когда число обратных вызовов превышает вектор размера.

Однако, когда я запускаю этот код, программа вылетает, как описано в коде. Я предполагаю ошибку сегментации, но я не могу быть уверен, так как никакой вывод не задан. Я также предполагаю, что ошибка возникает из-за некорректного понимания того, как объявлять и выделять массив указателей на указатели на функции.

Пожалуйста, переполнение стека, направляйте меня.

EDIT: Я не могу понять, как отступать от блоков кода. Это почти немного смущает ...

EDIT: Не обращайте на это внимания. Проверял источник страницы некоторых других сообщений =).

+0

Вы должны выравнивал код на 4 пробела для того, чтобы быть правильно отформатирована. – Artefacto

+1

Используйте кнопку '101010' на странице редактирования, чтобы пометить текст в код. – sbi

+1

Вам нужно двойное косвенное обращение в __engine_allocate_vec – Artefacto

ответ

1

На линии:

vec[vecsize++] = func; //error here! 

Что произойдет, если vecsize является >= initial_vecsize?

Также __engine_allocate_ve не работает, так как он только изменяет локальную копию vec, вы должны изменить подпись к ** и передать спор с &.

static void __engine_allocate_vec(engine_callback** vec)

__engine_allocate_vec(&vec);

+0

Спасибо! Я считаю, что вы были первыми. Иногда меня путают с пропуском и пропуском. Не могли бы вы объяснить, почему недостаточно указывать указатель на указатель на функцию? – manneorama

+0

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

3

функция распределения должна быть:

static void __engine_allocate_vec(engine_callback** vec) { 
    *vec = malloc(sizeof(engine_callback) * initial_vecsize); 
} 

, а затем:

if (!vec) 
    __engine_allocate_vec(&vec); 

Обратите внимание, что несоответствие типа указателя в исходном выделении функции будет иметь были пойманы, если вы опустил бросок. также не используйте имена, содержащие двойные символы подчеркивания в вашем коде, - они предназначены для использования в реализации.

0

Вы, кажется, malloc -ную на основе sizeof(engine_callback*), а не sizeof(engine_callback) ...

0

Ваша __engine_allocate_vec функция создает пространство для новых engine_callback с, но это ничего полезного с этим указателем не делать. (Он меняет свою локальную версию vec, которая была передана по значению, поэтому изменения не возвращаются к вызывающему. И имя аргумента скрывает имя глобального, поэтому оно не устанавливается.) Поэтому, когда он возвращается , ваш указатель все еще равен нулю.

попробовать что-то вроде этого ...

static void __engine_allocate_vec(engine_callback** vec) { 
    *vec = (engine_callback*) malloc(sizeof(engine_callback) * initial_vecsize); 
} 

Тогда в register_event_callback, передать &vec функции вместо vec.

Или сделайте функцию void и дайте ей установить глобальное значение. Нет, забудь, я сказал это.

0

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

Все остальные указали правильный путь сначала выделить вектор:

static void engine_allocate_vec(event_callback **vec) 
{ 
    *vec = malloc(sizeof **vec * initial_vecsize); 
} 

Обратите внимание, пару вещей. Прежде всего, я не делаю результат malloc. Это необязательно (как и на C89, в любом случае, более ранние версии C требуют отливки, как и C++), и это может потенциально подавить диагностику компилятора, если вы забудете включить stdlib.h или иным образом не имеете прототипа для malloc в объем. Во-вторых, я вызываю sizeof на выражение **vec, а не тип; поскольку тип выражения **vec равен event_callback, sizeof **vec возвращает тот же результат, что и sizeof (event_callback). Это помогает сократить видимый беспорядок, и это позволяет избежать некоторой общей ошибки, которая ползет, когда кто-то изменяет тип переменной, но не переносит это изменение до выражения sizeof в вызове malloc, например

double *f; /* was originally declared as float, later changed to double */ 
... 
f = malloc(sizeof (float) * size); /* type change not carried through */ 

Отметьте, что sizeof не оценивает его операнд (если это не VLA), поэтому вам не нужно беспокоиться о том, чтобы называть его неинициализированным выражением указателя.

Это поможет вам создать начальный вектор. Тем не менее, вы хотели бы иметь возможность расширять вектор по мере необходимости при регистрации более чем initial_vecsize обратных вызовов, не так ли? Если да, то позвольте мне предложить следующее:

static int engine_allocate_vec(event_callback **vec, 
    size_t *currentSize, 
    size_t extent) 
{ 
    int success = 0; 
    /** 
    * Assign the result of realloc to a temporary; realloc returns NULL 
    * on failure, and we don't want to risk losing our pointer to the 
    * previously allocated memory. Similarly, we don't update *currentSize 
    * unless the call succeeds. Note that realloc(NULL, size) is equivalent 
    * to malloc(size). 
    */ 
    event_callback *tmp = realloc(*vec, sizeof *tmp * (*currentSize + extent)); 
    if (tmp != NULL) 
    { 
    *vec = tmp; 
    *currentSize += extent; 
    success = 1; 
    } 
    return success; 
} 

Тогда ваша функция регистра становится:

/** 
* Adding vector_count variable to keep track of the number 
* of items in the vector as opposed to the physical size 
* of the vector. 
*/ 
static size_t vector_count = 0; 

void register_callback_event(event_callback func) 
{ 
    if (!vec) 
    { 
    /** 
    * Initial vector allocation 
    */ 
    if (!engine_allocate_vec(&vec, &vecsize, initial_vecsize)) 
    { 
     /* allocation failed; treating this as a fatal error */ 
     exit(0); 
    } 
    } 
    else if (vector_count == vecsize) 
    { 
    /** 
    * Need to extend the vector to accomodate 
    * the new callback. Double the vector size (i.e., 
    * extend it by the current vector size) 
    */ 
    if (!engine_allocate_vec(&vec, &vecsize, vecsize)) 
    { 
     /* extension failed - treating this as a fatal error*/ 
     free(vec); 
     exit(0); 
    } 
    } 

    vec[vector_count++] = func; 
} 
+0

Благодарим вас за sizeof-tip! Не думал об этом. Однако перераспределение памяти и проверка ошибок уже были. Я оставил их для краткости. – manneorama