Topis to cover

What is an expression?

Answer: An expression is textual piece of source code that can be evaluated to produce an object that has a type and a value.
Remember: Each expression has a type and a value.
  1. Arithmetic expressions. The most familiar expressions are arithmetical. For example, the following is an arithmetic expression.
    
    // Suppose k is an int variable with value 4
    (k - 2)*7     // This is an expression -- type: int, value: 14
    
  2. Assignment expressions. Assignment is also an expression.
    
    k = 5 // This is an expression -- type int, value: 5       
    

    In general, an assignment expression has the same type as the object being assigned to, and the same value as that object after the assignment is carried out. Thus, oddly enough, if x is a variable of type double, then

    
    (x = 3.4)*2.0     // type: double, value: 6.8, there is a side effect
    
    makes perfect sense. In particular,
  3. Less obvious expressions. Another less obvious example is:
    
    cout << x     // type: ostream, value: cout, there is a side effect
    
    As we saw last lecture, cout is an object of type ostream.

When do you know the type and value of an expression?

The type of the expression k+1 is something we almost always know before running the program. Look at the code below:
int k;
cin >> k;
cout << k+7; 
Q: What is the type of the expression k+7?

A: It's int.

Most of the time (especially for the subset of C++ we concentrate on) the type of an expression is known at compile time, i.e. when compiling the program, as opposed to running it. Because of this, C++ is said to be a "statically typed" language.

However, often the value of an expression cannot be determined until the program is run. Consider the code above again.

Q: What is the value of the expression k+7?

A: Well, it depends what the user enters for input. That's something that cannot be known until the program is run.

What's not an expression?

At this point it may seem like everything's an expression, but that's not true. For example: Still, most things are expressions, and understanding this fact and being able to identify the types and values of expressions are key to understanding C++ ... and most any other programming language.

When types collide -- implicit type conversion

Things get interesting when expressions involve different types.

In arithmetic expressions: promotion

Q: Consider the following code. What is the type and value of the expression x*k?

double x;
int k;
x = 3.3;
k = -2;
// x*k : type and value? 
A: (drag your mouse):
 // x*k -- type: double, value: -6.6 
Explanation: C++ knows how multiply two int objects, and it knows how to multiply two double objects, but it doesn't know how to multiply one of each. However, it understands that an int can be converted to a double and vice versa. So it converts one and performs the multiplication on two objects of the same type. But which way should it go?

bool b;
char c
int n;
double x;
b = true;
c = 'a';
n = 1;
x = 2.1;

// b+c :  type int
// c+c :  type int
// b+n :  type int
// c+n :  type int
// b+x :  type double
// c+x :  type double
// n+x :  type double

For arithmetic, types are always converted in the direction that gives the most precision - this is referred to as type promotion. Specifically, the direction is as follows:

→ int → double
So, in our example above, the int is converted (or promoted) to a double, and the operation is performed on two doubles. It wouldn't make nearly as much sense the other way round, would it? Some more examples are shown on the right.

Arithmetics on chars. One interesting feature of this match-up between characters and numbers is that statements like

 
cout << ('b' - 'a') << endl // 'b'-'a': int 
make perfect sense. In fact, the letters of the alphabet appear in order, so that a is 97, b is 98, ..., z is 122. So,
cout << ('b'+3) << endl;
will output 101.

In assignment expressions: follows the type of an assigned variable

Read the following carefully. This is one of the most common pitfalls that students fall into.
This implicit type conversion (implicit meaning that it happens automatically behind the scenes, without you doing anything directly) happens in other cases. The only one affecting us right now is assignments. You can assign an object of one type to an object of a different type, as long as C++ knows how to do the conversion. If it doesn't, the compiler will let you know.

So, for example, consider the following code:


double x;
int k; 
x = 3.3;
cout << (k = x) << endl;;    // expression k=x -- type: int, value: 3
In the above, the expression k=x is of type int with value 3. C++ truncates doubles when converting to ints.

