Maecenas at ipsum quis massa rutrum gravida! Ut in varius orci. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
Phasellus sed lectus nec risus posuere rhoncus sed et ligula. Sed gravida ornare turpis vel euismod. Phasellus quis tortor non lacus sodales rutrum sit amet non est
Donec elit nulla, pulvinar nec porta sed, hendrerit eget metus. Suspendisse porttitor ligula non felis volutpat pretium? Praesent laoreet nisl a eros ultricies in lacinia



Week - 3 Memory Mechanics (Structures and Array of structures)

Last week we saw the memory mechanics of floating point numbers, typecasting the pointers, and all those
asterisk-ampersand tricks and consequences. We have covered how primitive data-types are handled
at lower levels - more or less. All of this eventually makes us better programmers and designers. Today we discuss about memory mechanics of structures and the array of structures.

Starting off, consider the following structure;

struct fraction
{
int num;
int denom;
};

struct fraction pi;

In the memory, 'pi' would look this :



pi.num = 22;
pi.denom = 7;

Just assigning values. Now,
((struct fraction*)&(pi.denom)) -> num = 12;
Confused?! :D

Well what happens is as follows:
 address of pi.denom is sought
 &pi.denom which is an integer pointer, is type-cast to a fraction pointer.
 Now the num field of this pointer is assigned the 12. Question is where is this num field?

To explain this, we need to recollect what happened when a float pointer was converted to int, which is what we did last week. The contents of the memory remain the same. But the compiler thinks of the memory location as an int.Same thing happens here. The compiler thinks of &pi.denom to be the starting address of a structure (even if it really isn't).


Hence if we display pi.denom we get 12.

// ------------------------------- Code ------------------------------------ //

#include
struct fraction
{
int num;
int denom;
};
int main()
{
struct fraction pi;
pi.num = 22;
pi.denom = 7;
printf("Numerator : %d",pi.num);
printf("\nDenominator : %d",pi.denom);
((struct fraction*)&(pi.denom))->num = 12;
printf("\nAfter manipulating with memory...\n");
printf("Numerator : %d",pi.num);
printf("\nDenominator : %d",pi.denom);
return 0;
}



Moving on to the array of structures, consider the following structure;

struct Students
{
char *name;
char SUID[8];
int no_of_units;
};

Memory reserved when a variable is declared would look something like this :


Note that we read the contents in these sort of diagrams from left to right, bottom to top.

Array of such structures, in the memory would look like the following:

struct Student pupils[4];



Consider this:

pupils[0].no_of_units = 21;

This simply puts the value 21 in pupil[0] 's 'no_of_units' field.
Likewise,

pupils[2].name = strdup("Adam");

Here strdup() is a function defined in that it dynamically allocates the required memory
for the string and passes the pointer to the location where the memory was allocated. This pointer is stored in name field of pupils[0].

Things get tricky now:

pupils[3].name = pupils[0].SUID + 6;

This would pupils[3].name to point to the memory location 6 bytes away from
pupils[0].SUID. Thus,
strcpy(pupils[0].SUID,"1234567");
pupils[0].no_of_units = 0;
printf("%s",pupils[3].name);

Output : 7

Note that strcpy() does not do any kind of bounds-checking - it goes on copying bytes until it
encounters a '\0' character. Hence,

strcpy(pupils[0].SUID,"12345678");
pupils[0].no_of_units = 0;
printf("%s",pupils[3].name);
... simply displays 78. Note that pupils[0].no_of_units is assigned 0 so that it acts as
the '\0' character.( '\0' has a ASCII code of 0). Else there could be segmentation fault on
some systems with all sorts of junk values being displayed on the screen!

Extending this further,

strcpy(pupils[0].SUID,"12345678");
pupils[0].no_of_units = 65;
printf("%s",pupils[3].name);

would surprisingly display 78A.



// ---------------------------------- Complete Code ------------------------------------ //

#include
#include
struct Student
{
char *name;
char SUID[8];
int no_of_units;
};
int main()
{
struct Student pupils[4];
pupils[3].name = pupils[0].SUID + 6;
strcpy(pupils[0].SUID,"12345678");
pupils[0].no_of_units = 65;
printf("%s",pupils[3].name);
return 0;
}

// -------------------------------------------------------------------------------------- //

It has become apt to make a small point about strings in C.

Consider the following code:

#include
int main()
{
char name[] = {"Free and Open Source Software"};
printf("%s",&name[5]);
return 0;
}



Thus printf() function was lead into believing that the string started from the letter 'a'. This code makes one point clear: all the functions that deal with strings treat the given char pointer as the base address of the first character of the string ( regardless of the the bytes before the specified address) and treat the rest of the memory till the null character '\0' as the string. This '\0' character is actually what distinguishes an array of characters from strings.



Having discussed all the memory mechanics of the various data types, we are now in a position to write generic functions. Generic functions are functions that can handle any data type(Just like a template does in C++). A detailed account generic functions are will be taken care of next week.

We start off with the swap() function to swap two integers and the code (as most of us would know) is
as below:
void swap(int *aptr,int *bptr)
{
int temp;
temp = *aptr;
*aptr = *bptr;
*bptr = temp;
}

We see this function handles with integer data-types ( thus being int specific). Our aim is to write avgeneric swap() function - a function that can swap values of two memory locations regardless of what the data-types are. First of all lets see what happens in this function.

This function basically swaps the memory contents using their addresses. This is mandatory as there is no way out. (At least at this level of programming!). Using the memory address, we manipulate the individual variables. Swapping thus happens in the following 3 steps;

• contents of memory location pointed by aptr ( which contains address of the first variable
passed) are copied to a temporary variable temp.
• memory location pointed by aptr are over written with contents of memory location pointed
by bptr;
• contents of memory pointed bptr are replaced with value of temp ( which originally had
contents of first memory location)

Thus using the memory addresses we swap the contents of the memory.

Using all this as the background, next week we see how a generic function will be written for this int specific swap() function. And not just this, but grab some fundamentals, the do-and-don'ts of generic functions. So stay tuned!

Leave a Reply