Thursday, September 27, 2018

An introduction to declare, initialize and access a function pointer in C

     As we know that the pointers are variables which holds the address of another variable. Many of us might have used pointers such as integer pointer, character pointer, pointer to a structure, etc. in our usual C programming. But the use of C function pointers is very occasional. In this post, we can look at the ways to declare, initialize and access the function pointers.

What is Function Pointer:
    A function pointer is a variable, which holds the address of a function. As we know that for each variable in C, one address can be associated. This address is the memory location in main memory where the value of the variable is being stored at the run time. Like variables, each function is also has an address. Functions are sequence of instructions present in a main memory. The address of each function is the address of the first instruction of that function definition.

Function's Signature:
    Each function is having a specific signature. That is, a signature of a function is defined by the number of arguments they accept and the type of those arguments and the return type.

   For example, the below two functions have the same signatures.

   void function1 (int arg1, char arg2);
   void function2 (int val1, char val2);

   The functions "function1" and "function2" has the same signatures as they match in the number and type of arguments accepted and having the matching return type.
 
   void function3 (int data1, char data2, int data3);
   void function4 (int arg1, int arg2);

   The "function3" is having the different signature than the functions "function1" and "function2" as it differs in the number of arguments accepted.  Even though "function4" is accepting two arguments, it is having different signature than the "function1" and "function2" as its 2nd argument type differs.


Why does the Signature of a function matter?
    A function pointer always points to a function with a specific signature. So, we cannot use the same function pointer to point functions with two different signatures.
    For normal pointers, using typecast we can store address of variable of one particular type into a pointer of different type. Consider the below case.

   int *ptr = NULL;
   char data='v';
   ptr = (int*)&data;

  In the above example, we are storing the address of character variable into integer pointer using typecast. But this is NOT possible with function pointers which makes it unique and different.

Function Pointer Declaration:
    The function pointer can be declared as given below.
     <return-type> (*FnPtrName) (<argtype arg1>, <argtype arg2>,....<argtyp argN>)
                                          or
     <return-type> (*FnPtrName) (void)

  In the above syntax, the return-type can be void or any valid return types. FnPtrName can be any variable name which will be used for accessing the function pointers. It can accepts any number of arguments or it can be void.

Example:
  int (*FunPtr) (int,int) = NULL;
  
  In the above example, the function pointer is named as "FunPtr" and it accepts two integers as arguments and returns integer value. This pointer is initialized to NULL during definition of that variable.
  This function pointer can be used to store the address of any functions which is matching the signature of this function pointer; i.e any functions which is accepting two integer arguments and  returns integer results.

 
 Assigning an address to a Function pointer:

    Consider the below two function prototypes.
    int add (int,int);
    int sub (int,int);


    The functions "add" and "sub" accepts two integer arguments and returns integer results. These functions having the same signature as that of the function pointer defined in the previous example. So, we can manipulate these functions using the function pointer in example.
   We can assign the address of a function to a function pointer in two ways.

    FunPtr = add; (or) FunPtr = &add;

   Both of the above initialization are valid. The starting address of those functions will be assigned to the function pointer.

Comparing Function Pointers:
    We can compare a value of the function pointer using "==" operator as given below.

     if ((FunPtr == add) ||(*FunPtr == add))   /* We can compare directly specifying the function pointer or we can compare by de-referencing the function pointer ; both are same */
        printf ("Function Pointer Pointing to add function\n");
     else if ((FunPtr == &sub) || (*FunPtr == &sub))
       printf ("Function Pointer Pointing to sub function\n");

   When we specify "add" or "&sub", the starting address of those functions  are compared with the function pointers.

Invoking Function using a function pointer:
    We can call a function using function pointer in two ways. We can directly use the name of the function pointer or we can de-reference that function pointer.

    rslt = FunPtr (10,20); (or) rslt = (*FunPtr) (10,20);


Example:
    In this example we will put together the concepts introduced above.

 #include <stdio.h>
