Home arrow C programming arrow Branch Tables via Function Pointer Arrays in C

Language Translator

Hacking Zone

Hacking Tools
Attacking

Configure Windows

Windows Configuration

Novels

Mix Novels

Human Personality

Body Language
Branch Tables via Function Pointer Arrays in C PDF Print E-mail
Written by Hemanshu Patel   
Saturday, 24 November 2007
Article Index
Branch Tables via Function Pointer Arrays in C
Page 2
Page 3
Page 4
Page 5
Page 6

Interrupt vector tables


The fourth application of function jump tables is the array of interrupt vectors. On most processors, the interrupt vectors are in contiguous locations, with each vector representing a pointer to an interrupt service routine function. Depending upon the compiler, the work may be done for you implicitly, or you may be forced to generate the function table. In the latter case, implementing the vectors via a switch statement will not work!

Here is the vector table from the industrial power supply project mentioned above. This project was implemented using a Whitesmiths' compiler and a 68HC11 microncontroller.

IMPORT VOID _stext();    /* startup routine -68HC11 specific*/

static VOID (* const _vectab[])() = {
    SCI_Interrupt    ,    /* SCI              */
    badSPI_Interrupt,    /* SPI              */
    badPAI_Interrupt,    /* Pulse acc input  */
    badPAO_Interrupt,     /* Pulse acc overf  */
    badTO_Interrupt,    /* Timer overf      */
    badOC5_Interrupt,    /* Output compare 5 */
    badOC4_Interrupt,    /* Output compare 4 */
    badOC3_Interrupt,     /* Output compare 3 */
    badOC2_Interrupt,    /* Output compare 2 */
    badOC1_Interrupt,    /* Output compare 1 */
    badIC3_Interrupt,    /* Input capture 3  */
    badIC2_Interrupt,    /* Input capture 2  */
    badIC1_Interrupt,    /* Input capture 1  */
    RTI_Interrupt,        /* Real time        */
    Uart_Interrupt,        /* IRQ              */
    PFI_Interrupt,        /* XIRQ             */
    badSWI_Interrupt,    /* SWI              */
    IlOpC_Interrupt,    /* illegal          */
    _stext,                /* cop fail         */
    _stext,                /* cop clock fail   */
    _stext,                /* RESET            */
    };

A couple of points are worth making:

   1. The above is insufficient to locate the table correctly in memory. This has to be done via linker directives.
   2. Note that unused interrupts still have an entry in the table. Doing so ensures that:
          * The table is correctly aligned
          * Traps can be placed on unexpected interrupts

If any of these examples has whet your appetite for using arrays of function pointers, but you are still uncomfortable with the declaration complexity, then fear not! You will find a variety of declarations, ranging from the straightforward to the downright appalling below. The examples are all reasonably practical in the sense that the desired functionality is not outlandish (that is, there are no declarations for arrays of pointers to functions that take pointers to arrays of function pointers and so on).
Declaration and use hints

All of the examples below adhere to conventions that I have found to be useful over the years, specifically:

   1. All of the examples are preceded by static . This is done on the assumption that the scope of a function table should be highly localized, ideally within an enclosing function.
   2. In every example the array pf[] is also preceded with const. This declares that the pointers in the array cannot be modified after initialization. This is the normal (and safe) usage scenario.
   3. There are two syntactically different ways of invoking a function via a pointer. If we have a function pointer with the declaration:

      void (*fnptr)(int);    // fnptr is a function pointer

      Then it may be invoked using either of these methods:

      fnptr(3);        // Method 1 of invoking the function
      (*fnptr)(3);    // Method 2 of invoking the function

      The advantage of the first method is an uncluttered syntax. However, it makes it look as if fnptr is a function, as opposed to being a function pointer. Someone maintaining the code may end up searching in vain for the function fnptr(). With method 2, it is much clearer that we are dereferencing a pointer. However, when the declarations get complex, the added (*) can be a significant burden. Throughout the examples, each syntax is shown. In practice, the latter syntax seems to be more popular--and you should use only one.
   4. In every example, the syntax for using a typedef is also given. It is quite permissible to use a typedef to define a complex declaration, and then use the new type like a simple type. If we stay with the example above, then an alternative declaration is:

      typedef void (*PFV_I )(int);
      PFV_I fnptr = fna;    // Declare a PVFV_I typed variable and init it
      fnptr(3);            // Call fna with parameter 3 using method 1
      (*fnptr)(3);        // Call fna with parameter 3 using method 2

      The typedef declares the type PFV_I to be a pointer to a function that returns void and is passed an integer. We then simply declare fnptr to a variable of this type, and use it. Typedefs are very good when you regularly use a certain function pointer type, since it saves you having to remember and type in the declaration. The downside of using a typedef, is the fact that it is not obvious that the variable that has been declared is a pointer to a function. Thus, just as for the two invocation methods above, you can gain syntactical simplicity by hiding the underlying functionality.

      In the typedefs, a consistent naming convention is used. Every type starts with PF (Pointer to Function) and is then followed with the return type, followed by an underscore, the first parameter type, underscore, second parameter type and so on. For void, boolean, char, int, long, float and double, the characters V, B, C, I, L, S, D are used. (Note the use of S(ingle) for float, to avoid confusion with F(unction)). For a pointer to a data type, the type is preceded with P. Thus PL is a pointer to a long. If a parameter is const, then a c appears in the appropriate place. Thus, cPL is a const pointer to a long, whereas a PcL is a pointer to a const long, and cPcL is a const pointer to a const long. For volatile qualifiers, v is used. For unsigned types, a u precedes the base type. For user defined data types, you are on your own!

      An extreme example: PFcPcI_uI_PvuC. This is a pointer to a function that returns a const pointer to a const Integer that is passed an unsigned integer and a pointer to a volatile unsigned char.

