25
\$\begingroup\$

I have a PIC18F46K22 and program it with the XC8 compiler. In the end, I'll have a system like a pc with stdin and stdout. So in the main loop there will be a function which is checking if there is new input. If there's input, a function will be called accordingly. So for example when I input an A on stdin, the PIC will run a function like function_A instead of function_B which is called when I input a B.

When the PIC is done with the function, I want that the new input is sent to the function. So when pressing A opens the RS232 transmitter, from that moment every input will be sent over RS232. In the end the project is a stand-alone text editor. So when pressing A opens the file system, from that moment you aren't text editing anymore but looking through a list of files. That means that pressing Up and Down means something different than in the text-editing environment.

I've done a lot thinking on how to program this in C. I thought this up last night and would like to know if it's possible and if so, how. What I want to do is:

  • The main function calls a function like function_A
  • function_A changes a global variable function_addr to the address pointer of function in_function_A
  • From that moment, main calls the function at function_addr when there is new input.

So what I'd need is a main function which checks if function_addr is zero. If it is, a 'normal' function should be called, like function_A. If it isn't, the function at function_addr should be called. I also need a function_A which changes the function_addr to a pointer to in_function_A.

Note: when the filesystem function should be closed, is_function_A should just change function_addr to 0.

So basically my question is how can I

  • Get the address of a function (and store it in a variable)
  • Call a function at a specified address
\$\endgroup\$
7
  • 6
    \$\begingroup\$ Off topic. Belongs on stackoverflow.com. \$\endgroup\$
    – user207421
    Commented Jan 25, 2013 at 11:15
  • \$\begingroup\$ To me, a state machine approach is much less risky than dealing with function pointers. Your input byte is passed to a state machine structure (could be as simple as a switch-case) which jumps to various bits of code as a function of the state variable or variables. Also, you're not totally clear on where your stdin is coming from (not that it really matters much, but I'm curious). \$\endgroup\$ Commented Jan 25, 2013 at 14:27
  • 10
    \$\begingroup\$ @EJP I disagree. Just because there is an overlap does not mean it has to be on one site or the other. It is asking questions related to the design and programming of low level embedded systems, that seems on topic either place. \$\endgroup\$
    – Kortuk
    Commented Jan 25, 2013 at 15:16
  • \$\begingroup\$ State machines can be based on function indirection: the call to the state transition function returns a new state transition function. \$\endgroup\$
    – Kaz
    Commented Jan 25, 2013 at 22:26
  • 1
    \$\begingroup\$ Lets have this discussion here. \$\endgroup\$
    – user17592
    Commented Feb 2, 2013 at 9:42

2 Answers 2

23
\$\begingroup\$

A function

int f(int x){ .. }

Get the address of a function (and store it in a variable)

int (*fp)(int) = f;

Call a function at a specified address

int x = (*fp)( 12 );
\$\endgroup\$
11
  • 3
    \$\begingroup\$ +1, obvious in the C and ASM world, but not so obvious to those who started out with OO languages. \$\endgroup\$ Commented Jan 25, 2013 at 8:42
  • 4
    \$\begingroup\$ The fp call can also be written as 'int x = fp(12);' to increase readability. \$\endgroup\$
    – Rev
    Commented Jan 25, 2013 at 9:16
  • 1
    \$\begingroup\$ I have to admit, working mainly in C# and Java currently, I miss those good old function pointers from C sometimes. They are dangerous when not done exactly right, and most tasks can be accomplished in other ways, but they sure are useful those few times when they really do come in handy. \$\endgroup\$
    – user
    Commented Jan 25, 2013 at 10:20
  • \$\begingroup\$ @MichaelKjörling: So true. I am constantly switching between embedded C and C# programming (even implementing similar code for communication between device and PC) and as mighty as C# might be, i still enjoy C so much. \$\endgroup\$
    – Rev
    Commented Jan 25, 2013 at 12:51
  • 2
    \$\begingroup\$ fp(12) does not increase readability over (*fp)(12). They have different readability. (*fp)(12) reminds the reader that fp is a pointer, and also resembles the declaration of fp. In my mind, this style is associated with old sources, like X11 internals, and K&R C. One possible reason it might be associated with K&R C is because in ANSI C, it is possible to declare fp using function syntax, if fp is a parameter: int func(int fp(double)) {}. In K&R C that would have been int func(fp) int (*fp)(double); { ... }. \$\endgroup\$
    – Kaz
    Commented Jan 25, 2013 at 22:30
18
\$\begingroup\$

While Wouters answer is absolutely correct this maybe more beginner friendly and a better example regarding your question.

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 */
}

If it is not obvious that the function pointer actually has a target function address assigned, its wise to perform a NULL check.

if (fp != NULL)
    fp(); /* with parameters, if applicable */
\$\endgroup\$
3
  • 8
    \$\begingroup\$ +1 for the null check, but do note that the compiler might not guarantee that the value at the address in RAM that fp happens to occupy is actually NULL initially. I would do int (*fp)(int) = NULL; as a combined declaration and initialization to be certain - you don't want to jump to some random memory location because of some goofy value that just happened to be there. Then just assign fp like in your Example() function. \$\endgroup\$
    – user
    Commented Jan 25, 2013 at 10:19
  • 1
    \$\begingroup\$ Thanks for the comment, i added the NULL initialization. \$\endgroup\$
    – Rev
    Commented Jan 25, 2013 at 12:47
  • 4
    \$\begingroup\$ @MichaelKjörling good point, but if fp is a "global variable" (as int he code above), then it is guaranteed to be initialized to NULL (without having to do so explicitly). \$\endgroup\$
    – Alok
    Commented Jan 25, 2013 at 17:59

Your Answer

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