Saturday, September 17, 2022

non categorized

 

1. How can "typedef" make my code portable?

ref:  https://stackoverflow.com/questions/19499321/how-can-typedef-make-my-code-portable


ans1:

The size of type int is not exactly specified by C, it is only supposed to be at least equal to 16 bits. So, on some processor architecture, compiler implements it as 16 bits whereas on some other as 32 bits. So, if a programmer writes int a, it would take 2 byte on the 1st architecture and 4 byte on the second -- i.e. non-uniform behavior across platforms (lack of portability considering the 'size' aspect).

To avoid that, the types are defined using names like uint_16 or uint_32 in some global header files (say, types.h). Inside types.h, the user-defined types would be correctly mapped to the suitable native type -- and there may be processor specific defines in types.h. e.g. considering your example:-

#ifdef AVR_32
typedef unsigned int  uint_32
#else
typedef unsigned long uint_32  
#endif

Later, a programmer would use these types(uint_16, uint_32 etc.) and won't bother about the size himself.


ans2:


Typedefs in the context of portability are generally used in preprocessor macros with conditional compiling:

#if defined(INTEL_PROCESSOR)
typedef int my_desired_integer;
#elif defined(POWER_PC)
typedef long int my_desired_integer;
#elif defined(SOME_OTHER_WEIRD_PROCESSOR_OR_OS)
typedef short int my_desired_integer;
#endif

Then you use my_desired_integer everywhere in your code and the preprocessor will make sure to put the right type into the typedef.


2. function pointer + typedef : 


ref: https://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work?rq=1


Function pointers in C

Let's start with a basic function which we will be pointing to:

int addInt(int n, int m) {
    return n+m;
}

First thing, let's define a pointer to a function which receives 2 ints and returns an int:

int (*functionPtr)(int,int);

Now we can safely point to our function:

functionPtr = &addInt;

Now that we have a pointer to the function, let's use it:

int sum = (*functionPtr)(2, 3); // sum == 5

Passing the pointer to another function is basically the same:

int add2to3(int (*functionPtr)(int, int)) {
    return (*functionPtr)(2, 3);
}

We can use function pointers in return values as well (try to keep up, it gets messy):

// this is a function called functionFactory which receives parameter n
// and returns a pointer to another function which receives two ints
// and it returns another int
int (*functionFactory(int n))(int, int) {
    printf("Got parameter %d", n);
    int (*functionPtr)(int,int) = &addInt;
    return functionPtr;
}

But it's much nicer to use a typedef:

typedef int (*myFuncDef)(int, int);
// note that the typedef name is indeed myFuncDef

myFuncDef functionFactory(int n) {
    printf("Got parameter %d", n);
    myFuncDef functionPtr = &addInt;
    return functionPtr;
}











4.  variable length argument in detail

Ref: https://blog.aaronballman.com/2012/06/how-variable-argument-lists-work-in-c/
void foo( int bar, ... ) {
  va_list args;
  va_start( args, bar );
       
  for (;;) {
    some_type t = va_arg( args, some_type );
    /* Do something with t */
  }
  va_end( args );
}
typedef unsigned char *va_list;
#define va_start(list, param) (list = (((va_list)&param) + sizeof(param)))
#define va_arg(list, type)    (*(type *)((list += sizeof(type)) - sizeof(type)))


ans 2:

If you look at the way the C language stores the parameters on the stack, the way the macros work should become clear:-

Higher memory address    Last parameter
                         Penultimate parameter
                         ....
                         Second parameter
Lower memory address     First parameter
       StackPointer  ->  Return address
(note, depending on the hardware the stack pointer maybe one line down and the higher and lower may be swapped)

The arguments are always stored like this1, even without the ... parameter type.

The va_start macro just sets up a pointer to the first function parameter, e.g.:-

 void func (int a, ...)
 { 
   // va_start
   char *p = (char *) &a + sizeof a;
 }
which makes p point to the second parameter. The va_arg macro does this:-

 void func (int a, ...)
 { 
   // va_start
   char *p = (char *) &a + sizeof a;

   // va_arg
   int i1 = *((int *)p);
   p += sizeof (int);

   // va_arg
   int i2 = *((int *)p);
   p += sizeof (int);

   // va_arg
   long i2 = *((long *)p);
   p += sizeof (long);
 }
The va_end macro just sets the p value to NULL.



5.typedef in detail:
https://stackoverflow.com/questions/4295432/typedef-function-pointer?rq=1

typedef is a language construct that associates a name to a type.
You use it the same way you would use the original type, for instance

typedef int myinteger;
typedef char *mystring;
typedef void (*myfunc)();
using them like

myinteger i;   // is equivalent to    int i;
mystring s;    // is the same as      char *s;
myfunc f;      // compile equally as  void (*f)();
As you can see, you could just replace the typedefed name with its definition given above.

The difficulty lies in the pointer to functions syntax and readability in C and C++, and the typedef 
can improve the readability of such declarations. However, the syntax is appropriate, since functions - 
unlike other simpler types - may have a return value and parameters, thus the sometimes lengthy and 
complex declaration of a pointer to function.

The readability may start to be really tricky with pointers to functions arrays, and some other even more indirect flavors.

To answer your three questions

Why is typedef used? To ease the reading of the code - especially for pointers to functions, or structure names.

The syntax looks odd (in the pointer to function declaration) That syntax is not obvious to read, at least when beginning. 
Using a typedef declaration instead eases the reading

Is a function pointer created to store the memory address of a function? Yes, a function pointer stores the address of a function. 
This has nothing to do with the typedef construct which only ease the writing/reading of a program ; the compiler just expands the 
typedef definition before compiling the actual code.



#include <stdio.h>
#include <math.h>

/*
To define a new type name with typedef, follow these steps:
1. Write the statement as if a variable of the desired type were being declared.
2. Where the name of the declared variable would normally appear, substitute the new type name.
3. In front of everything, place the keyword typedef.
*/

// typedef a primitive data type
typedef double distance;

// typedef struct 
typedef struct{
    int x;
    int y;
} point;

//typedef an array 
typedef point points[100]; 

points ps = {0}; // ps is an array of 100 point 

// typedef a function
typedef distance (*distanceFun_p)(point,point) ; // TYPE_DEF distanceFun_p TO BE int (*distanceFun_p)(point,point)

// prototype a function     
distance findDistance(point, point);

int main(int argc, char const *argv[])
{
    // delcare a function pointer 
    distanceFun_p func_p;

    // initialize the function pointer with a function address
    func_p = findDistance;

    // initialize two point variables 
    point p1 = {0,0} , p2 = {1,1};

    // call the function through the pointer
    distance d = func_p(p1,p2);

    printf("the distance is %f\n", d );

    return 0;
}

distance findDistance(point p1, point p2)
{
distance xdiff =  p1.x - p2.x;
distance ydiff =  p1.y - p2.y;

return sqrt( (xdiff * xdiff) + (ydiff * ydiff) );
}





No comments:

Post a Comment