SE250:lab-2:bvic005

From Marks Wiki
Jump to navigation Jump to search

The point of this lab was to investigate the memory structure used by C when programs are run.


Task 1

This task was to use the "sizeof()" function to determine the size of various data types.

The code i used:

int task1(void) {

	char *charp;
	short *shortp;
	int *intp;
	long *longp;
	float *floatp;
	double *doublep;

	char char1;
	short short1;
	int int1;
	long long1;
	float float1;
	double double1;

	printf("char pointer: %d\n", sizeof(charp) );
	printf("short pointer: %d\n", sizeof(shortp) );
	printf("int pointer: %d\n", sizeof(intp) );
	printf("long pointer: %d\n", sizeof(longp) );
	printf("float pointer: %d\n", sizeof(floatp) );
	printf("double pointer: %d\n\n", sizeof(doublep) );

	printf("char: %d\n", sizeof(char1) );
	printf("short: %d\n", sizeof(short1) );
	printf("int: %d\n", sizeof(int1) );
	printf("long: %d\n", sizeof(long1) );
	printf("float: %d\n", sizeof(float1) );
	printf("double: %d\n", sizeof(double1) );

	return 0;
}

I compiled this code with gcc on windows and on the linux server, and got these results for both:

char pointer: 4
short pointer: 4
int pointer: 4
long pointer: 4
float pointer: 4
double pointer: 4

char: 1
short: 2
int: 4
long: 4
float: 4
double: 8

The linux server had identical results which was unexpected, as it is 64 bit, so i was expecting 64bit pointers. However, it returned 4 byes(32bits), the same as the 32bit windows machine.

For both machines, all pointers are the same size.


Task 2

This task was to work out the difference between addresses of two ints and two doubles. ie, the space an data type takes up in the memory.

My Code:

int task2(void) {

	int x, y;

	printf("&x = %p, &y = %p, diff = %ld, diff = %ld\n", &x, &y, (long)(&x-&y), (long)&x - (long)&y );

	return 0;

}

Linux Results:

&x = 0xff95372c, &y = 0xff953728, diff = 1, diff = 4

Windows Results:

&x = 0x22cca4, &y = 0x22cca0, diff = 1, diff = 4

As you can see, linux and windows put the variables in different "parts" of their memory, but assign it in the same way. The addresses are assigned next to each other, going "backwards". The first difference result shows one, as when c compares addresses, it scales them by the size of the data they are pointing to. Hence, the two variables are 1 int apart. The second difference comes from forcing c to convert the addresses to doubles before comparing them, so it shows that the two variables are 4 bytes apart.


Task 3

This task investigates what happens when a char array is initalised between the x and y ints from the previous task.

My Code:

int task3(void) {

	int x;
	char arr[4];
	int y;

	printf("size of array = %d &arr = %p arr+4 = %p &arr[4] = %p\n", sizeof(arr), &arr, arr+4, &arr[4] );

	printf("&x = %p, &y = %p, diff = %ld, diff = %ld\n", &x, &y, (long)(&x-&y), (long)&x - (long)&y );

	return 0;

}

Linux results:

size of array = 4 &arr = 0xffa0d728 arr+4 = 0xffa0d72c &arr[4] = 0xffa0d72c
&x = 0xffa0d72c, &y = 0xffa0d724, diff = 2, diff = 8

Windows results:

size of array = 4 &arr = 0x22cca0 arr+4 = 0x22cca4 &arr[4] = 0x22cca4
&x = 0x22cca4, &y = 0x22cc9c, diff = 2, diff = 8

This shows that the array is being assigned to the same memory stack as the ints are, and is in fact between them. The size of the array shows up as 4 bytes, which is expected, as it is the size of all the chars in the array together(4 times 1 byte). The difference between the two ints has also increased by 4 bytes, from 4 to 8.

The arr+4/&arr[4] addresses are calling outside the boundaries of the array(which goes from 0 to 3), and interestingly enough, it is resulting in the address for the previous int(x), not the next one(y) as would be expected. This is presumably because while the memory sections are being allocated "backwards", C is reading addresses in the array memory space "forward".

I then varied the size of the array from 0 to 10, and recorded the difference between the two ints:

array size -> diff(linux), diff(windows)
0 -> 4, 32
1 -> 4, 8
2 -> 4, 8
3 -> 4, 32
4 -> 8, 8
5 -> 4, 32
6 -> 4, 32
7 -> 4, 32
8 -> 4, 16
9 -> 4, 32
10 -> 4, 32

These results are rather interesting. It seems that linux does not always assign memory in the order it is asked for. For all sizes of array other than 4, the array was assigned memory after the two ints, despite being declared between them, resulting in the 4 byte difference. Windows, on the other hand, always game the array memory between the ints. This memory was not the same size as the size of the array, however. It varied considerably.

Finally, i reset the array size to 4, assigned 0 to x and y, and then assigned arr[4] to 10. As expected(see above observations on arr[4] address), x printed out as 10, rather than 0.


Task 4

In task 4, we just had to shift x and y from the previous two tasks to global variables.

Linux results:

