SE250:lab-2:npit006

From Marks Wiki
Jump to navigation Jump to search

Intro

I was sick with the flu so I could not do this lab on campus... so these results are based on running this on a 64 bit Windows machine using Dev-C++. I firstly tried this lab using MSVC which was a complete and utter waste of time because it was telling me that &x - &y was 12 bytes for Task 1. I'm dual booting with Linux, so I'll give my code a go on that when I can.


Question 1 - Pointer/Datatype Sizes

#include <stdio.h>

int main(void){
     int intVar=10;
     int* ptrToIntVar=&intVar;

     short shortVar=10;
     short* ptrToShortVar=&shortVar;

     long longVar=10;
     long* ptrToLongVar=&longVar;

     double doubleVar=10.000;
     double* ptrToDoubleVar=&doubleVar;

     float floatVar=10.000;
     float* ptrToFloatVar=&floatVar;

     char charVar='a';
     char* ptrToCharVar=&charVar;

     char stringVar[]="Hello, this is a string";
     char* ptrToStringVar=stringVar;

     printf("Int Testing %d %d \n",sizeof(intVar),sizeof(ptrToIntVar));
     printf("Short Testing %d %d \n",sizeof(shortVar),sizeof(ptrToShortVar));
     printf("Long Testing %d %d \n",sizeof(longVar),sizeof(ptrToLongVar));
     printf("Double Testing %d %d \n",sizeof(doubleVar),sizeof(ptrToDoubleVar));
     printf("Float Testing %d %d \n",sizeof(floatVar),sizeof(ptrToFloatVar));
     printf("Char Testing %d %d \n",sizeof(charVar),sizeof(ptrToCharVar));
     printf("String Testing %d %d \n",sizeof(stringVar),sizeof(ptrToStringVar));

     return 1;
}

From the results, I was able to conclude that all pointers are 4 bytes, which was unexpected, as I am running on a 64 bit machine and therefore expect pointers of 8 bytes (64 bits) in size. Still, this could simply be a limitation of the C language - as far as I know, the last C language specification was the ANSI C99 one when 64 bit computers weren't prevalent. But I did expect the pointers all to be the same size regardless of the datatypes they point to.

Char = 1 byte, Short = 2 bytes, Int = Long = Float = 4 bytes, Double = 8 bytes, String obviously varies

Question 2 - Local Variables Addresses and Performing Arithmetic with Them

#include <stdio.h>

int main(void){
	int x;
	int y;

 	printf("&x = %p, &y = %p, Difference = %ld \n",&x,&y, (long)(&x-&y));
	printf("&x = %p, &y = %p, Difference = %ld \n",&x,&y, ((long)&x-(long)&y));
       return 1;
}

With this code, I received the results below. I would expect there to be 4 bytes difference between &x and &y since int types occupy 4 bytes. The second difference calculation gave me that expected result. The first difference calculation involves subtracting one %p type from another %p type then casting as long, which gives an odd result. It's possible that it means that the difference between &x and &y is 1 datatype, which is true (1 int). When casting both &x and &y from %p types to longs, we receive 4 bytes as a result.

Though &y is declared after &x, &y seems to have a lower memory address. This could be explained by the call stack pushing upwards into unallocated memory territory as more variables are added to it.

&x = 0022FF74, &y = 0022FF70, Difference = 1
&x = 0022FF74, &y = 0022FF70, Difference = 4

Question 3 - Local Variable/Array Memory Addresses

Arr is 4 bytes long.
The address of arr is 0022FF70
The value of arr+4 is 0022FF74.
The address of arr[4] is 0022FF74.

&x = 0022FF74, &y = 0022FF6C, Difference (number of datatypes?) = 2
&x = 0022FF74, &y = 0022FF6C, Difference (bytes) = 8

This output was as expected. arr is essentially a pointer to the array starting at arr[0], therefore, arr+4 is the same as arr[4]. &x > arr > &y... and so the difference between &x and &y is as expected... 4 bytes for the int plus 4 bytes for the array.

Next, I varied the size of the array and these were my results.

No array, &x - &y = 4
arr[0], &x - &y = 32
arr[1], &x - &y = 8
arr[2], &x - &y = 8
arr[3], &x - &y = 32
arr[4], &x - &y = 8
arr[5], &x - &y = 32
arr[6], &x - &y = 32
arr[7], &x - &y = 32
arr[8], &x - &y = 16
arr[9], &x - &y = 32
arr[10], &x - &y = 32

After resetting the array to 4 bytes, setting x=y=0 and making arr[4]=0, these were my results. It's easy to see what has happened here. Since we declared arr as a 4 byte array, the compiler wedged the array in between &x and &y with no gaps. But we defined arr[4] as 10, which is the 5th byte of the array. Thus we have left the bounds of the array and overwritten memory which was intended for use by the x variable.

Arr is 4 bytes long.
The address of arr is 0022FF70
The value of arr+4 is 0022FF74.
The address of arr[4] is 0022FF74.

&x = 0022FF74, &y = 0022FF6C, Difference (number of datatypes?) = 2
&x = 0022FF74, &y = 0022FF6C, Difference (bytes) = 8
x = 10, y = 0

Question 4/2 - Global Variable Addresses and Performing Arithmetic With Them

This involves the same code as Question 2 except that x and y are now declared globally, and the results are below. The bottom line is the difference between &x and &y in bytes... (long)&x - (long)&y ... For whatever reason, the difference is 16 bytes rather than the 4 bytes in Question 2. Since x and y are now global static variables, their memory addresses are radically different to their old ones.

