Keil Logo

C51: Problems with Function Pointers Overwriting Variables


Information in this article applies to:

  • C51 All Versions

QUESTION

I have a C51 program that uses a function to call other functions via a table of function pointers. I keep having a problem with those functions overwriting the variables used by the main C function. What's going on?

ANSWER

The following program will help exemplify this problem. Note that func_a and func_b are called via function pointers from the func_table array. The dispatch function calls these functions through this table.

/*---------------------------------------------*/
unsigned func_a (int value)
{
volatile int xdata avar;
for (avar = 0; avar < value; avar++);
return (avar);
}

/*---------------------------------------------*/
unsigned func_b (int value)
{
volatile int xdata bvar;
for (bvar = 0; bvar < value; bvar++);
return (bvar);
}

/*---------------------------------------------*/
unsigned (*func_table []) (int value) =
  {
  func_a,
  func_b,
  };

/*---------------------------------------------*/
unsigned dispatcher (void)
{
static xdata int i;

i = (i + 1) % 2;

return ((*func_table [i]) (i));
}

/*---------------------------------------------*/
void main (void)
{
xdata int k;
while (1)
  {
  for (k = 0; k < 100; k++)
    dispatcher ();
  }
}
/*---------------------------------------------*/

This simple program appears as though it SHOULD work. However, the following linker MAP file shows some interesting information.

SEGMENT                          XDATA_GROUP
  +--> CALLED SEGMENT          START    LENGTH
----------------------------------------------
?C_C51STARTUP                  -----    -----
  +--> ?PR?MAIN?DMAIN
  +--> ?C_INITSEG

?PR?MAIN?DMAIN                 100BH    0002H
  +--> ?PR?DISPATCHER?DMAIN

?C_INITSEG                     -----    -----
  +--> ?PR?_FUNC_A?DMAIN
  +--> ?PR?_FUNC_B?DMAIN

?PR?_FUNC_A?DMAIN              100BH    0002H

?PR?_FUNC_B?DMAIN              100BH    0002H

Note that the variables in segment ?PR?MAIN?DMAIN start at the same address as the variables in segments ?PR?_FUNC_A?DMAIN and ?PR?_FUNC_B?DMAIN. This means that the variables in main are OVERLAID with the variables in func_a and func_b. This is NOT what is desired. Whenever func_a or func_b executes, the variables in main will get trashed.

In the MAP file, note that ?C_INITSEG is the segment which "calls" func_a and func_b. This is incorrect since ?C_INITSEG is the variable initialization routine which actually calls NO functions. So, why does the linker think these functions are called by the initialization code? Why doesn't it know that they are called by the ?PR?DISPATCHER?DMAIN segment?

The reason for this is because the function table is declared as follows:

unsigned (*func_table []) (int value) =

And, it is stored in the default data space (XDATA in this example). Therefore, the compiler makes a link to func_a and func_b from the PUBLIC XDATA space allocated to the DMAIN module. Since the initialization code initializes the XDATA table, the segment ?C_INITSEG links to these functions.

There are 2 ways you can solve this problem. Manually editing the segment links in the call tree or declaring the pointer table to lie in CODE space.

You can manually remove the links from ?C_INITSEG and add them to ?PR?DISPATCHER?DMAIN using the following overlay command for the linker.

OVERLAY (?C_INITSEG ~ ?PR?_FUNC_A?DMAIN,
         ?C_INITSEG ~ ?PR?_FUNC_B?DMAIN,
         ?PR?DISPATCHER?DMAIN ! ?PR?_FUNC_A?DMAIN,
         ?PR?DISPATCHER?DMAIN ! ?PR?_FUNC_B?DMAIN)

The easiest way to do this from µVision is to select the Linker Options and go to the file tab. Check the "User Command File" check box and click the Create button and the Edit button. Close the Linker Options dialog box and enter the overlay command in the linker response file window that was opened. Make sure you put ampersand characters ('&') at the end of each line.

In µVision, open the Project Options Dialog, Select the BL51 Misc Tab, and enter the following in the Overlay Input Line:

?C_INITSEG ~ ?PR?_FUNC_A?DMAIN,
?C_INITSEG ~ ?PR?_FUNC_B?DMAIN,
?PR?DISPATCHER?DMAIN ! ?PR?_FUNC_A?DMAIN,
?PR?DISPATCHER?DMAIN ! ?PR?_FUNC_B?DMAIN

When you link the project, you will see that the links from ?C_INITSEG are gone and links from ?PR?DISPATCHER?DMAIN have been added.

SEGMENT                          XDATA_GROUP
  +--> CALLED SEGMENT          START    LENGTH
----------------------------------------------
?C_C51STARTUP                  -----    -----
  +--> ?PR?MAIN?DMAIN
  +--> ?C_INITSEG

?PR?MAIN?DMAIN                 1008H    0002H
  +--> ?PR?DISPATCHER?DMAIN

?PR?DISPATCHER?DMAIN           -----    -----
  +--> ?PR?_FUNC_A?DMAIN
  +--> ?PR?_FUNC_B?DMAIN

?PR?_FUNC_A?DMAIN              100AH    0002H

?PR?_FUNC_B?DMAIN              100AH    0002H

Declaring the Pointer Table in CODE Space

Another method of correcting this problem is to declare the function pointer table to lie in CODE space. For example:

code unsigned (*func_table []) (int value) =
  {
  func_a,
  func_b,
  };

The above declaration causes a new segment ?CO?modulename to be generated. It is this segment (instead of ?C_INITSEG) that is now linked to these functions. This is shown in the following MAP file excerpt.

SEGMENT                          XDATA_GROUP
  +--> CALLED SEGMENT          START    LENGTH
----------------------------------------------
?C_C51STARTUP                  -----    -----
  +--> ?PR?MAIN?DMAIN

?PR?MAIN?DMAIN                 1000H    0002H
  +--> ?PR?DISPATCHER?DMAIN

?PR?DISPATCHER?DMAIN           -----    -----
  +--> ?CO?DMAIN

?CO?DMAIN                      -----    -----
  +--> ?PR?_FUNC_A?DMAIN
  +--> ?PR?_FUNC_B?DMAIN

?PR?_FUNC_A?DMAIN              1002H    0002H

?PR?_FUNC_B?DMAIN              1002H    0002H

Note here that ?CO?DMAIN "calls" func_a and func_b. Note also that ?PR?DISPATCHER?DMAIN "calls" ?CO?DMAIN. This completes the call tree and no OVERLAY entries are required.

In conclusion, note that function pointers in C51 are not as simple as they may seem. Be cautious when using them, and your program will work as expected. If you are not careful, you may spend a lot of time debugging the problem.

MORE INFORMATION

SEE ALSO


Last Reviewed: Thursday, February 25, 2021


Did this article provide the answer you needed?
 
Yes
No
Not Sure
 
  Arm logo
Important information

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies.

Change Settings

Privacy Policy Update

Arm’s Privacy Policy has been updated. By continuing to use our site, you consent to Arm’s Privacy Policy. Please review our Privacy Policy to learn more about our collection, use and transfers
of your data.