Operators Top ArraysAssignment Contents

Assignment

Once a variable has been created, it is possible to change its value, or binding, using the assignment operator. Consider the following sequence of statements:

    //test
    int BLACK = 1;                    //BLACK created, initialized to 1
    int BROWN = 2;                    //BROWN created, initialized to 2
    int GREEN = 3;                    //GREEN created, initialized to 3
    int eyeColor = BLACK;             //eyeColor created, initialized to 1
    //calculate!
    printf("%d\n",eyeColor);          //should print 1
    eyeColor = GREEN;                 //assignment! eyeColor becomes 3
    printf("%d\n",eyeColor == BLACK); //equality, should false: 0
    printf("%d\n",eyeColor == BROWN); //equality, should false: 0
    printf("%d\n",eyeColor == GREEN); //equality, should true:  1

Recall that the // character sequence starts a comment in C; those two characters and any following characters on the line are ignored.

The operator = (equals sign) is the assignment operator. The assignment operator, however, is not like the operators + and *. If one wants to add the variables:

    a + b

one would take the value of a and the value of b and add those two values together. In general, for the mathematical operators, both sides of the operator are evaluated. For =, the variable on the left is not evaluated. If it were, the assignment:

    eyeColor = GREEN;

would attempt to assign the value of 3 (the current value of GREEN) to the value 1 (the current value of eyeColor). The last three expressions given in the code fragment above refer to the equality operator. The equality operator, == (often pronounced "double equals"), returns true if its operands refer to the same thing and false otherwise.

In the above snippet of C code, we use the integers 1, 2, and 3 to represent the colors black, brown, and green. By abstracting 1, 2, and 3 and giving them meaningful names (i.e., BLACK, BROWN, and GREEN) we find it easy to read code that assigns and tests eye color. We do this because it is difficult to remember which integer is assigned to which color. Without the variables BLACK, BROWN, and GREEN, we have to keep little notes somewhere to remind ourselves what's what. Here is an equivalent sequence of statements without the use of the variables BLACK, GREEN, and BROWN.

    //test
    int eyeColor = 1;
    //calculate!
    printf("%d\n",eyeColor);                //should print: 1
    eyeColor = 3;
    printf("%d\n",eyeColor == 2);           //should print false: 0
    printf("%d\n",eyeColor == 3);           //likely print true:  1

In this interaction, the meaning of eyeColor is not so obvious. We know it's a 3 at the end, but what eye color does 3 represent? When numbers appear directly in code, they are referred to as magic numbers because they obviously mean something and serve some purpose, but how they make the code work correctly is not always readily apparent, much like a magic trick. Magic numbers are to be avoided. Using well-named variables to hold these values is considered stylistically superior.

Lvalues vs. rvalues

To distinguish when the value of a variable is extracted and when it is updated, Computer Scientists use the terms rvalue and lvalue. An rvalue refers to the value of a variable while an lvalue refers to a location in memory. In an assignment statement such as:

    x = y;          //update the value of x with the value of y

the variable y appears to the right of the assignment operator and is therefore an rvalue. We extract the value of y in this case. The variable x, on the other hand, appears to the left of the assignment operator and is therefore an lvalue. We update the value of x.

Variables versus constants

Many programming languages have constructs similar to variables known as constants. A constant can be thought of as a variable that, once it gets a value, cannot be reassigned. Constants are used for values that never change, the value of π, the square root of 2, and so on. The C programming language does not have these kinds of constants15. So C programmers use variables as constants instead. In the previous section, you probably noticed that some variables were named using all capital letters: BLACK, GREEN, and BROWN. By convention, a variable named in (mostly) all-caps is not meant to change from its initial value. The use of caps emphasizes the constant nature of the variable.

There is one type of constant C does have, the name of a statically allocated array. You will learn about statically allocated arrays in the next chapter.

Precedence and Associativity of Assignment

The assignment operator is right associative. The right associativity allows for statements like

    a = b = c = d = 0;

which conveniently assigns a zero to four variables at once and, because of the right associative nature of the operator, is equivalent to:

    (a = (b = (c = (d = 0))));

The resulting value of an assignment operation is the value assigned, so the assignment d = 0 returns 0, which is, in turned, assigned to c and so on.

Assignment has the lowest precedence among the binary operators. Thus, assignment is always performed last in any expression. For example:

    a = b = c * d

is equivalent to the fully parenthesized:

    (a = (b = (c * d)))

Note that expressions like:

    a = b = c * d = e

are nonsensical; the multiplication has to happen first and therefore the value of e would have to be assigned to a number, which is illegal. On the other hand:

    a = b = c * (d = e)

is perfectly legal and has the effect of assigning to d the value of e and to a the value of c * d.

Assignment Patterns

The art of writing programs lies in the ability to recognize and use patterns that have appeared since the very first programs were written. In this text, we take a pattern-based approach to teaching how to program. For the topic at hand, we will give a number of patterns that you should be able to recognize to use or avoid as the case may be.

The Transfer Pattern

The transfer pattern is used to change the value of a variable based upon the value of another variable. Suppose we have a variable named alpha which is initialized to 3 and a variable beta which is initialized to 10:

    alpha = 3;
    beta = 10;

Now consider the statement:

    alpha = beta;

This statement is read like this: make the new value of alpha equal to the value of beta, throwing away the old value of alpha. What is the value of alpha after that statement is executed? Highlight the following line to see the answer:

The new value of alpha is 10.

