264 Intermediate C Programming
twice. Assigning p1 to p2 at line 10 merely copies the pointer, and the two different pointers
store the same memory address 60000.
Can the problem be solved by simply not calling the destructor for p2? Yes, but it
depends on the intention of the program. When p1 and p2 have the same value, changing
p1 -> name[0] (the first letter of the name) will also change p2 -> name[0]. However, in
the following code:
int x = 5;1
int y = x;2
x = 12;3
What is the value of y? Should it be 5 or 12? Experience tells us that y should be 5. This
is correct because x and y occupy different memory addresses. Even though p1 and p2
have different addresses (100 and 104), they store the same value, 60000. Thus changing
p1 -> name[0] also changes p2 -> name[0]. In the next example, both p1 and p2 have
distinct values generated by calling Person construct. Will the following program work?
#in clude < stdio .h >1
#in clude < stdlib .h >2
#in clude < string .h >3
#in clude " person .h "4
int main ( i n t argc , char * argv [])5
{6
Person * p1 = P erson _const ruct (1989 , 8, 21 , " Amy ") ;7
Person * p2 = P erson _const ruct (1991 , 2, 17 , " Bob ") ;8
p2 = p1 ;9
Perso n_print ( p1 );10
Perso n_print ( p2 );11
Pers on_des truct ( p1 );12
Pers on_des truct ( p2 );13
return EXIT _SUCCES S ;14
}15
This is the call stack and the heap memory after line 9:
Frame Symbol Address Value
main
p2 104 80000
p1 100 60000
Symbol Address Value
p2 -> name[3] 85003 0’
p2 -> name[2] 85002 ’b’
p2 -> name[1] 85001 ’o’
p2 -> name[0] 85000 ’B’
p2 -> name 80012 85000
p2 -> date 80008 17
p2 -> month 80004 2
p2 -> year 80000 1991
p1 -> name[3] 70003 0’
p1 -> name[2] 70002 ’y’
p1 -> name[1] 70001 ’m’
p1 -> name[0] 70000 ’A’
p1 -> name 60012 70000
p1 -> date 60008 21
p1 -> month 60004 8
p1 -> year 60000 1989
Programmer-Defined Data Types 265
Does this program work? No, valgrind still reports problems in Person destruct. Line
10 still copies p1’s value to p2 and both are 60000. This also causes a memory leak because
the memory at 80000 and 85000 is no longer accessible.
Consider another scenario when the objects are not accessed through pointers:
#in clude < stdio .h >1
#in clude < stdlib .h >2
#in clude < string .h >3
#in clude " person .h "4
int main ( i n t argc , char * argv [])5
{6
Person p1 ;7
Person p2 ;8
p1 . year = 1989;9
p1 . month = 8;10
p1 . date = 21;11
p1 . name = strdup (" Amy ") ;12
p2 . year = 1991;13
p2 . month = 2;14
p2 . date = 17;15
p2 . name = strdup (" Bob ") ;16
17
Perso n_print (& p1 );18
Perso n_print (& p2 );19
free ( p1 . name );20
free ( p2 . name );21
return EXIT _SUCCES S ;22
}23
The program uses strdup to copy strings. In this program, both p1 and p2 are objects
on the call stack:
Frame Symbol Address Value
main
p2.name 128 85000
p2.date 124 17
p2.month 120 2
p2.year 116 1991
p1.name 112 70000
p1.date 108 21
p1.month 104 8
p1.year 100 1989
Symbol Address Value
p2.name[3] 85003 0’
p2.name[2] 85002 ’b’
p2.name[1] 85001 ’o’
p2.name[0] 85000 ’B’
p1.name[3] 70003 0’
p1.name[2] 70002 ’y’
p1.name[1] 70001 ’m’
p1.name[0] 70000 ’A’
What will happen if the program has this line?
p2 = p1 ;17
The assignment = copies one object’s attributes to another object’s attributes. After exe-
cuting this line, the call stack and heap will appear as follows:
266 Intermediate C Programming
Frame Symbol Address Value
main
p2.name 128 70000
p2.date 124 21
p2.month 120 8
p2.year 116 1989
p1.name 112 70000
p1.date 108 21
p1.month 104 8
p1.year 100 1989
Symbol Address Value
p2.name[3] 85003 0’
p2.name[2] 85002 ’b’
p2.name[1] 85001 ’o’
p2.name[0] 85000 ’B’
p1.name[3] 70003 0’
p1.name[2] 70002 ’y’
p1.name[1] 70001 ’m’
p1.name[0] 70000 ’A’
Now, p1.name and p2.name have the same value (70000). The heap memory originally
pointed to by p2.name is still in the heap but is no longer accessible because p2.name is
no longer 85000. This causes memory leak. Moreover, lines 20 and 21 free the same heap
memory at 70000 twice. As you can see, if an object’s attribute is a pointer, we need to
be very careful about how memory is allocated and freed. If we are not careful, then the
program may leak memory or release the same memory twice, or both.
Are there general rules for handling objects that have attributes which are pointers?
Fortunately, there are. When an object’s attribute is a pointer, that usually indicates the
need for four functions.
constructor: allocates memory for the attribute and assigns the value to the attribute.
destructor: releases memory for the attribute.
copy constructor replacing =: by creating a new object from an existing object. This is
sometimes referred to as cloning. The new object’s attribute points to heap memory
allocated by calling malloc.
assignment replacing =: modifying an object that has already been created by using the
constructor or the copy constructor. Since the object has already been constructed,
the object’s attribute stores the address of a heap memory. This memory must be
released before allocating new memory.
The first two functions have already been given above. The other two functions are
shown below:
#in clude < stdio .h >1
#in clude < stdlib .h >2
#in clude < string .h >3
#in clude " person .h "4
Person * Per son_copy ( Person * p );5
// create a new object by copying the attri b utes of p6
Person * Pe rson_as sign ( Person * p1 , Person * p2 );7
// p1 is already a Person object , make its attribute8
// the same as p2 s attribu tes ( deep copy )9
Person * Per son_copy ( Person * p )10
{11
return Per son_c onstru ct ( p -> year , p -> month ,12
p -> date , p -> name );13
}14
Person * Pe rson_as sign ( Person * p1 , Person * p2 )15
{16
free ( p1 -> name );17
p1 -> year = p2 -> year ;18
p1 -> month = p2 -> month ;19
p1 -> date = p2 -> date ;20
Programmer-Defined Data Types 267
p1 -> name = strdup ( p2 -> name );21
return p1;22
}23
int main ( i n t argc , char * argv [])24
{25
Person * p1 = P erson _const ruct (1989 , 8, 21 , " Amy ") ;26
Person * p2 = P erson _const ruct (1991 , 2, 17 , " Jennifer ") ;27
Person * p3 = P erson_cop y ( p1 ); // create p328
Perso n_print ( p1 );29
Perso n_print ( p2 );30
Perso n_print ( p3 );31
p3 = P e rson_a ssign (p3 , p2 );32
Perso n_print ( p3 );33
Pers on_des truct ( p1 );34
Pers on_des truct ( p2 );35
Pers on_des truct ( p3 );36
return EXIT _SUCCES S ;37
}38
What is the difference between Person copy and Person assign? Person copy creates
a new Person object by allocating memory. Person assign has to release memory for
existing attributes before copying the attributes.
The copy constructor allocates separate memory space so that changing one object later
does not affect the other. This is called a deep copy. The assignment function has to do
more work, because the object already occupies memory. In our example, the original name
in p3 is “Amy”. When p2 is copied to p3, p3 -> name does not have enough memory for
the longer name “Jennifer”. Thus, the assignment function first releases the memory for
p3 -> name and then allocates memory again by later calling strdup.
The assignment function can check whether p3 -> name has enough memory. If
p3 -> name has enough memory, it is unnecessary to release p3 -> name and allocate
memory again. Note that this would require an if statement and a call of strlen. This can
marginally complicate the program. If the new name is longer, it is still necessary to free and
allocate memory. Some beginner programmers want to optimize their code. However, it is
often difficult for even experienced programmers to know what is slowing down a program.
You should avoid making this type of unnecessary complication without first profiling the
code using gprof. This is a very important principle to follow.
Whereas a deep copy allocates memory so that objects do not share memory, a shallow
copy allows several objects’ attributes to point to the same memory addresses. Shallow
copies can be useful in some cases. For example, in a student database, every student has an
attribute that points to an object representing the school. It is unnecessary for every student
to have an individual copy of the school’s object. There can be one school object shared by
every student object. In this scenario sharing makes sense, and the copy constructor and
assignment operator should perform a shallow copy of the school attribute. Another reason
for using shallow copies is when objects share a very large piece of memory and few objects
actually need to modify this shared memory. A copy is made only when an object intends
to make changes. This is called copy on write and is beyond the scope of this book.
268 Intermediate C Programming
16.5 Structures within Structures
Can a structure’s attribute be another structure? Yes. In this example, we move a
Person’s date of birth from three integers into one Date object:
// da t eofbirth . c1
#in clude < stdio .h >2
#in clude < stdlib .h >3
#in clude < string .h >4
typedef s t ru ct5
{6
int year ;7
int month ;8
int date ;9
} Dat eOfBirth ;10
DateOf Birth Date OfBi rth_c onst ruct ( i n t y , int m , i nt d)11
{12
DateOf Birth dob ;13
dob . year = y ;14
dob . month = m;15
dob . date = d ;16
return dob ;17
}18
void Date OfBir th_pr i nt ( Dat eOfBirth d)19
{20
printf (" Date of Birth : %d /% d /% d n" ,21
d. year , d . month , d . date ) ;22
}23
typedef s t ru ct24
{25
char * name ;26
DateOf Birth dob ;27
} Person ;28
Person * P e rson_ const r uct ( char * n , i nt y , i n t m , i n t d ) ;29
void Pers o n_des truct ( Person * p );30
// create a new object by copying the attri b utes of p31
Person * Per son_copy ( Person * p );32
// p1 is already an object , make its attr i bute the33
// same as p2 s attribu t es34
Person * Pe rson_as sign ( Person * p1 , Person * p2 );35
void Person _print ( Person * p );36
int main ( i n t argc , char * argv [])37
{38
Person * p1 = P erson _const ruct (" Amy " , 1989 , 8 , 21) ;39
Person * p2 = P erson _const ruct (" Jennifer " , 1991 , 2, 17) ;40
Person * p3 = P erson_cop y ( p1 ); // create p341
Perso n_print ( p1 );42
Perso n_print ( p2 );43
Perso n_print ( p3 );44
p3 = P e rson_a ssign (p3 , p2 ); // change p345
..................Content has been hidden....................

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