Дизайн кодування C - функціональні покажчики?


24

У мене є PIC18F46K22 і програмую його за допомогою компілятора XC8. Зрештою, у мене буде така система, як ПК із stdinта stdout. Отже, в основному циклі буде функція, яка перевіряє, чи є новий вхід. Якщо є вхід, функція буде викликана відповідно. Так, наприклад, коли я ввожу A на stdin, PIC буде виконувати функцію, на зразок якої 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 . Мені також потрібен a, function_Aякий змінює function_addrвказівник на in_function_A.

Примітка: коли функцію файлової системи слід закрити, is_function_Aслід просто змінити значення function_addrна 0.

Тому в основному моє питання - як я можу

  • Отримати адресу функції (і зберегти її у змінній)
  • Викличте функцію за вказаною адресою

6
Не по темі. Належить на сайті stackoverflow.com.
користувач207421

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

9
@EJP Я не згоден. Тільки те, що є перекриття, не означає, що воно повинно бути на тому чи іншому сайті. Це задає питання, пов'язані з розробкою та програмуванням вбудованих систем низького рівня, що видається на тему в будь-якому місці.
Кортук

Машини стану можуть базуватися на непрямості функції: виклик функції переходу стану повертає нову функцію переходу стану.
Каз

1
Дозволяє мати цю дискусію тут .

Відповіді:


21

Функція

int f(int x){ .. }

Отримати адресу функції (і зберегти її у змінній)

int (*fp)(int) = f;

Викличте функцію за вказаною адресою

int x = (*fp)( 12 );

3
+1, очевидний у світі C та ASM, але не такий очевидний для тих, хто розпочав роботу з мовами OO.
Анніндо Гош

4
Виклик fp також може бути записаний як 'int x = fp (12);' для підвищення читабельності.
Rev1.0

1
Маю визнати, що, працюючи в основному на C # та Java, я іноді пропускаю ті старі хороші покажчики функцій від C. Вони небезпечні, коли не виконуються правильно, і більшість завдань можна виконати іншими способами, але вони впевнені, що вони корисні в ті кілька разів, коли вони дійсно знадобляться.
CVn

@ MichaelKjörling: Так правда. Я постійно перемикаюся між вбудованими програмами на C і C # (навіть впроваджую аналогічний код для зв'язку між пристроєм та ПК) і настільки ж сильним, як це може бути C #, мені все ще дуже подобається C.
Rev1.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.
Rev1.0

4
@ MichaelKjörling добре, але якщо fpце "глобальна змінна" (як int він код вище), то вона гарантовано буде ініціалізована NULL(без цього явно робити це).
Алок
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.