&x = 00404070, &y = 00404060, Difference = 4
&x = 00404070, &y = 00404060, Difference = 16

Question 4/3 - Global Variable/Array Memory Addresses

Arr is 4 bytes long.
The address of arr is 00404070
The value of arr+4 is 00404074.
The address of arr[4] is 00404074.

&x = 00404010, &y = 00404014, Difference (number of datatypes?) = -1
&x = 00404010, &y = 00404014, Difference (bytes) = -4
x = 0, y = 0

This time, we took the code from Question 3, but declared x,y, and arr[4] globally instead of locally. This time, arr >>> &y > &x. Thus the difference between &x and &y is now 4 bytes and in a different direction. Since these variables are global, the memory is statically allocated presumably from the Initiated Constants section... hence the vastly different memory addresses to Task 3.

Regardless of what the size of arr[] was or whether I addressed arr[4] (an out of bounds operation), &x - &y, x and y all remained the same. This is as expected since arr[] is no longer wedged in between x and y in memory.

Question 5 - Pointer Memory Addresses

p1 is 0022FF6C
p2 is 0022FF68
The difference in memory addresses is 4

This output is as expected. Notice that, despite q being declared before r, the memory address of q is higher, and thus, p1 has a higher value. The difference is 4 bytes as expected.

Question 6 - Lifetimes of Local/Static/Malloc Variables

When calling local_str()
sp = 0022FF40(≡F┴w)
sp X'd = 0022FF40(≡F┴w)

When calling local_str() then local_str2()
sp = 0022FF40(≡F┴w)
sp X'd = 0022FF40(≡F┴w)

When calling static_str()
sp = 00402000(tuvwxyz)
sp X'd = 00402000(XXXXXXX)

When calling malloc_str()
sp = 003D2498(hijklmn)
sp X'd = 003D2498(XXXXXXX)

My results above were what I expected. In the first two cases, sp points to local variables which no longer exist after local_str() and local_str2() execute. Hence the memory at those locations stores rubbish values. For the third case, statically declared variables outlive the function, and so sp points to s which contains a string. For the fourth case, the string will exist in the malloc stack until the free() command is used, and so this code is legitimate.

The only mystery is why using strcpy() on sp didn't alter sp for the first two cases - I'm guessing that since sp points to local memory area, strcpy is not being permitted to alter the values there.


Question 7 - Struct Memory Addresses

&my_struct = 00404060
Offsets:
my_char:0
my_short:-2
my_int:-4
my_long:-8
my_float:-12
my_double:-16

Above are my results. As expected, &my_struct is in the global area. In fact, everything is as expected with all of the structure datatypes occupying their allotted memory, except for my_char() which is allocated 2 bytes instead of 1. Having been told in lectures that structures often occupy more space than their components, this isn't really surprising.


Question 8 - Union Memory Addresses

Task 8 involved repeating Task 7 with a union instead of a struct. From the below results, it is clear that unions are placed in a similar area of memory to structs, but it seems that there is no offset between the union and its components in every example. I'm not sure why this is... perhaps the components simply aren't being allocated memory (yet).

&my_struct = 00404060

Offsets:
my_char:0
my_short:0
my_int:0
my_long:0
my_float:0
my_double:0


Question 9 - Malloc() and Free()

Code:

#include <stdio.h>

int main(void){
    char *sp1,*sp2,*sp3;
    
    sp1 = malloc(10);
    printf("sp1 lives at %p and here are its contents: %s\n\n",sp1,sp1); 

    sp2 = malloc(10);
    free(sp1);
    printf("After freeing, sp1 lives at %p and here are its contents: %s\n",sp1,sp1);
    printf("sp2 lives at %p and here are its contents: %s\n\n",sp2,sp2); 

    sp3 = malloc(10);
    printf("sp3 lives at %p and here are its contents: %s\n",sp3,sp3);   
    
    
    sp1="Insert string here";
    printf("\n\nAfter altering sp1, sp1 lives at %p and here are its contents: %s\n",sp1,sp1);
    
    getchar();
}

Results:

sp1 lives at 003D2500 and here are its contents: ¿♦=

After freeing, sp1 lives at 003D2500 and here are its contents:
sp2 lives at 003D2518 and here are its contents: É♦= 

sp3 lives at 003D2500 and here are its contents:


After altering sp1, sp1 lives at 004030CF and here are its contents: Insert string here


From this, we can see that any memory allocated using malloc() initially contains rubbish data. After calling free(sp1), sp1 still points to the same address, but the memory there is wiped clean. After freeing sp1, that memory space was free for use by sp3... hence their addresses are the same. And since sp1 still technically points somewhere, we can alter its contents without a problem.

Question 10 - Function Memory Addresses

Random global variable lives at: 00404060
Random local variable lives at: 0022FF74
Pointer to random local variable lives at: 0022FF70
Function local_str lives at: 0040133D
Function local_str2 lives at: 00401359
Function static_str lives at: 00401375
Function malloc_str lives at: 0040137F
Static variable lives at: 00404010


I added a few extra things and the output is above. Here we can see that the functions (i.e. program code) inhabit a similar area of memory to global variables... it would appear that functions are declared before anything else here. The output is essentially a summary of the memory address of C components thus far.

Conclusion

For the most part, this lab produced results which I expected once I stopped using Dev-C++. One unusual thing is that the call stack seems to occupy a much lower memory address than the global variables. From lectures, I understood it was meant to be the other way around (generally)

And there is the unsolved mystery of the 0 union offsets in Task 8.