Structs

When we are programming we don't want to have to constantly ask for memory, by asking for chars, shorts, ints, longs, etc..

What c/c++ allows us to do is to CREATE COMPOSITES of many different types of things, all of which might be differnet sizes.

When we create this composite it is in a way creating our very own new type, with it's own amount of memory. Made up of smaller things all of which may be differnt sizes.

The composites are called structs.

A struct is a very simple concept in c/c++ but it is the backbone of most of the programming that gets done.

It allows us to create a LAYOUT OF MEMORY in a prefab way that allows us to refer to it by a name and it's parts.

Defining a Struct

When you use the struct keyword , you are DEFINING the MEMORY LAYOUT you would like, you are not ASKING the compiler for memory, you are simply creating the definition of what some call an Abstract Data Type, You are essentially creating your own new type and defining the way it's MEMORY LAYS OUT.


	struct ball
	{
	    char unsigned diamater; // NOTE(professor): inches
	    float unsigned weight; // NOTE (Professor): ounces
	    int unsigned age; // NOTE(Professor): years old
	    short unsigned condition; // NOTE(Professor): rating
	};
 	

Declaring a variable of a struct type

When you declare a variable of a struct type, instead of declaring one thing, the compiler will allocate memory for all the things defined by the struct, all packed together in one place in memory, where they can be referenced by one name


	int x; // declaration for one thing
	char y; // declaration for one thing

	/* declaration for many things,
		packed together in memory,
		all under one name.*/

	struct ball Ball;  
	

Difference in Compilers

When you declare a variable, of a type that you defined using the struct keyword, when using gcc (compile in C) you must type the word struct at the time of variable declaration to allow for compilation as here.


		struct ball Ball;
	  

If you use g++, a c++ compiler you do not need to include the struct keyword you can just declare the variable like this


		ball Ball;
	  

Setting Values

To set the values of the MEMBERS or FIELDS, of a variable of a user defined struct(ADT) you can use the . operator


		  Ball.diamater = 8; 
	          Ball.weight = 14.5; 
	          Ball.age = 2; 
	          Ball.condition = Ball.age + 1; // order dependendent
	  

Lets trace throught the following code using gdb

We can use the gdb command ptype to view the type definition of Ball

We can also print the current values of all the members of Ball

Address of Ball

we can look at the address of ball using the & operator, just like any other variable.

Notice that gdb put (struct ball *) in parens before the address to show us that the name Ball is actually a pointer variable. It points to the first bytes of the packed memory that was allocated when you delcared the variable. This is the same address as the first byte of the first member defined in the struct

Structs and pointers video on twitter(X)

Size of Ball

How many bytes in memory do you think Ball should take up?

Remember the definition of Ball is below, which adds up to 11 bytes of memory


	  struct ball
	  {
	  char unsigned diamater;    1 byte
	  float unsigned weight;     4 bytes
	  int unsigned age;          4 bytes
	  short unsigned condition;  2 bytes
	  };
	  

When we ask for the sizeof Ball in GDB it says 16. But our the total bytes of our member variables only add up to 11? why?

When we ask for the address of Ball we get the address of the first byte where the compiler started to pack the fields of the struct, sequentially, one after the other. This address coincides with the first byte of the first field.

WE DON'T KNOW exactly how much space the compiler will leave in between fields. The compiler is under NO particuliar constraint to lay these things out as compactly as possible.

We expected 11 bytes but the compiler gave us 16bytes. If The compiler thinks it may be more effecient because of the way the CPU accesses things, it may choose to leave 32 bits of space for every field, even if the type of that field is smaller and use less bits for it's data. You may say this is wasting memory. But, The cpu may be slower at accessing 8bit values that are,UNALIGNED, not on an even boundary, than accessing 32 bits on an even boundary

In this case I changed the second field to a int, and a set each field to 1. This may make it alittle clearer

Notice that the 3 bytes of padding in the first field, which is a char, were left untouched. They are just there for padding and their value does not matter

I added a int varaible after Ball to see how much padding would be placed between the Ball and my new int. You can see the address of each field here as well as the address of our new int.

Casting

The big advantage of programming in C/C++ is that your in almost complete control of how things are happening with memory

C is a statically typed lanuage so C KEEPS TRACK of what you said a variable was when you declared. The compiler will warn you or give you an error if you try and use something in a way you didn't tell it as shown in the next slide.

Here we try and set a pointer to a short to the address of the Ball. C knows this address is for a ball so it gives us a warning to let us know we may have a bug in our code.

It does allow us to compile though.

However!! Once you have an address of a byte, You are NOT TIED to the type that was specified for that address for the rest of the program.

You can do what is called a cast, which tells the compiler, "Don't worry I know what I am doing, I want to POINT to this address that you consider a ball as a short (short *)

Doing this is called casting


		short* TestPointer = (short *) &Ball;

	  

The expression (short *) is called a cast and tells the compiler that I want to look at the bytes starting at that address in a different way now then I said before. To cast something You put in parentheses the type you want the thing to become.