size of array = 4 &arr = 0x10010ee4 arr+4 = 0x10010ee8 &arr[4] = 0x10010ee8
&x = 0x10010ee0, &y = 0x10010ee8, diff = -2, diff = -8

Windows results:

size of array = 4 &arr = 0x403030 arr+4 = 0x403034 &arr[4] = 0x403034
&x = 0x403040, &y = 0x403020, diff = 8, diff = 32

In linux, the now global variables are now allocated in a different area of memory, and are now allocated forward. In windows, they are also allocated in a different area, but they are still allocated backward, and the size of memory for the array is again larger than the size of the array.

Machine Change

This is all i have had time to do in the lab session. I have done all that follows at home, using cygwin/gcc on my 32bit windows machine, and the shell.ece.auckland.ac.nz(as login.cs.auckland.ac.nz was extremely slow from outside uni) 64bit linux server via ssh.

Interestingly, when i run task1 on the shell.ece server, it shows 64bit(8byte) pointers, as you would expect for a 64bit machine, but what i didn't get on the login.cs machine. Also, the "long" type is 64bits on the shell.ece machine, rather than 32bits. This is probably because the login.cs machine is ppc, whereas the shell.ece machine is x86.

Task 5

code:

int task5(void) {

	int *p1, *p2;
	
	int q; p1 = &q;
	int r; p2 = &r;
	
	printf("p1 = %p p2 = %p &q = %p &r = %p\n", p1, p2, &q, &r);

	return 0;

}

Linux results:

p1 = 0x7fffffc4c4e8 p2 = 0x7fffffc4c4e0 &q = 0x7fffffc4c4e8 &r = 0x7fffffc4c4e0

Windows results:

p1 = 0x22ccb8 p2 = 0x22ccb0 &q = 0x22ccb8 &r = 0x22ccb0

As expected, p1 equals the address, of q, and likewise for p2 and r. I tried the code with the curly braces, as shown in the handout, but the compiler threw errors about q and r having not been declared when it reached the print line. I'm not sure what was supposed to happen with the braces, it it seemed to just make the compiler ignore the contents of the braces.

Task 6

In this task we had to assign variables in various ways, and then try and print them.

Code:

char *local_str( ) {
	char s[8] = "0123456";
	return s;
}
char *local_str2( ) {
	char s[8] = "abcdefg";
	return s;
}
char *static_str( ) {
	static char s[8] = "tuvwxyz";
	return s;
}
char *malloc_str( ) {
	char *s = malloc( 8 );
	strcpy( s, "hijklmn" );
	return s;
}

int task6(void) {
	char *sp;

	sp = local_str( );
	printf( "sp = %p(%s)\n", sp, sp );
	
	sp = local_str( );
	local_str2( );
	printf( "sp = %p(%s)\n", sp, sp );
	
	sp = static_str( );
	local_str2( );
	printf( "sp = %p(%s)\n", sp, sp );
	
	sp = malloc_str( );
	local_str2( );
	printf( "sp = %p(%s)\n", sp, sp );
	
	return 0;
}

When compiled, gcc threw up some warnings, mainly about local variables being passed back from a function.

Linux results:

sp = 0x7fffffef4ce0(0123456)
sp = 0x7fffffef4ce0(abcdefg)
sp = 0x601228(tuvwxyz)
sp = 0x602010(hijklmn)

Despite local variables not being kept after a function returns, the data from local_str still seems to be there when the address is read. When local_str2 is called after local_str, the local string in it must be assigned the memory previously allocated to the string from local_str, hence why the address now contains the info from local_str2.

The data from the static a malloc variables are still there.

Windows results:

sp = 0x22cca0(456)
sp = 0x22cca0(efg)
sp = 0x402000(tuvwxyz)
sp = 0x670178(hijklmn)

The windows results show the same thing as the linux, but some of the local variable data seems to be lost, presumably because the memory space is not actually reserved for that data anymore.

We then had to add code to assign a string of X's to the memory address used by the variables/

Code:

char *local_str( ) {
	char s[8] = "0123456";
	return s;
}
char *local_str2( ) {
	char s[8] = "abcdefg";
	return s;
}
char *static_str( ) {
	static char s[8] = "tuvwxyz";
	return s;
}
char *malloc_str( ) {
	char *s = malloc( 8 );
	strcpy( s, "hijklmn" );
	return s;
}

int task6(void) {
	char *sp;

	sp = local_str( );
	printf( "sp = %p(%s)\n", sp, sp );
	strcpy( sp, "XXXXXXX" );
	printf( "sp X’d = %p(%s)\n", sp, sp );
	
	sp = local_str( );
	local_str2( );
	printf( "sp = %p(%s)\n", sp, sp );
	strcpy( sp, "XXXXXXX" );
	printf( "sp X’d = %p(%s)\n", sp, sp );
	
	sp = static_str( );
	local_str2( );
	printf( "sp = %p(%s)\n", sp, sp );
	strcpy( sp, "XXXXXXX" );
	printf( "sp X’d = %p(%s)\n", sp, sp );
	
	sp = malloc_str( );
	local_str2( );
	printf( "sp = %p(%s)\n", sp, sp );
	strcpy( sp, "XXXXXXX" );
	printf( "sp X’d = %p(%s)\n", sp, sp );
	
	return 0;
}