Function pointer templates


The first eleven examples are generic in the sense that they do not use memory space qualifiers and hence may be used on any target. Example 12 shows how to add memory space qualifiers, such that all the components of the declaration end up in the correct memory spaces.

Example 1

pf[] is a static array of pointers to functions that take an INT as an argument and return void.

void fna(INT);    //Example prototype of a function to be called

//Declaration using typedef
typedef void (* const PFV_I)(INT);
static PFV_I pf[] = {fna,fnb,fnc, … fnz);

//Direct declaration
static void (* const pf[])(INT) = {fna, fnb, fnc, … fnz};

//Example use
INT a = 6;
pf[jump_index](a);        //Calling method 1
(*pf[jump_index])(a);    //Calling method 2

Example 2

pf [] is a static array of pointers to functions that take a pointer to an INT as an argument and return void.

void fna(INT *);    //Example prototype of a function to be called

//Declaration using typedef
typedef void (* const PFV_PI)(INT *);
static PVF_PI[] = {fna,fnb,fnc, … fnz};

//Direct declaration
static void (* const pf[])(INT *) = {fna, fnb, fnc, … fnz};

//Example use
INT a = 6;
pf[jump_index](&a);        //Calling method 1
(*pf[jump_index])(&a);    //calling method 2

Example 3

pf [] is a static array of pointers to functions that take an INT as an argument and return a CHAR

CHAR fna(INT);     //Example prototype of a function to be called

//Declaration using typedef
typedef CHAR (* const PFC_I)(INT);
static PVC_I[] = {fna,fnb,fnc, … fnz};

//Direct declaration
static CHAR (* const pf[])(INT) = {fna, fnb, fnc, … fnz};

//Example use
INT a = 6;
CHAR res;
res = pf[jump_index](a);        //Calling method 1
res = (*pf[jump_index])(a);    //Calling method 2

Example 4

pf [] is a static array of pointers to functions that take an INT as an argument and return a pointer to a CHAR.

CHAR *fna(INT);    //Example prototype of a function to be called

//Declaration using typedef
typedef CHAR * (* const PFPC_I)(INT);
static PVPC_I[] = {fna,fnb,fnc, … fnz};

//Direct declaration
static CHAR * (* const pf[])(INT) = {fna, fnb, fnc, … fnz};

//Example use
INT a = 6;
CHAR * res;
res = pf[jump_index](a);     //Calling method 1
res = (*pf[jump_index])(a);    //Calling method 2

Example 5

pf [] is a static array of pointers to functions that take an INT as an argument and return a pointer to a const CHAR (i.e. the pointer may be modified, but what it points to may not).

const CHAR *fna(INT);     //Example prototype of a function to be called

//Declaration using typedef
typedef const CHAR * (* const PFPcC_I)(INT);
static PVPcC_I[] = {fna,fnb,fnc, … fnz};

//Direct declaration
static const CHAR * (* const pf[])(INT) = {fna, fnb, fnc, … fnz};

//Example use
INT a = 6;
const CHAR * res;
res = pf[jump_index](a);        //Calling method 2
res = (*pf[jump_index])(a);    //Calling method 2

Example 6

pf [] is a static array of pointers to functions that take an INT as an argument and return a const pointer to a CHAR (i.e. the pointer may not be modified, but what it points to may be modified).

CHAR * const fna(INT i);//Example prototype of a function to be called

