Почему нет скобок после процедуры прерывания внутри attachInterrupt

Мне просто любопытно, почему в конце ISR нет скобок при подключении и назначении команды прерывания?

 attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE);
 ...
 void blink() {
    state = !state;
 }

В, attachInterrupt я ожидал, что "мигание" будет записано как "мигание()", в скобках, а не только имя функции, вроде того, как вы обычно вызываете функцию blink() из своего цикла или функций настройки (или в другом месте). Итак, что здесь происходит и существуют ли другие сценарии, в которых вы могли бы использовать функцию без скобок после нее?

, 👍3

Обсуждение

Кто, черт возьми, хотел бы это закрыть? Это хороший вопрос., @Nick Gammon

Модераторы могут видеть пользователей, проголосовавших за закрытие этого вопроса., @VE7JRO


1 ответ


Лучший ответ:

6

Правильно, что вы ставите квадратные скобки после имени функции, если вы ее вызываете. Но с помощью attachInterrupt() вы просто хотите сообщить ему, какую функцию вызывать в случае прерывания. С помощью скобок компилятор подумает, что вы хотите, чтобы функция была выполнена, а значение результата/возврата было указано в качестве параметра для attachInterrupt(). Но выполнение здесь должно происходить только в случае прерывания. Функции attachInterrupt ()/соответствующему ISR нужно знать, что вызывать в случае прерывания. Просто информация/адрес функции.

Может быть, вы уже слышали об указателях. Это переменные, которые содержат адрес другой переменной. Они указывают куда-то еще в памяти. мигание (без скобок) - это в основном указатель на функцию. Если оставить скобки в стороне, компилятор сообщит, что вам нужен адрес функции, а не ее выполнение. Таким образом, вы предоставляете параметр функции по ссылке, указывая адрес, а не значение.

Если вам нравится/нужно, вы можете использовать этот принцип самостоятельно, определив собственную переменную указателя функции. Например, это

void (*myCallbackFunction)(int);

которая является функцией, которая принимает значение int в качестве параметра и ничего не возвращает (void). Затем вы можете установить указатель на функцию и вызвать ее как обычную функцию

void blinkXTimes(int times){
    for(int i=0;i<times;i++){
        digitalWrite(pin, HIGH);
        delay(500);
        digitalWrite(pin, LOW);
        delay(500);
    }
}

void setup(){
    pinMode(pin, OUTPUT);
    myCallbackFunction = blinkXTimes;

void loop(){
    myCallbackFunction(3);
    delay(5000);
}

Мы вызываем myCallbackFunction(), но это указатель на функцию, который указывает на blinkXTimes(), поэтому он будет выполнен.

Итак, что здесь происходит и существуют ли другие сценарии, в которых вы могли бы использовать функцию без скобок после нее?

Приведенный выше принцип очень полезен для написания библиотек с функциями обратного вызова пользователей. Другим примером (помимо attachInterrupt()) является библиотека проводов. Там прерывания приема и запроса должны взаимодействовать с пользователем библиотеки для доставки или запроса данных от него. Для этого библиотека Wire позволяет вам устанавливать собственные функции обратного вызова. Обратитесь к примерам этой библиотеки.

Другим приложением было бы создание конечного автомата с указателем функции (в основном вызывающего различные функции на каждой итерации цикла в зависимости от состояния, которое представлено указателем функции).

Это два больших приложения, о которых я знаю. Первый из них встречается чаще. Хотя указатели на функции - это всего лишь инструмент. Их можно использовать для многих целей.

,

Это было феноменально полезно!!!, @Diesel

Просто придираюсь к мелочам, но указание указателя в качестве аргумента для параметра указателя все равно передается по значению. В C++ есть только один способ передачи по ссылке: если у вас есть ссылочный параметр, например " void f(ТИП & p);"., @the busybee

К сожалению, многие из этих терминов перегружены. Стандарт C использовал термин "ссылочный тип" для " T "в конструкции" T*", но в C++ этот термин там не используется. Однако применение унарного оператора * к указателю по-прежнему буквально называется "де*ссылкой*" на указатель в C++. По общему признанию, эта терминология для использования в качестве передающего значения указателя более запутана в C++, чем это было/(все еще есть) в C. Но она обычно используется и понимается в контексте даже программистами C++, если в противном случае неточна. Для тех, кто еще не сталкивался с этим использованием, что ж, теперь у вас есть., @timemage