9 Assignment and Pointer Semantics
9 Assignment and Pointer Semantics
Semantics
Rida A. Bazzi
𝜆 𝜆
𝜆
x->x->x->x = x->x->x->x
© RIDA BAZZI This document is copyrighted by Rida Bazzi and should not be shared or used
for other than the purpose for which it was provided to you.
Assignment Semantics
Assignment semantics is concerned with the meaning of
a = expr
where:
1. Copy semantics. This semantics is used in C, C++ and is used for basic types
in Java
2. Reference semantics. This semantics is use by Java for assigning object
values
In what follows I will concentrate on copy semantics, but I will later explain
reference semantics
Box-Circle Diagram
address
value
name location
Also, we make the distinction between the location and the value stored in the
location. A location can store different values at different times.
Finally, we make the distinction between a location and the address of the
location. The location itself can be thought of as a physical location but the
address is just a number that can be used to describe the “position” of the
location in memory. The address itself is not a name of the location but can be
used in naming the location. For example, 1024 is an address. By itself, 1024 is
not a name of a location, but “The location at address 1024” is a name for the
location whose address is 1024!! This is not playing on words. The distinction is
real. Note that we say “a” name not “the” name because one location can have
multiple names.
We say that the location (the box) is associated with the name. The line between
the name and the location represents this association (which is also called
binding)
In general, a name need not be a simple variable name. We also, treat more
involved expressions as names. For example, a[i] where a is an array is the name
of a location (that depends on the value of i).
5
In both forms, a value is copied to a location. The difference is where the value
comes from.
l-values and r-values
l-value is an expression that has a location associated with it
Examples a, b[i+j], b[i+b[i]], *p, **q
r-value is an expression that does not have a location associated with it, but has a
value associated with it
Examples 5, i+j, 2*a
l-value1
l-value2
l-value
✘
r-value
3. r-value = l-value not possible
4.
✘
r-value1 = r-value2 not possible
Value of an expression
if an expression is an l-value, we define its value to be the value stored in the
location associated with it
if an expression in an r-value, we define its value to be the value associated with it
Examples
a = 5; // at this point, the value of the expression “a” is 5
b = a+5; // the value of the expression “a+5”, which is an r-value, is 10
Pointer Semantics in C
Pointer declaration has the form
T * x;
*x addry
x addry VT
VT is a value of type T
The picture shows that the location “pointed to” by x is associated with the name *x. More
specifically, if x is a pointer variable, *x is an l-value. The location associated with *x is the
location whose address is equal to the value in the location associated with x (the location
whose address is the value of the expression x). More simply, the location associated with
*x is the location “pointed to” by x. Or *x is a name for the location pointed to by x.
Pointer Semantics Examples
We consider two pointer variables x and y and assume
int *x;
int *y;
...
// point 1
The ... represents some missing code that is not shown, and we assume that, at point 1, the box-
circle diagram is the following
m1 *x addr2 m2
x addr2 2
m3 addr4 m4
*y
y addr4 5
In the diragram, m1, m2, m3 and m4 are used to refer to the boxes without using program
variables. Notice how in the diagram location m2 is associated with *x and location m4 is
associated with *y.
Assuming the situation is as show above, we consider the effects of various assignments.
1. x = y : this will copy the value in the location associated with y to the location
associated with x. The situation becomes as follows
m1 addr2 m2
*x
x addr4 2
m3 addr4 m4
y addr4 5
*y
notice how the value in the location associated with y (addr4) is copied to the location
associated with x. The result is that x points to m4.
2. *x = *y : this will copy the value from the location associated with *y to the location
associated with *x (remember that this is being applied to the situation above).
m1 addr2 m2
*x
x addr4 5
m3 addr4 m4
y addr4 5
*y
malloc(): memory allocation function
malloc() : input: integer which specifies the “size” in bytes
of the memory to be allocated
malloc() allocates memory whose size is equal to the “size” parameter and returns the
address of the first byte of the allocated memory
The allocated memory is allocated on the heap and is not initialized by malloc()
Example
x = (T *) malloc(sizeof(T));
The call to malloc() allocates memory whose size is the size of a value of type T. The
returned value has type void * . That is why we use type casting (T *) when we assign
the value to x.
x addry
memory allocated
with malloc()
Note C does not require that a value of type void * be typecast in order to assign it to
a variable of type T *. The type casting is implicitly done. Nevertheless, it is good
practice to have an explicit type case in this case. It makes the code more readable and
potentially easier to detect mistakes. This is why C++ requires typecasting with malloc().
Remember that code that you write is read much more often than it is written!
free(): memory de-allocation function
free() : input: pointer to memory that was previously
allocated
output: no output
The input to free() must have a value which is the address of a previously
allocated memory. If the value passed to free() does not satisfy this requirement,
its behavior is undefined which means that you cannot rely on what will happen.
The size of the memory to be freed is not specified. The memory manager knows
the size because it stores that information when malloc() was previously called.
& : address operator
& & is a unary operator
Value The value of & l-value is equal to the address of the location associated
with the l-value
Example The value of &x is the address of the location associated with x
Location the location associated with *(expr), where expr is an expression that is
either an l-value or an r value is the location whose address is equal to the value
of expr.
Illustration addrm
x addrm
*x
l-value
value
name location
location
Bazzi’s house
value stored in
two different
names for the the house
same location
The house at
3.14 𝝀 lane
3.14 𝝀 lane
(like *address) address
Example
int x;
int *y;
value is int
value is address of
a location that
stores a value of
type int
addrx
y addrx
Example continued
y = &*y; // *y is an l-value
// the location associated with *y is the
// location whose address is the value in
// the location associated with y (the value
// of y)
addrx
*y
y addrx
*y
y addrx
*&y
// y = *&y is equivalent to y = y
// so, the value of y does not change
Example continued
x = 1;
x 1
y addrx
x 1
addrm
y addrm
*y
int value
Example continued
*y = x; // copy value in location associated with x to location
// associated with *y
x 1
addrm
y addrm 1
*y
Structures
When we declare a structure
struct {
int i, j;
} x;
m11
i
x m1
j
m12
Note how the locations for x.i and x.j are inside the location we
associate with x.
struct {
int i, j;
} x[4];
i
x[0]
j x[1].j location
i
x[1]
j x[1].j value
i
x[2]
j
i
x[3]
j
Pointers with Structures
When we declare a structure
struct st {
If we execute
we get
addrm
*x
i
x m
next
Aliases
Two expressions are aliases of each other if they have the same
location associated with them. In other words, the two expressions
are two different names for the same location.
Since the definition requires that the two expressions are the names
of the same location, it follows that the definition only applies to l-
values.
We have already seen that x and *&x are aliases of each other
1. pointers
2. arrays
int * x;
int * y;
x = (int *) malloc(sizeof(int));
*x
x
If we execute y = x, we get
1 2
*x
x
*y
y
i = 5;
j = 3;
int *x;
int *y;
x = (int *) malloc(sizeof(int));
y = x;
free(x); // frees location m
*x
x m
*y deallocated
y
int **x;
int * y;
x = (int **) malloc(sizeof(int *)); // mem1
*x = (int *) malloc(sizeof(int)); // mem2
y = *x;
pointer to int int
*x mem1 mem2
**x
x
*y
y
pointer to int*
x = (T * ) malloc(sizeof(T))
*x = (int *) malloc(sizeof(int))
Dangling Reference: Example 2
pointer to deallocated memory
int **x;
int * y;
x = (int **) malloc(sizeof(int *));
*x = (int *) malloc(sizeof(int));
y = *x;
free(*x);
*x
**x
x
*y deallocated
y
int * f()
{ int x; // memory for x allocated on stack
// point 1
return &x;
}
main()
{
int *y;
y = f(); // memory for x deallocated when function
// returns but y still point to it
// point 2
// point 1 // point 2
y main() y main()
stack
x f() x
{ int *x;
{ int y;
x = &y; // point 1
}
// point 2
}
Example
*x mem1 mem2
**x
x
*y
y
If we execute x = &y
mem1 mem2
**x
ge
x rba
ga
*x
*y
y
repeat
receive input
call f(input) to process input
produce output
forever
If the function f() allocates some memory that is not needed after the call and is
not deallocated, then as time goes by more and more memory gets allocated
without it being deallocated (which creates a big leak as shown on the next page).
At some point, there will be no more heap memory and malloc() will fail and the
program will fail.
BIG LEAK
Garbage: Example 1
{ int * x;
int * y;
int * z;
// point 1
}
*x
x mem1
*y
y mem2
*z
g e mem3
z ba
r
ga
f()
{ int * x;
x = (int *) malloc(sizeof(int)); // mem1 is allocated
}
main()
{
f(); // mem1 is garbage when function exit if free() is not called
}
On the other hand, the following call does not produce a garbage location
int * f()
{ int * x;
return (int *) malloc(sizeof(int)); // mem2 is allocated
}
main()
{ int * y;
y = f(); // here mem2 is not garbage because its name is *y
}
O1
O2
If we execute
O1 = new A;
we get
O1
O2
If we execute
O2 = O1;
we get
O1
O2