24
\$\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\$
  • 6
    \$\begingroup\$ Off topic. Belongs on stackoverflow.com. \$\endgroup\$ – user207421 Jan 25 '13 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\$ – Adam Lawrence Jan 25 '13 at 14:27
  • 9
    \$\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 Jan 25 '13 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 Jan 25 '13 at 22:26
  • 1
    \$\begingroup\$ Lets have this discussion here. \$\endgroup\$ – Keelan Feb 2 '13 at 9:42
21
\$\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\$
  • 3
    \$\begingroup\$ +1, obvious in the C and ASM world, but not so obvious to those who started out with OO languages. \$\endgroup\$ – Anindo Ghosh Jan 25 '13 at 8:42
  • 4
    \$\begingroup\$ The fp call can also be written as 'int x = fp(12);' to increase readability. \$\endgroup\$ – Rev1.0 Jan 25 '13 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\$ – a CVn Jan 25 '13 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\$ – Rev1.0 Jan 25 '13 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 Jan 25 '13 at 22:30
17
\$\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\$
  • 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\$ – a CVn Jan 25 '13 at 10:19
  • 1
    \$\begingroup\$ Thanks for the comment, i added the NULL initialization. \$\endgroup\$ – Rev1.0 Jan 25 '13 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 Jan 25 '13 at 17:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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