The transfer pattern tells us that value of beta is imprinted on alpha at the moment of assignment but in no case are alpha and beta conjoined in any way in the future. Think of it this way. Suppose your friend spray paints her bike neon green. You like the color so much you spray paint your bike neon green as well. This is like assignment: you made the value (color) of your bike the same value (color) as your friend's bike. Does this mean your bike and your friend's bike will always have the same color forever? Suppose your friend repaints her bike. Will your bike automatically become the new color as well? Or suppose you repaint your bike. Will your friend's bike automatically assume the color of your bike?

Let's look at the transfer pattern graphically. For the assignments:

   alpha = 3;
   beta = 7;

we would diagram the situation as:

or more conveniently:

alpha: 3
beta: 7

Now, when we assign alpha the value of beta:

    alpha = beta;

we replace the current value stored at the memory location identified by alpha with the value of beta:

or:

alpha: 7
beta: 7

We can see from the diagram that if we give a new value to beta, causing us to replace the value stored at beta, the value stored at alpha will be unaffected.

To test your understanding, consider what happens if the following code is executed:

    alpha = 4;
    beta = 13;
    alpha = beta;
    beta = 5;

What are the final values of alpha and beta? Highlight the following line to see the answer:

The value of alpha is 13 and the value of beta is 5.

Here, the variable diagram would look like:

alpha: 4       ->       alpha: 13       ->       alpha: 13
beta: 13 beta: 13 beta: 5

where the diagram on the left illustrates what things look like after alpha and beta get their original values, the diagram in the middle after alpha gets beta's value, and the diagram on the right after beta gets the value of 5.

To further test your understanding, what happens if the following code is executed:

    alpha = 4;
    beta = 13;
    alpha = beta;
    alpha = 42;

What are the final values of alpha and beta? Highlight the following line to see the answer:

The value of alpha is 42 and the value of beta is 13.

Here, the variable diagram would look like:

alpha: 4       ->       alpha: 13       ->       alpha: 42
beta: 13 beta: 13 beta: 13

where the diagram on the left illustrates what things look like after alpha and beta get their original values, the diagram in the middle after alpha gets beta's value, and the diagram on the right after alpha gets the value of 42.

The Update Pattern

The update pattern is used to change the value of a variable based upon the original value of the variable. Suppose we have a variable named counter which is initialized to zero:

    int counter = 0;

Now consider the statement:

    counter = counter + 1;

This statement is read like this: make the new value of counter equal to the old value of counter plus one. Since the old value is zero, the new value is one. Consider this sequence:

    //test
    int counter = 0;
    counter = counter + 1;
    counter = counter + 1;
    counter = counter + 1;
    counter = counter + 1;
    counter = counter + 1;
    printf("%d\n",counter);

What is the value of counter after the following code is executed? Highlight the following line to see the answer:

The value of counter is 5.

There is another form of this update:

    counter = 0;
    counter += 5;

The operator += says to update the variable on the left by adding in the value on the right to the current value of the variable.

The update pattern can be used to sum a number of variables. Suppose we wish to compute the sum of the variables a, b, c, d, and e. The obvious way to do this is with one statement:

    int sum = a + b + c + d + e;

However, we can use the update pattern as well:

    //test
    int a = 1,b = 2,c = 3,d = 4,e = 5;
    int sum = 0;
    sum = sum + a;
    sum = sum + b;
    sum = sum + c;
    sum = sum + d;
    sum = sum + e;
    printf("sum is %d\n",sum);

If a is 1, b is 2, c is 3, d is 4, and e is 5, then the value of sum in both cases is 15. Why would we ever want to use the update pattern for computing a sum when the first version is so much more compact and readable? The answer is...you'll have to wait until we cover a programming concept called a loop. With loops, the update pattern is almost always used to compute sums, products, etc.

The Throw-away Pattern

The throw-away pattern is a mistaken attempt to use the update pattern. In the update pattern, we use the original value of the variable to compute the new value of the variable. Here again is the classic example of incrementing a counter:

    counter = counter + 1;

In the throw-away pattern, the new value is computed but it the variable is not reassigned, nor is the new value stored anywhere. Many novice programmers attempt to update a counter simply by computing the new value:

    counter + 1;       // the value counter + 1 is thrown away!

C does all the work to compute the new value, but since the new value is not assigned to any variable; the new value is thrown away.

The Throw-away Pattern and Functions

The throw-away pattern applies to function calls as well. We haven't discussed functions much, but the following example is easy enough to understand. First we define a function that computes some value:

    int
    increment(int x)
        {
        return x + 1;
        }

The function increment, when given an integer (stored in x), returns a value one greater than the value of x. What the function actually does, however, is irrelevant to this discussion. but we want to start indoctrinating you on the use of functions. Repeat this ten times:

We always do four things with functions: define them, call them, return something, and save the return value.

To call the function, we use the function name followed by a set of parentheses. Inside the parentheses, we place the value we wish to send to the function. Consider this code, which includes a call to the function increment:

    int y = 4;
    y = increment(y);
    printf("y is %d\n",y);

If we were to run this code, we would see the following output:

    y is 5

The value of y, 4, is sent to the function which adds one to the given value and returns this new value. This new value, 5, is assigned back to y. Thus we see that y has a new value of 5.

Suppose, we run the following code instead:

    int y = 4;
    increment(y);             // NOT y = increment(x); as before
    printf("y is %d\n",y);

Note that the return value of the function increment is not assigned to any variable. Therefore, the return value is thrown away and the output becomes:

    y is 4

The variable y is unchanged because it was never reassigned.

About Patterns

As you can see from above, not all patterns are good ones. However, we often mistakenly use bad patterns when programming. If we can recognize those bad patterns more readily, our job of producing a correctly working program is greatly simplified.

lusth@cs.ua.edu


Operators Top ArraysAssignment Contents