Linux results:

sp = 0x7fffff982660(0123456)
sp Xd = 0x7fffff982660(XXXXXXX)
sp = 0x7fffff982660(abcdefg)
sp Xd = 0x7fffff982660(XXXXXXX)
sp = 0x601228(tuvwxyz)
sp Xd = 0x601228(XXXXXXX)
sp = 0x602010(hijklmn)
sp Xd = 0x602010(XXXXXXX)

This shows once again that on linux while the memory is no longer reserved for the local strings, it can still be used as if it where while it has not been allocated to something else.

Windows results:

sp = 0x22cca0(456)
sp X'd = 0x22cca0('1@)
sp = 0x22cca0(efg)
sp X'd = 0x22cca0('1@)
sp = 0x402000(tuvwxyz)
sp X'd = 0x402000(XXXXXXX)
sp = 0x670178(hijklmn)
sp X'd = 0x670178(XXXXXXX)

Once again(again), on windows, the memory range cannot be used reliably once it is no longer allocated to the local strings, hence the gibberish in returned instead of X's.

Task 7

In this task we had to define a structure containing a variety of data types, and map where each was assigned relative to the start of the structure.

Code:

struct {
	char my_char;
	short my_short;
	int my_int;
	long my_long;
	float my_float;
	double my_double;
} my_struct;

int task7(void) {
	printf( "&my_struct = %p\n", &my_struct );
	printf( "offsets:\n"
	"my_char: %ld\n"
	"my_short: %ld\n"
	"my_int: %ld\n"
	"my_long: %ld\n"
	"my_float: %ld\n"
	"my_double: %ld\n",
	(long)&my_struct - (long)&my_struct.my_char,
	(long)&my_struct - (long)&my_struct.my_short,
	(long)&my_struct - (long)&my_struct.my_int,
	(long)&my_struct - (long)&my_struct.my_long,
	(long)&my_struct - (long)&my_struct.my_float,
	(long)&my_struct - (long)&my_struct.my_double );
	
	return 0;
}

Linux results:

&my_struct = 0x601360
offsets:
my_char: 0
my_short: -2
my_int: -4
my_long: -8
my_float: -16
my_double: -24

Windows results:

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

On both windows an linux, the memory addresses of each variable are assigned is order, and are the same size as the actual variable, apart from the char which is 2bytes, rather than 1 byte. The only difference between the two machines is that the long on the 64bit linux is 8 bytes long, rather than 4 bytes long, so the offsets for the linux float and double are 4 bytes more than on windows.

Task 8

In this task we had did the same as task 7, but with a union rather than a struct

Code:

union {
	char my_char;
	short my_short;
	int my_int;
	long my_long;
	float my_float;
	double my_double;
} my_union;

int task8(void) {
	printf( "&my_union = %p\n", &my_union );
	printf( "offsets:\n"
	"my_char: %ld\n"
	"my_short: %ld\n"
	"my_int: %ld\n"
	"my_long: %ld\n"
	"my_float: %ld\n"
	"my_double: %ld\n",
	(long)&my_union - (long)&my_union.my_char,
	(long)&my_union - (long)&my_union.my_short,
	(long)&my_union - (long)&my_union.my_int,
	(long)&my_union - (long)&my_union.my_long,
	(long)&my_union - (long)&my_union.my_float,
	(long)&my_union - (long)&my_union.my_double );
	
	return 0;
}

Linux results:

&my_union = 0x601450
offsets:
my_char: 0
my_short: 0
my_int: 0
my_long: 0
my_float: 0
my_double: 0

Windows results:

&my_union = 0x404050
offsets:
my_char: 0
my_short: 0
my_int: 0
my_long: 0
my_float: 0
my_double: 0

On both windows an linux, there now appears to be no space allocated for the variables within the union.

Task 9

In this task we investigated what happens to malloc memory space when it was freed.

Code:

int task9(void) {
	char *sp1, *sp2, *sp3;
	
	sp1 = malloc(10);
	sp2 = malloc(10);
	printf("sp1 = %p sp2 = %p\n", sp1, sp2);
	free(sp1);
	sp3 = malloc(10);
	printf("sp1 = %p sp2 = %p sp3 = %p\n", sp1, sp2, sp3);
	
	strcpy( sp1, "abcdefghi" );
	
	printf("sp1 = %s\n", sp1);
	printf("sp3 = %s\n", sp3);
	
	return 0;
}

Linux results:

sp1 = 0x602030 sp2 = 0x602050
sp1 = 0x602030 sp2 = 0x602050 sp3 = 0x602030
sp1 = abcdefghi
sp3 = abcdefghi

Windows results:

sp1 = 0x670188 sp2 = 0x670198
sp1 = 0x670188 sp2 = 0x670198 sp3 = 0x670188
sp1 = abcdefghi
sp3 = abcdefghi

When sp1's memory is freed, it is then assigned to sp3. the address in the sp1 pointer is retained, however, so sp1 can still be used. It just points to the same memory as sp3.

Bvic005 12:28, 11 March 2008 (NZDT)