Explicit type conversion

Another way to do type conversion amongst the built-in types (int, double, char, bool) is to follow the C-language sytax, which is to preface the expression you are converting with the new type in parentheses. For example, if k is an int and you'd like to convert it to the equivalent double, you'd write:
(double)k
which would "cast" k to type double. C++ actually adds functionality to C, i.e. it is literally C plus some other stuff. So C constructs like (double)k all work.
We also have explicit type conversion. Suppose, for example, that m and n are ints, n being the larger. We'd like to print out the value of m/n. Well,

cout << m/n << endl;
will just print out zero! (Make sure you know why!) We'd like to get some fractional value, in other words, we'd like these values treated as doubles. To explicitly convert them to doubles first we'd write:

cout << double(m)/double(n) << endl;

Quick check: Can you explain what type and value you get with the following code?

1 / double(2)
Answer (drag your mouse): It's of type double and value 0.5. Integer 1 is implicitly promoted to the double type before the division operation.

Explicit conversion can get tricky later on, but at this stage it's as simple as writing the new type name followed by the old object in ()'s.

Some Quick Conversion Rules
int → double : This does exactly what you'd expect.
double → int : This simply truncates the value, meaning that whatever's after the decimal point just gets chopped. You can get in trouble if the double value is too big.
bool → int : true goes to 1, false goes to 0.
int → bool : 0 goes to false, everything else goes to true;
int → char : if the int is in the range 0-127 the char value is determined by the ASCII table;
char → int : the int value is determined by the ASCII table;

Representing data in a computer

Bits and bytes

Note: Since SY110 covers this topic, you are expected to understand about bits and bytes, binary-to-decimal and decimal-to-binary conversion, and how the ASCII table defines a mapping between characters and bytes.

Recall that inside a computer everything is 0's and 1's! (A bit is just a 0/1 value.) But how can all of these things - chars, ints, bools, and doubles - be represented by zeros and ones? Our understanding of types will really depend on being able to answer these questions.

Size of each type

The memory of a computer is simply one long sequence of bits. However, these bits are organized into chunks of 8 called bytes. To emphasize, a byte consists of 8-bits. In a byte, we can represent the numbers from 0 to 255. Below is the table showing how many bytes are used for each type.
type #bytes notes
char 1 A single byte ASCII code for the character
bool 1 0 for false, and a non zero for true
int 4 A 4-byte signed integer.
double 8 Represented with a scientific notation (i.e., sign, mantissa, exponent)

Address of a variable: address-of operator &

We said that a variable has storage in the memory. Do we know where the variable is located in memory? The answer is yes!
code program output

int p;
p = 3;
cout << "p is at " << &p << endl;
cout << "p's value is " << p << endl;
p is at 0x7fffe5aec3c4
p's value is 3
In the above, you're expected to know:

Hexadecimal numbers

Memory diagram

Q: Consider the following program and program output. Annotate the memory diagram on the right with the variables are and their values so that the digaram shows the state of memory just after the code has been executed. (Drag your mouse on the memory diagram to see the answer.)
code output

int p;
p = 10; 
p = p + 5;

int q;
q = p - 3;

cout << &p << endl;
cout << &q << endl;
0x7ffff79351d4
0x7ffff79351d8
  
memory diagram
   address              value          (annotate variables p and q)
0x000000000000
...
0x7ffff79351c4
0x7ffff79351c8
0x7ffff79351cc
0x7ffff79351d0
0x7ffff79351d4         15               p
0x7ffff79351d8         12               q
0x7ffff79351dc            
...
0xffffffffffff

Using VS Code Debugger

The instructor will show how to use the debugger.

Basic commands

To see the memory contents

  1. Install the "Memory View" extension.
  2. View → Command Palette → MemoryView: Add new memory view...
  3. Give the address of p by typing
    &p
    Whenever the value of a variable changes, you need to refresh the memory view to see the most recent content.

Mandatory Practice Problems

Other Practice Problems