We can take the block of memory that was allocated when we declared Ball and look at as a series of shorts(2 byte values).

If you recall, 16 bytes were allocated for four fields, a char, 2 ints and a short

In this code take the entire 16 bytes orignally allocated for a ball and change the value in 2 byte chunks

As you can see we set TestPointer to hold the address of Ball


	    short *TestPointer = (short *) &Ball;
	  
	  

We also set all 16 bytes to the value 0xFF using memset function in the string library. You should be able to write your own memset function.


		memset(&Ball, 0xFF, 16);
	  

We create a loop where we set the value that TestPointer is pointing at to 0x1010and increment the address in Test Pointer each time by the size of it's definition(2bytes).


	    for(; TestPointer < TestPointer + 16; TestPointer++)
		*TestPointer = 0x1010;
	  

What is wrong with this code ? what should this code read?

TestPointer++//increments address by 2bytes every time

And so on...

You do however still can acces the 16 bytes as as the ball struct it originally was!!!

array indexing

In many languages when you hear the term array, it means a collection of objects that are indexed by a value that is number. for example, if we want 40 balls

           struct ball Balls[40];
	  

Instead of


		struct ball Ball1
		struct ball Ball2
		struct ball Ball3
		struct ball Ball4
		// and so on
	  

Array syntax

Array syntax in C/C++ is just short hand for messing with memory

Arrays can be declared for any type, including any user defined type your create with a struct.

In C arrays are blocks of contiguous memory of the type they are declared to be.

In c/c++ when you declare an array, the compiler lays out the things in memory, spaced, at regular intervals based on the size of the type of the array you are declaring

Each number n within the, array ACCESSOR BRACKETS [] reference the nth item of the array.

the name of the array and the first index, array[0] both reference the same address.

The address of the nth item is calculated as follows (index * sizeof(type) + (address of first item).

This calculation is done automatically for you by the compiler

You can obtain the address of the arrays first byte either by using, the name of the array, or &Array[0]

examples: Given an array of int named Array


		int Array[20]; // 20 integers starting at address Array.
	  

If Array is at Addrss 10, then, Array[2] is at address 18

In other languages arrays may only be conceptual in nature. They very well may not lay in memory in a single contiiguous block, one item after the next. In C/C++ arrays are guaranteed by the standard to be layed out in memory following these conventions.

ARRAY NAMES in C are symbols that mantain the address of the first index in memory of the array. We can take a pointer and point it to that address


		int Array[40];
		*int ArrayPointer = Array;    // these pointers hold this same address.
		*int ArrayPointer2 = &Array[0];  
	  

This gives you a pointer to Array[0], you can use the ACCESSOR BRACKETS with ArrayPointer and the compiler will do the address calculation for your or you can reference different indexes by typing ArrayPointer + someIndex


		Array[3] == ArrayPointer + 3 == ArrayPointer2[3];
	  

sizeof operator on arrays and arraypointers

Although an array name and a pointer (to the same address of the same type) can operate in the same way, C mantains the name of the array and the pointer as two different kinds of things.

If you use the sizeof operator on an array name it will return the total number of bytes allocated for the array.

If you use the size of operator on the pointer that points the same address as the array name, it will return whatever the standard size is of an address on your system.

please note the c compiler treats the followint two things as the same


	    int Array[30];
	    printf("%p\n", Array);
	    printf("%p\n", &Array);
	  

Arrow syntax

If you define a struct as follows. You can declare a variable of that type or you can declare a pointer to that type and set the address in the pointer to the first bytes of the memory where that struct is being stored


		struct projectile 
		{
		    char location;
		    int speed;
		    int activation;
		    short label;
		};

		struct projectile P;  //
		struct projectile *ProjectilePointer = &P;
	    

To access any field of the struct usig the pointer you have to use the -> arrow operator instead of the . dot operator


	     /* The . operator and the -> operator
		do exactly the same thing.
                One is used with a regular struct variable
		and one is used with a pointer to a struct
             */

	     P.location = 'A';
             ProjectilePointer->location = 'A'

	     P.speed = 100;
	     ProjectilePointer->speed = 100;	

	    

Mathematical Operators

In c we have all the regular math operators

  • " = " means set some memory to some value
  • lh = rh mean set the memory at lh to the value of rh
  • rh could be any combination of mathamatical operations with the operators, " + * - / " that act on variables or constants
  • There is no exponent operator.
  • Operator precedence follows common math operation precedence rules
  • For readability it is adviseable to show order of operatoins with parens or to use spaceing like this x + y*z + a/b - 3

Bitwise Operators

Sometimes we want to manipulate individual bits directly.

With bit wise operators we often use Hex notation because hex digits line up perfectly with binary

1 hex digits is 4 bits.


		char x = 0xA // will assing 00001010 to location of x in memory
	    

next week

  • argc, argv, arguments to main
  • String pointers