C Structures

Leading up to a post on working with CGRect, CGPoint and CGSize, it makes sense to visit C structures. A structure is a collection of variables, grouped together to facilitate organization of data. For example, one might define a set of x and y coordinates as follows:

Creating a Structure

1
2
3
4
struct point{
  int x;
  int y;
};

With this definition in place, we can create new instances of a point as shown below. Also, notice how structure members are accessed using dot syntax.

1
2
3
4
5
6
7
8
9
// Allocate, then initialize
struct point topLeft;
topLeft.x = 15, topLeft.y = 20;
 
// Allocate and initialize in one step
struct point lowerRight = {100, 200};
 
NSLog(@"x: %d y: %d", topLeft.x, topLeft.y);
NSLog(@"x: %d y: %d", lowerRight.x, lowerRight.y);

Typedef

We can add a typedef (type definition) to create a more convenient data type name. Look at the code that follows:

1
2
3
4
5
struct point{
  int x;
  int y;
};
typedef struct point POINT;

Now, we can create new points as shown here:

1
2
3
4
5
6
7
8
9
// Allocate, then initialize
POINT topLeft;
topLeft.x = 15, topLeft.y = 20;
 
// Allocate and initialize in one step
POINT lowerRight = {100, 200};
 
NSLog(@"x: %d y: %d", topLeft.x, topLeft.y);
NSLog(@"x: %d y: %d", lowerRight.x, lowerRight.y);

It’s important to note, a typedef is nothing more than a name for an existing type, there is no new data type created.

Nested Structures

We can also nest structures, below I create a definition for a rectangle, by using two points:

1
2
3
4
5
6
7
8
9
10
11
struct point{
  int x;
  int y;
};
typedef struct point POINT;
 
struct rectangle {
  POINT topLeft;
  POINT lowerRight;
};
typedef struct rectangle RECTANGLE;

Here’s how one might use the rectangle structure.

1
2
3
RECTANGLE box = {100, 100, 200, 300};
NSLog(@"x: %d y: %d", box.topLeft.x, box.topLeft.y);
NSLog(@"x: %d y: %d", box.lowerRight.x, box.lowerRight.y);

That should cover what we need to look a little further into the structures CGRect, CGPoint and CGSize in another post.

  1. Good information John.

    There are a couple other things that are good to know with structs. A struct can have one or more “declarators” which allow you to define and declare in one step, as follows:

    struct MyStruct
    {
    int a;
    int b;
    } myStruct1, myStruct2, myStruct3;

    The declarators declare instances of the struct which are “declared” at the point of definition of the struct itself. You can also declare pointers and arrays, or even mix and match, as follows:

    struct MyStruct
    {
    int a;
    int b;
    } *myStructPointer1, myStructArray[10], **myStructPointerToPointer;

    You may also add initializers to the declarators:

    struct MyStruct
    {
    int a;
    int b;
    } myStruct1 = { 256, 128};

    Which will initialize a to 256 and b to 128.

    It is also useful to consider how structs are passed to functions. There are basically two ways to do this, by value or by pointer. Consider the following method signature:

    void someMethod(struct MyStruct aStruct);

    If this method is called as follows:

    MyStruct myStruct = {0, 0};
    someMethod(myStruct);

    This declares and initializes myStruct, and then passes it by value to someMethod(). Essentially what will happen is that the compiler will generate code to ensure that a copy of the original myStruct is made (typically on the stack), and then this copy will be passed to someMethod. Note that if someMethod() makes any modifications to the date in the struct, those changes will (in this case) be lost when the method returns because someMethod() is working on a copy.
    An alternative is to pass a pointer to the struct. Consider this alternative method signature:

    void someMethod2(struct MyStruct *myStructPointer);

    This takes a pointer to a MyStruct. We might then use this sequence:

    MyStruct myStruct = { 0, 0 };
    someMethod2(&myStruct);

    Notice that we’ve used the “address of” operator “&” to take the address of our struct and pass this to the method. This skips the stack copy (which could be unwanted overhead if the struct is large) and also has the side effect that someMethod2() can now modify the original struct (which may or may not be what you want – you can use const modifiers on the parameters to provide “read only” pointers, in this case const struct MyStruct*).
    It is also worth noting that the syntax to dereference a pointer to a struct would be as follows:

    void someMethod2(struct MyStruct *myStructPointer)
    {
    int a = myStructPointer->a;
    int b = myStructPointer->b;
    myStructPointer->a = 1;
    myStructPointer->b = 2;
    }

  2. Glad to see this. It’s a nice refresher now that I’m getting into Objective-C, seeing as I haven’t done any serious C work in 10 years or more.

  3. Using a struct looks very convenient. Interesting how Steve points out some potential gains with respect to memory utilization.

    Are there any other memory related advantages though in comparison to declaring an Objective-C class with a .m and .h file?

  4. @Sven: With structs you have better control over memory packing and allocation alignment, which comes in handy with large arrays.

    If you have an array of structs vs an array of objects, you can do things like allocate your struct array memory aligned to a page boundry, then pack your structs to take advantage of the cache line size. This helps speed things up memory access – like if you have a large number of elements in a struct or object array that you have to walk through.

    With a struct, you can order your variables (and pointers to functions) based on frequency of use. If you have a variable in your struct that is accessed much more than the rest, if it is first you can avoid one addition operation each time it is accessed.

    Because you can pack your stuct as you see fit (with some limitations), you can process it like any other memory field (sort of like a union). For example:

    – – –

    typedef struct pixel { uchar8 r,b,g,a }; /* 4 bytes, one for each color + alpha */
    unsigned int *screenAsInt;

    pixel screen[20*20]; /* allocate a 20×20 bitmap… (questionable to put on the stack) */
    screenAsInt = (uint32 *)screen;

    /* roll our own SIMD to set each pixel to 0x00 (black) */
    f=20*20; /* counter */
    do {
    f–;
    *screenAsInt=0x00; /* this sets all four chars to 0x00 */
    screenAsInt++; /* go to the next pixel */
    while (f>0);

    /* set first pixel to red */
    screen[0].red=255;

    – – –

    This is really nice for speeding up code. You can get huge performance gains for graphics and audio processing, while still having the convenience of element access with stucts.

    Also, (at least with C++, I’m just learning ObjC, but think it’s the same), every method automatically has at least one more calling argument added for ‘this’. CPUs have a limited number of registers for arguments, and when they run out the compiler allocates and passes arguments on the stack (which is much slower). This is only a problem when the object pointer is passed and not used.

    Another trick you can do with structs: You can have pointers to functions in stucts, but unlike method pointers in classes, you can change those pointers dynamically (though you can have the same thing in C++ and probably ObjC, it’s less efficient.) For Example:

    – – –

    typedef struct shape {

    (void *) renderMe(void *this);

    }

    shape transformer;

    transformer.renderMe = &drawCar;
    transformer.renderMe(&transformer); /* draw as car */

    transformer.renderMe = &drawRobot;
    transformer.renderMe(&transformer); /* draw as robot (same function call) */

    – – –

    Also, it is easier to share struct data between programs compiled with different compilers, as long as you pack your struct correctly.

  5. Great idea for a post!
    Just a note, instead of this:
    struct point{
    int x;
    int y;
    };
    typedef struct point POINT;

    You can also do this:
    typedef struct {
    int x;
    int y;
    } POINT;

    Which makes things a bit more concise.

    Also a note on NS_ENUM would be good here as in obj-c I usually use enums more even than C structs.

Comments are closed.