//Declaration using typedef
typedef CHAR * const (* const PFcPC_I)(INT);
static PVcPC_I[] = {fna,fnb,fnc, … fnz};

//Direct declaration
static CHAR * const (* const pf[])(INT) = {fna, fnb, fnc, … fnz};

//Example use
INT a = 6;
CHAR * const res = pf[jump_index](a);    //Calling method 1
CHAR * const res = (*pf[jump_index])(a);    //Calling method 2

Example 7

pf [] is a static array of pointers to functions that take an INT as an argument and return a const pointer to a const CHAR (i.e. the pointer, nor what it points to may be modified)

const CHAR * const fna(INT i);//Example function prototype

//Declaration using typedef
typedef const CHAR * const (* const PFcPcC_I)(INT);
static PVcPcC_I[] = {fna,fnb,fnc, … fnz};

//Direct declaration
static const CHAR * const (* const pf[])(INT) = {fna, fnb, fnc, … fnz};

//Example use
INT a = 6;
const CHAR* const res = pf[jump_index](a);         //Calling method 1
const CHAR* const res = (*pf[jump_index])(a);     //Calling method 2

Example 8

pf [] is a static array of pointers to functions that take a pointer to a const INT as an argument (i.e. the pointer may be modified, but what it points to may not) and return a const pointer to a const CHAR (i.e. the pointer, nor what it points to may be modified)

const CHAR * const fna(const INT *i);    //Example prototype

//Declaration using typedef
typedef const CHAR * const (* const PFcPcC_PcI)(const INT *);
static PVcPcC_PcI[] = {fna,fnb,fnc, … fnz};

//Direct declaration
static const CHAR * const (* const pf[])(const INT *) = {fna, fnb, fnc, … fnz};

//Example use
const INT a = 6;
const INT *aptr;
aptr = &a;
const CHAR* const res = pf[jump_index](aptr);    //Calling method 1
const CHAR* const res = (*pf[jump_index])(aptr);//Calling method 2

Example 9

pf [] is a static array of pointers to functions that take a const pointer to an INT as an argument (i.e. the pointer may not be modified, but what it points to may ) and return a const pointer to a const CHAR (i.e. the pointer, nor what it points to may be modified)

const CHAR * const fna(INT *const i);    //Example prototype

//Declaration using typedef
typedef const CHAR * const (* const PFcPcC_cPI)(INT * const);
static PVcPcC_cPI[] = {fna,fnb,fnc, … fnz};

//Direct declaration
static const CHAR * const (* const pf[])(INT * const) = {fna, fnb, fnc, … fnz};

//Example use
INT a = 6;
INT *const aptr = &a;
const CHAR* const res = pf[jump_index](aptr);        //Method 1
const CHAR* const res = (*pf[jump_index])(aptr);        //Method 2

Example 10

pf [] is a static array of pointers to functions that take a const pointer to a const INT as an argument (i.e. the pointer nor what it points to may be modified) and return a const pointer to a const CHAR (i.e. the pointer, nor what it points to may be modified)

const CHAR * const fna(const INT *const i);    //Example prototype

//Declaration using typedef
typedef const CHAR * const (* const PFcPcC_cPcI)(const INT * const);
static PVcPcC_cPcI[] = {fna,fnb,fnc, … fnz};

//Direct declaration
static const CHAR * const (* const pf[])(const INT * const) = {fna, fnb, fnc, … fnz};

//Example use
const INT a = 6;
const INT *const aptr = &a;
const CHAR* const res = pf[jump_index](aptr);        //Method 1
const CHAR* const res = (*pf[jump_index])(aptr);        //Method 2

This example manages to combine five incidences of const and one of static into a single declaration. For all of its complexity, however, this is not an artificial example. You could go ahead and remove all the const and static declarations and the code would still work. It would, however, be a lot less safe, and potentially less efficient.

Just to break up the monotony, here is the same declaration, but with a twist.


 
< Prev   Next >
Your Ad Here

Donate us!!

Enter Amount:

RSS socialnet

Add to MyYahoo!
Subscribe in NewsGator Online
Add to Newsburst
Add to Google
Add to My AOL
Add to Pluck
Subscribe in FeedLounge
Add to Windows Live
Add to NetVibes
Subscribe in Rojo
Subscribe in Bloglines
Add to MyMSN
Add to Plusmo for your cellphone
Add to PageFlakes
Add to Technorati
Add to BlinkBits

Powered password keylogger is a driver-based software keylogger by Eltima

Tired of MS Office ? Try Ashampoo Office 2008 . All OS supported