Chapter 16
Programmer-Defined Data Types
16.1 Struct and Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
16.2 Passing Objects as Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
16.3 Objects and Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
16.3.1 Returning an Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
16.3.2 Objects and malloc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
16.4 Constructors and Destructors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
16.5 Structures within Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
16.6 Binary Files and Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
Section 7.1.2 mentioned several reasons for creating header files (.h files), including defining
constants using #define, declaring functions, and defining new data types—usually referred
to as “types”. This chapter explains how to define new types using structures. First let us
consider what makes up a type by thinking about the differences between int and double.
A type specifies:
The format for the data. For example, integers and double-precision floating-point
numbers are represented differently in the computer’s memory.
The range of possible values, and the size required to store the data. A 4-byte integer
stores valid values that are between the range of 2,147,483,648 (2
31
, approximately
10
9
) and 2,147,483,647 (2
31
1). In contrast, the absolute value of a double-precision
floating number (8 bytes) can be as small as 10
308
and as large as 10
308
(approxi-
mately).
The effect of operations on the data. Because the two types have different formats,
when a program has a statement a + b, the actual operations depend on whether a
and b are integers or floating-point numbers. Also, some operations are restricted to
certain data types. For example, switch must be used with an integer; double cannot
be used in switch statements.
C also supports other data types, such as char and float. C does not have a separate
type for strings; instead, C uses null-terminated arrays of char for strings, as explained in
Chapter 6. A natural question is whether C allows programmers to create new types. The
answer is yes.
Why would programmers want to create new types? The most obvious reason is to put
related data together. For example, a college student has a name (string), year (integer),
grade point average (floating-point), and so on. C programmers would have to do something
awkward if C did not allow the creation of new types. For example, separate arrays could
be created to store the students’ data: an array of strings for student names, an array of
integers for years, an array of floating-point numbers for scores, etc. There is no good way
to associate the elements in different arrays. There is no good way to ensure that the arrays
have the same numbers of elements. Therefore, supporting programmer-defined types is
essential.
249
250 Intermediate C Programming
16.1 Struct and Object
When multiple pieces of data are organized together, it is a structure. A structure is a
type (similar to int or char). After creating the structure, the type can be used to create
an object (borrowing a term from C++ and Java). An object is a specific instance of a type.
For example, “bicycle” is a type describing the properties: two wheels, gears, etc. A given
bicycle is an instance that has these properties. Borrowing another term used in C++ and
Java, we call each piece of data an attribute. For a bicycle, the wheel size is an attribute.
The brand is another attribute. These three terms are further described below:
Structure: a data type so that multiple pieces of data can be organized together. The
piece may have different types (int, char, or even other structures).
Object: a specific instance of a structure. In the example below, int and t are both
types. Suppose t is a structure. Using these types, x is a specific instance of integer,
and y is a specific instance of type t.
int x;1
t y ;2
Attribute: A structure organizes different pieces of data together. Each piece of data
is referred to as an attribute. The attributes store the information specific to the
particular object. The attributes’ values of different objects are likely different.
It is important to clearly distinguish these three concepts (structure, object, and at-
tribute). Here are some more examples:
1. “Person” can be a structure. Individual people are specific instances of Person. Let’s
call two specific people: “Alice” and “Bob”. Their names, age, height, and phone
numbers are attributes. The attributes of “Alice” are different from those of “Bob”.
They have the same attributes, but those attributes have different values, and are
stored in different locations in memory.
2. “Car” is a structure. Every car has some attributes, such as year, brand, and color.
Your car is a particular instance and it is an object. That means it has a particular
year, brand, and color that can be different from my car.
3. “Desk” is a structure. Every desk has attributes, such as width, height, number of
drawers, weight, material, etc. The desk in your office is an instance. It is a particular
desk with specific values for those attributes that can be different from the desk at
your home.
Here is another way to think about the relationship between structure and object: A
structure describes what attributes (age, height, color, etc.) an object has. An object has
specific values for those attributes that can be distinguished from any other object of the
same structure. Furthermore, an object is stored in memory somewhere. A structure has no
values for its attributes, it is not stored in memory.
Now that we know what structures, objects, and attributes are, how do we create them?
Below is an example that creates a new type for vectors. A vector has three components:
x, y, and z. It is desirable to create a new type called Vector and put these attributes
together. Programmers often create new structure types by using typedef struct. The
structure’s name is given at the end of the structure, after } and before ;.
// vector . h1
#i f n d e f VECTOR_H2
#d ef in e VECTOR_H3
typedef s t ru ct4
{5
Programmer-Defined Data Types 251
int x;6
int y;7
int z;8
} Vector ; /* don t forget ; */9
#e ndif10
The type begins with typedef struct, which tells gcc that a new type is defined here.
This type contains multiple attributes and they form a structure. After the closing brace,
Vector is the name of the new type. Remember to add the semicolon after the name. This
book adopts the naming convention of using a capital letter to start the name of a structure.
It is common to have only one structure in each header file and the file’s name is the same as
the structure’s name, but in lowercase. Thus, the structure Vector is defined in the header
file vector.h.
In the following program, v1 is a Vector object. This object has three attributes. To
access an attribute, a period is needed after v1, such as v1.x and v1.y.
// vector . c1
#in clude < stdio .h >2
#in clude < stdlib .h >3
#in clude < string .h >4
#in clude " vector .h "5
int main ( i n t argc , char * argv [])6
{7
Vector v1 ;8
v1 .x = 3;9
v1 .y = 6;10
v1 .z = -2;11
printf (" The vector is (% d, %d , % d) . n ",12
v1 .x , v1 .y , v1 .z) ;13
return EXIT _SUCCES S ;14
}15
The program’s output is shown below:
The vector is (3, 6, -2).
What does line 8 actually do? It creates an object on the call stack.
Symbol Address Value
v1.z 108 garbage
v1.y 104 garbage
v1.x 100 garbage
The type Vector requires three integers and the call stack stores those three integers.
Each attribute occupies 4 bytes (assuming that sizeof(int) is 4). The attributes are not
initialized and the values are garbage. Line 9 changes the value at address 100 to 3.
Symbol Address Value
v1.z 108 garbage
v1.y 104 garbage
v1.x 100 garbage 3
A Vector object can be copied to another Vector object, as the following example
illustrates:
252 Intermediate C Programming
// vector2 .c1
#in clude < stdio .h >2
#in clude < stdlib .h >3
#in clude < string .h >4
#in clude " vector .h "5
int main ( i n t argc , char * argv [])6
{7
Vector v1 ;8
v1 .x = 3;9
v1 .y = 6;10
v1 .z = -2;11
printf (" The vector is (% d, %d , % d) . n ",12
v1 .x , v1 .y , v1 .z) ;13
Vector v2 = {0};14
printf (" The vector is (% d, %d , % d) . n ",15
v2 .x , v2 .y , v2 .z) ;16
v2 = v1 ;17
printf (" The vector is (% d, %d , % d) . n ",18
v2 .x , v2 .y , v2 .z) ;19
v1 .x = -4;20
v2 .y = 5;21
printf (" The vector is (% d, %d , % d) . n ",22
v1 .x , v1 .y , v1 .z) ;23
printf (" The vector is (% d, %d , % d) . n ",24
v2 .x , v2 .y , v2 .z) ;25
return EXIT _SUCCES S ;26
}27
The program’s output is:
The vector is (3, 6, -2).
The vector is (0, 0, 0).
The vector is (3, 6, -2).
The vector is (-4, 6, -2).
The vector is (3, 5, -2).
Line 14 creates a Vector object and initializes every attribute to zero. Please remember
that C does not initialize the attributes for you. Attributes must be initialized explicitly.
Line 17 copies v1’s attributes to v2’s attributes. The attributes are copied from v1 to v2
one by one. The call stack is shown below:
Symbol Address Value
v2.z 120 2
v2.y 116 6
v2.x 112 3
v1.z 108 2
v1.y 104 6
v1.x 100 3
Since v1 and v2 occupy different addresses in the call stack, changing the attributes of
v1 does not affect the attributes of v2 and vice versa. Line 20 changes v1.x; line 21 changes
v2.y. The effects are limited to the corresponding addresses.
Programmer-Defined Data Types 253
Symbol Address Value
v2.z 120 2
v2.y 116 6 5
v2.x 112 3
v1.z 108 2
v1.y 104 6
v1.x 100 3 4
As a result, lines 19 and 20 print different values.
Even though Vector is a type and assignment = is supported, the type does not have
all the properties of built-in types (int, char, double, etc.). For example, we cannot use
== or != to compare two Vector objects:
Vector v1 ;1
Vector v2 ;2
v1 .x = 1;3
v1 .y = 2;4
v1 .z = 3;5
v2 .x = 0;6
v2 .y = -1;7
v2 .z = -2;8
9
i f ( v1 != v2 )10
{11
printf (" v1 and v2 are d ifferent .n ") ;12
}13
When compiling this function, gcc will say:
invalid operands to binary !=
If we want to compare two Vector objects, we have to write a function that compares
the attributes, for example,
#in clude " vector .h "1
int equalVe ctor ( Vector v1 , Vector v2 )2
// return 0 if any attribute is different3
// return 1 if all attribu t es are equal4
{5
i f ( v1 .x != v2 .x) { return 0; }6
i f ( v1 .y != v2 .y) { return 0; }7
i f ( v1 .z != v2 .z) { return 0; }8
return 1;9
}10
The following section explains how to pass objects as function arguments.
16.2 Passing Objects as Arguments
A Vector object can be passed as a function argument. When passing an object as an
argument, all attributes are copied to the argument of the called function. This is the same
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset