• No results found

Pointers to Structures

In document C notes (Page 124-127)

15 Dynamic Memory Allocation

16.4 Pointers to Structures

Just as you can declare pointers to integers, oating point numbers and characters, you can also declare pointers to structures:

Student *pstd

University *puni

Complex *pcomp

Here the *(star) notation tells the compiler that a pointer is being declared rather than a variable and the pat the start of the name is just to remind you that this is a pointer.

Memory can be allocated to a structure usingmalloc()in the following manner:

pstd = (Student *) malloc(sizeof(Student))

pcomp = (Complex *) malloc(20*sizeof(Complex))

which reserves memory for one Studentand an array of 20 variables of type Complex. Calls to free()deallocate this memory:

free(pstd)

free(pcomp)

Note that you can have pointers to structures as members of other structure. For instance, it would have been better to declare stdas a pointer to aStudent, rather than as an array in the denition of University. This would then allow the correct amount of memory to be allocated at run-time rather than xing its size at compile-time. To declare a structure as a member of another structure, the former needs to be declared before the latter as memory has to be allocated when a variable of that type is declared. However, you can declare a pointer to a structure that hasn't been dened and this is because all pointers use up the same amount of memory. Structures can even contain pointers to themselves, and this is a common technique for constructing linked lists in C.

16.4.1 Accessing Members

If you have declared a pointer to a structure and allocated memory to it with a call to

malloc(), its members can be accessed using the arrow ->notation (a hyphen followed by a greater than character). This is illustrated in the following code segment:

Complex *pcomp

pcomp = (Complex *) malloc(sizeof(Complex))

pcomp->real = 3.0

pcomp->im = -5.0

Instead of using a dot to reference the member of a structure, an arrow is used instead, and this is because the two following expressions are equivalent:

pcomp->real (*pcomp).real

The dereferencing *operator means that the pointer can now be treated as a true variable, and the dot operator can be used to access the member. However, because the dot operator has a higher precedence than the star operator, the rst part of the expression needs to be placed in parentheses. This notation is obviously cumbersome, so the arrow notation is used instead.

Although C provides a mechanism to copy and assign structures, pointers are generally used to transfer information instead. Note that the only way to pass an array to a function is via pointers. The reason for this is that structures and arrays generally contain a lot of data and to copy all of the variables each time a function is called is slow. Pointers are used to pass the location of the array/structure in memory and this is obviously much quicker, although sometimes the notation can be confusing!

16.4.2 Initialising and Destroying Structures

Just like functions are used to hide the complexity of a procedure, structures are used to hide the complexity inherent in the data. For instance, you may write a function that implements a Newton-Raphson calculation and the user of such a function does not need to understand how Newton-Raphson works. Similarly, you may dene a structure of typeStudentwith a set of well-dened interface functions and the user of this data type would not need to understand how this has been implemented. In practice, both of these techniques are used to design large programs as a programmer can work on one small subtask at one time, and it unnecessary to understand the whole problem. In fact, problem statements are often so poorly dened that the programmer must enter an interactive design cycle, as discussed in the next section.

So the ability to encapsulate both procedures and data into smaller, well-dened parcels is extremely good programming style.

Once a pointer to a structure is dened, it is usual to declare and dene initialisation and destroying functions that are called just after it is declared and before it goes out of scope. As these are interface functions (analogous tofopen() and fclose()), it is common to declare them in the header le just after the structure is dened.

For theStudent structure, these functions would be declared as:

Student *Student_init(char *name, int age, int mark1, int mark2, int lectures_missed, double bank_balance)

void Student_destroy(Student *pstd)

and dened something like:

/* Allocate memory and initialise a Student variable

* using the arguments supplied. Returns a pointer

* to allocated memory.

*/

Student *Student_init(char *name, int age, int mark1, int mark2, int lectures_missed, double bank_balance) {

Student *pstd

/* reserve space for a type Student */

pstd = (Student *) malloc(sizeof(Student))

assert(pstd != NULL)

/* reserve space for *name */

pstd->name = (char *) malloc((strlen(name)+1)*sizeof(char))

assert(pstd->name != NULL)

strcpy(pstd->name, name) /* copy string */

/* copy remaining variables */

pstd->age = age

pstd->bank_balance = bank_balance

pstd->assn_marks0] = mark1

pstd->assn_marks1] = mark2

pstd->no_lectures_missed = lectures_missed

return pstd /* return pointer to reserved memory */

}

/* Free the memory allocated to a pointer of type

* Student by first releasing name and then pstd.

*/

void Student_destroy(Student *pstd) {

free(pstd->name)

free(pstd)

return

}

Even when a variable is declared it is common to dene initialisation and freeing functions that allocate and free any internal dynamic memory.

Functions and structures are generally grouped together in large programs and a set of functions are written to manipulate (initialise, delete, modify) each structure, where the structure and its functions are generally grouped together in a separate source le, with the header le specifying this interface. This structured paradigm then leads naturally onto Object-Oriented programming, and C++, which is the most widely used OO language.

16.4.3 Structures and Functions

The nal reason for using structures is that their use can hide the amount of data being passed between functions. Instead of having to pass all the members individually, only a single variable (or a pointer to that variable) needs to be passed. This reduces the chance of making typing errors or putting the function's arguments in the wrong order as this is hidden in the structure's denition. Also, if you add a new member to a structure, the function's interface will not change, as it will be implicitly passed across along with the rest of the structure. This feature means that C supports an incremental programming strategy where the motto is:

Don't break working code.

Code should be designed so that the minimum amount of changes are necessary when a new feature is added.

There are two ways to declare an instance of a structure: either as a variable or as a pointer, and many people are confused over which one is best. The rule of thumb is that if you're structure is large or contains pointers or arrays, declare a pointer to that structure

and dene your own initialisation functions, otherwise just declare a normal variable and use C's inbuilt copying and assignment rules.

In document C notes (Page 124-127)