C дизайн кода - функция указателей?


24

У меня есть PIC18F46K22 и я программирую его с помощью компилятора XC8. В конце концов, у меня будет система вроде ПК с stdinи stdout. Таким образом, в главном цикле будет функция, которая проверяет, есть ли новый ввод. Если есть ввод, функция будет вызываться соответственно. Так, например , когда я ввод Л на stdin, ПИК будет работать как функцию function_Aвместо того , function_Bкоторый вызывается , когда я ввод в B.

Когда PIC сделан с функцией, я хочу, чтобы новый вход был отправлен в функцию. Таким образом, при нажатии A открывается передатчик RS232, с этого момента каждый вход будет отправляться через RS232. В итоге проект представляет собой самостоятельный текстовый редактор. Таким образом, при нажатии A открывается файловая система, с этого момента вы больше не редактируете текст, а просматриваете список файлов. Это означает, что нажатие клавиш «Вверх» и «Вниз» означает нечто иное, чем в среде редактирования текста.

Я много думал о том, как запрограммировать это на C. Я думал об этом вчера вечером и хотел бы знать, возможно ли это, и если да, то как. Что я хочу сделать, это:

  • mainФункция вызывает функцию какfunction_A
  • function_Aизменяет глобальную переменную function_addrна адресный указатель функцииin_function_A
  • С этого момента mainвызывается функция function_addrпри появлении нового ввода.

Так что мне нужна mainфункция, которая проверяет, function_addrравен ли ноль. Если это так, должна вызываться «нормальная» функция, например function_A. Если это не так, function_addrдолжна вызываться функция at . Мне также нужно, function_Aкоторый меняет function_addrуказатель на in_function_A.

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

Так что в основном мой вопрос, как я могу

  • Получить адрес функции (и сохранить его в переменной)
  • Вызов функции по указанному адресу

6
Не по теме. Принадлежит на stackoverflow.com.
user207421

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

9
@ EJP Я не согласен. То, что есть совпадение, не означает, что оно должно быть на одном сайте или другом. Он задает вопросы, связанные с проектированием и программированием низкоуровневых встраиваемых систем, что кажется темой в любом месте.
Кортук

Конечные автоматы могут быть основаны на косвенности функций: вызов функции перехода состояний возвращает новую функцию перехода состояний.
Каз

1
Давайте обсудим это здесь .

Ответы:


21

Функция

int f(int x){ .. }

Получить адрес функции (и сохранить его в переменной)

int (*fp)(int) = f;

Вызов функции по указанному адресу

int x = (*fp)( 12 );

3
+1, очевидно в мире C и ASM, но не так очевидно для тех, кто начинал с ОО-языков.
Аниндо Гош

4
Вызов fp также может быть записан как 'int x = fp (12);' повысить читаемость.
Откр. 1

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

@ MichaelKjörling: так правда. Я постоянно переключаюсь между встроенным программированием на C и C # (даже реализуя подобный код для связи между устройством и ПК), и, насколько могущественным, как C #, я все еще наслаждаюсь C.
Rev.1.0

2
fp(12)не повышает читаемость (*fp)(12). У них разная читаемость. (*fp)(12)напоминает читателю, что fpявляется указателем, а также напоминает объявление fp. На мой взгляд, этот стиль связан со старыми источниками, такими как внутренние компоненты X11 и K & R C. Одна из возможных причин, по которой он может быть связан с K & R C, заключается в том, что в ANSI C можно объявить fpс использованием синтаксиса функции, если fpэто параметр: int func(int fp(double)) {}, В K & R C это было бы int func(fp) int (*fp)(double); { ... }.
Каз

17

Хотя ответ Ваутерса абсолютно верный, это может быть более дружественным для новичка и лучшим примером относительно вашего вопроса.

int (*fp)(int) = NULL;

int FunctionA(int x){ 
    return x + x;
}

int FunctionB(int x){ 
    return x * x;
}

void Example(void) {
    int x = 0;

    fp = FunctionA;
    x = fp(3);  /* after this, x == 6 */

    fp = FunctionB;
    x = fp(3);  /* after this, x == 9 */
}

Если не очевидно, что указателю функции действительно назначен адрес целевой функции, целесообразно выполнить проверку NULL.

if (fp != NULL)
    fp(); /* with parameters, if applicable */

8
+1 для проверки на ноль, но учтите, что компилятор может не гарантировать, что значение по адресу в ОЗУ, которое, как оказалось, fpзанимает, изначально равно NULL. Я бы сделал int (*fp)(int) = NULL;как объединенную декларацию и инициализацию, чтобы быть уверенным - вы не хотите переходить к какой-то случайной ячейке памяти из-за некоторого тупого значения, которое только что произошло. Затем просто назначьте, fpкак в вашей Example()функции.
CVn

1
Спасибо за комментарий, я добавил инициализацию NULL.
Rev.1.0

4
@ MichaelKjörling хорошая точка зрения, но если fpэто «глобальная переменная» (как в коде выше), то она гарантированно будет инициализирована NULL(без необходимости явно).
Alok
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.