int add (int,int);
int sub (int,int);
int main()
{
    int var1 = 10, var2 = 20, rslt = 0;

    int (*FunPtr) (int,int) = NULL;

    FunPtr = &add;

    rslt = FunPtr (var1,var2);    /* Calling function "add" using function pointer */
    printf ("Addition Result:%d\r\n",rslt);

Tuesday, September 25, 2018

Compiling a C code using GCC for 32-bit machine architecture on a 64-bit machine


    Many of us aware of the fact that the 64 bit version of machine architecture will have the backward compatibility to lower versions. An application built for 32 bit version of Linux machine can run seamlessly on 64 bit version also, but the vice versa is not true. So, if we compile a C code on 64 bit version of Linux, it cannot be executed on the 32 bit version of Linux.
    By default, GCC builds an application for the native machine architecture. So, on 64-bit machine, the GCC builds an application for 64-bit architecture. If we want an application built on a 64-bit machine to run on both 32-bit and 64-bit machines, we can achieve it using the special compilation flag of GCC.

The -m Flag:
    GCC provides a special  compilation flag "-m" to specify the machine architecture for which the application needs to be built.
    To build for 32-bit version, specify the compilation flag as "-m32", whereas for 64-bit version, use the flag "-m64".

    Note: To execute a 32-bit application on a 64-bit machine, we need to have the 32-bit version of dynamic libraries required by that application on that 64-bit machine; otherwise the application cannot be executed.

Examples:

test.c

#include <stdio.h>
int main()
{
    printf ("Welcome\r\n");
    return 0;
}

32-bit Compilation:

gcc -m32 -o 32bitExe test.c

This Command will compile the "test.c" for 32-bit machine architecture and creates the executable with the name "32bitExe".

 Running the executable, will give the below output.

./32bitExe
Welcome


64-bit Compilation:

gcc -m64 -o 64bitExe test.c

This command will compile the code "test.c" for 64-bit machine architecture and creates the executable with the name "64bitExe".

Running the executable, will give the below output.

./64bitExe
Welcome


How to identify the executable is 32-bit or 64-bit version:

    The linux command "file" can be used to identify whether an executable is of type 32-bit or 64-bit.

    Syntax: file <executable-name>

Example:
We wanted to know whether the executable name "32bitExe" is a 32-bit or 64-bit executable. 

file 32bitExe
32bitExe: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.0.0, BuildID[sha1]=0xd4f96ae7862c856df70e8aa9fe54aeb1b41992df, not stripped

As highlighted in bold above, the output of the file command clearly specifies that the executable "32bitExe" is a 32-bit version of executable.

Similarly, for the executable "64bitExe",

file 64bitExe
64bitExe: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.4.0, BuildID[sha1]=0x14a6ae7113cedf4945c25ad5b599227f85cb46ca, not stripped


Thursday, September 20, 2018

An overview of "#" operator in C - The "C" Stringizing Operator

    In this post, I am going to talk about the stringizing operator in C. We know that the symbol "#" is used in C preprocessor directives (#define statements). In addition, it has special functionality. The # is used as stringizing operator in C.This operator generates a double quoted string when it is preceded by some multi-word string. This multi-word string can have variable name, random string, numbers, etc. This operator can be used only in the macro expansions.

Sample Program:

#include <stdio.h>
#include <string.h>
#define MAXVALUE 10
#define LOG_FAILURE(x) if(x) \
                            /* Condition Met; no need to log*/; \
                        else \
                            LogFn (#x,__func__,__LINE__)

void LogFn(const char *logstr, const char *FnName,unsigned int line)
{
    printf ("LOG: FAILED CONDITION -> %s : In FUNCTION -> %s: In LINE -> %u\n",
                logstr, FnName, line);
}

int main ()
{
    int testval = 5;
    char name[] = "NAMETEST";

    LOG_FAILURE ((testval > MAXVALUE) && "Comparing testval and MAXVALUE"); // This will log the statement as the test result fails.

    testval = 15;

    LOG_FAILURE ((testval > MAXVALUE) && "Comparing testval and MAXVALUE"); // This will not be logged as the test result passes

    testval = 2;

    LOG_FAILURE ((strlen(name) > MAXVALUE) && "Comparing string length and MAXVALUE"); // This will be logged as the test result fails.

    return 0;
}


Output:

LOG: FAILED CONDITION -> (testval > MAXVALUE) && "Comparing testval and MAXVALUE" : In FUNCTION -> main: In LINE -> 20
LOG: FAILED CONDITION -> (strlen(name) > MAXVALUE) && "Comparing string length and MAXVALUE" : In FUNCTION -> main: In LINE -> 28

Expanded source code after Macro Expansion:

# 9 "stringize_oper1.c"
void LogFn(const char *logstr, const char *FnName,unsigned int line)
{
    printf ("LOG: FAILED CONDITION -> %s : In FUNCTION -> %s: In LINE -> %u\n",
                logstr, FnName, line);
}

int main ()
{
    int testval = 5;
    char name[] = "NAMETEST";

/* The macro is expanded after preprocessing; Note that the argument preceded by # has now been converted into double-quoted string */
    if((testval > 10) && "Comparing testval and MAXVALUE") ; else LogFn ("(testval > MAXVALUE) && \"Comparing testval and MAXVALUE\"",__func__,20);

    testval = 15;