3

I'm just curious as to why there are no brackets at the end of the ISR when attaching and assigning the interrupt command?

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

In, attachInterrupt, I would have expected "blink" to be written as "blink()", with the brackets, not just the name of the function, sort of like how you would normally call the function blink() from your loop or setup functions (or elsewhere). So, what's going on here, and are there other scenarios where you might use a function without the brackets after it?

2
  • 2
    Who on earth would want to close this? It is a good question.
    – Nick Gammon
    Jul 16, 2021 at 7:58
  • Moderators can see the users who voted to close this question.
    – VE7JRO
    Jul 21, 2021 at 21:13

1 Answer 1

6

It is right, that you are putting brackets after the function name if you are calling it. But with attachInterrupt() you just want to give it the information, which function to call in case of an interrupt. With the brackets the compiler would think you want the function to be executed and the result/return value being given as parameter to attachInterrupt(). But the execution here should only happen in case of an interrupt. The attachInterrupt() function/the corresponding ISR need to know, what to call in case of an interrupt. Just the information/address of the function.

Maybe you have already heard of pointers. That are variables that hold the address of a different variable. They are pointing somewhere else in the memory. blink (without the brackets) is basically a function pointer. Leaving the brackets away tells the compiler, that you want the address of the function, not executing it. You are providing the function parameter by reference this way, providing the address, not the value.

If you like/need you can use that principle yourself by defining your own function pointer variable. For example this

void (*myCallbackFunction)(int);

which is a function, that takes an int as parameter and returns nothing (void). You can then set the function pointer and call it as a normal function

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);
}

We are calling myCallbackFunction(), but it is a function pointer, that points to blinkXTimes(), so that will be executed.

So, what's going on here, and are there other scenarios where you might use a function without the brackets after it?

The above principle is super useful for writing libraries with user callback functions. Another example (besides attachInterrupt()) is the Wire library. There the receive and request interrupts need to interact with the library user to deliver to or request data from him. For this the Wire library lets you set your own callback functions. Refer to the examples of that library.

Another application would be building a Finite State Machine with a function pointer (basically calling different functions on each loop iteration depending on the state, which is represented by the function pointer).

That are the two big application I know of. The first one is more common. Though function pointers are just a tool. They can be used for many things.

3
  • 1
    This was phenomenally helpful!!!
    – Diesel
    Jul 15, 2021 at 20:59
  • Just nit-picking, but giving a pointer as an argument for a pointer parameter is still passing by value. In C++ there is just one way to pass by reference: If you have a reference parameter like void f(TYPE & p);. Jul 16, 2021 at 5:54
  • Unfortunately a lot of these terms are overloaded. The C standard used the term "referenced type" for T in the T * construction, but C++ doesn't use this term there. However, the application of unary * operator to a pointer is still literally called "dereferencing" a pointer in C++. Admittedly, that terminology for use as passing-by-way-of-pointer-value is more confusing in C++ than it was/(still is) in C. But, it is commonly used and understood in context, even by C++ programmers, if otherwise imprecise. For anyone that hasn't encountered this usage yet, well, now you have.
    – timemage
    Jul 17, 2021 at 16:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.