| CSC 161 | Grinnell College | Spring, 2015 |
| Imperative Problem Solving and Data Structures | ||
The purpose of this lab is to explore types and variables in C.
In Scheme, a variable (or identifier or parameter) can represent any type of data, and the type of a variable may change from one call of a procedure to another. For example, consider the following Scheme procedure that adds all numbers on a list or its sublists:
(define sum-numbers
(lambda (ls)
(cond ((number? ls) ls)
((not (list? ls)) 0)
((null? ls) 0)
(else (+ (sum-numbers (car ls))
(sum-numbers (cdr ls))))
)
)
)
When sum-numbers is called:
(sum-numbers '((1) 2.0 four ((3/4) (five) 6)))
the parameter ls starts as a list, but various calls will give ls various values, including sublists, integers (e.g., 1, 6), real numbers (2.0), fractions (3/4), and symbols (e.g., four, five). At the end of this example, sum-numbers returns 9.75.
In C, the data type of each variable must be declared before the variable is used in a program (or procedure). Once declared, the data type cannot change within the procedure; values can change, but not the type of data.
When you start programming in C, you need to be familiar with the few primitive types of variables provided to manage data with. Often processing proceeds according to the type of data involved. However, in some processing situations, C allows for implicit and explicit type conversions between these storage classes. In this lab you will perform different operations, and examine the results.
In C there are 4 primitive types:
char /* a character */ int /* an integer */ float /* a real number (single-precision)*/ double /* a real number that needs twice the space as a float (double-precision) */
The following notes provide some background about each of these data types,
As we shall discuss in several weeks, these types of data are represented in different ways within a computer.
C provides common arithmetic operations for both ints and doubles, with an extra capability for for ints.
Basic rules:
Include the following code segment in a C program, compile the program, and observe the results:
int a = 5;
int b = 6;
int c = a/b;
int d = b/a;
int e = (a + b) / 10;
int f = (a + b) % 10;
double x = (a + b) / 10;
double y = (a + b) / 10.0;
printf ("a=%d, b=%d, c=%d, d=%d, e=%d, f=%d, x=%lf, y=%lf\n", a, b, c, d, e, f, x, y);
Observation: You may recall there is a fourth type, float, that was not involved in Step 1. doubles are often preferred to floats, because a double is stored more accurately than a float. Also, constants, such as 3.14 are almost always stored as doubles in C. An exception arises when memory is scarce, and one wants to use as little space as possible within a computer's memory. With the size of main memory in today's computer, however, size often is not a consideration, and doubles typically are used rather than floats.
We will discuss details of the various data types, including both doubles and floats, in a few weeks.
In Step 1, the computations a/b and b/a were performed with ints. That is, the result was an integer, and any fraction of an integer was discarded. Also, in the case of computing y, the presence of the double 10.0 caused the machine to convert (a + b) to double and then perform division. In this case, the implicit type 10.0 caused conversion to double before the computation took place.
In C, we can assign values implicitly to int and double variables, but we also can explicitly ask the computer to change the data type before an operation. This explicit specification of a change in type is called casting
Include the following code segment in a C program, compile the program, and observe the results:
int k = 5.0;
int m = 7.7;
double n = 5;
int p = k/m;
double q = k/m;
printf ("k = %d, m=%d, n=%lf, p=%d, q=%lf\n", k, m, n, p, q);
double r = (double) (k/m);
double s = (double) k / m;
double t = k / (double) m;
double u = (double) k / (double) m;
printf ("r=%lf, s=%lf, t=%lf, u=%lf\n", r, s, t, u);
Observation: Sometimes there can be a danger in casting. Types have sizes. If you try to put a number that is bigger than the size of the type you are casting it to, you may get undesired results.
After deciding what operator you will use, you need to put them in certain order or in parentheses because the program will be read in a certain order and so the operators will be applied in a certain order.
int v;
v = 5 + 4 * 3 + 1
int w; w = (5 + 4) * (3 + 1)
Suppose you have a counter days that counts down the number of days you have left for the summer. Every day that passes, you need to decrement the number of days to get your countdown going. You could do it like this:
days = days - 1;
This would mean that your are taking the number of days, subtracting 1 from it, and setting that as my new days value.
There is a shortcut to saying that the new value of days is the old value of days -1. Here it is:
days -= 1
You can do a variety of operations just like this:
In practice, 1 is the most common value to add or subtract from a variable, and C provides two special ways to increment by 1. For a variable a,
Examine the output of the following code, in which variables are incremented as the only part of an expression.
int a = 1;
int b = 7;
printf ("a = %d, b = %d\n", a, b);
a++;
++b;
printf ("a = %d, b = %d\n", a, b);
In this code the ++ operation is the only part of the statement. Note what results are obtained.
Now consider the following example that combines the increment operators with other activities (e.g., assignment)
int a,b,c;
a = 0;
b = a++;
c = ++a;
printf(" a = %d b = %d c = %d\n", a,b,c);
a = 0;
b = ++a;
c = a++;
printf(" a = %d b = %d c = %d\n", a,b,c);
Examine the output and explain the result. For example, explain any difference observed between the two increment operators in this context.
The next block combines the increment operators within a print and within arithmetic expressions. Examine the output to determine whether or not there is a difference between the two increment operators in this context.
int r, s;
r = 5;
s = 7;
printf(" r = %d s = %d\n", ++r, s++);
printf(" r = %d s = %d\n", r, s);
int t = r++ + s++;
printf(" r = %d s = %d, t = %d\n", r, s, t);
int u = ++r + ++s;
printf(" r = %d s = %d, u = %d\n", r, s, u);
In addition to the increment operators, ++a and a++, C provides pre- and post-decrement, --a and a--, that subtract one from the variable.
Repeat Step 7, replacing ++ by -- throughout. Again, examine the output and explain what is printed.
Conclusion: When combined with other operations, the increment operations ++a (pre-increment) and a++ (post-increment) cause different values to be used in processing. This observation yields the following programming observation and suggestion:
In C, a character is stored as in a coded form — an integer between 0 and 255. (Historically, the code used numbers 0 to 127, but the extended code now uses codes over the full range 0 to 255.) Thus, a char is considered to be an integer with a restricted size. For the most part, we do not care what the internal code is for an integer — conceptually, we just have a character. Technically, the underlying code is called the American Standard Code for Information Interchange or ASCII.
The program ascii-print.c prints many of the ASCII values along with their corresponding characters. Codes smaller than 32 correspond to characters that are not printable (e.g., a backspace, tab, line feed, return, etc.). Similarly, codes larger than 126 may not print properly in terminal windows (e.g., code 177 is ±, code 181 is µ, and code 216 is Ø). Since only codes between 32 and 126 (inclusive) reliably print in a terminal window, the program uses a for loop that starts with code 32, the code for a space character, and ends at 126.
Since characters are stored according to their integer codes, it is possible to add an integer value to a char value.
Include the following lines of code within a program:
char ch = 'a';
ch = ch + 7;
printf("ch + 7 = '%c'\n", ch);
Add 7 to ch again (you can just copy the last two lines again) and a print out the value.
In C, a char is considered a type of small int, so it is possible to assign a char variable an integer value. Set ch to 48 and print out the result:
ch = 48;
printf("char ch = '%c'\n", ch);
Note what is printed. Can you explain why?
Set ch equal to the integer 0 and print it again.
You may notice the print statement doesn't print out ch as being zero; rather the two single quotes are printed next to each other. The reason is that the character code for the integer 0 is called a "NULL"; when printed the cursor does not move in any way — effectively printing of the character for code 0 is ignored in the output.
Development of laboratory exercises is an iterative process. Prof. Walker welcomes your feedback! Feel free to talk to him during class or stop by his office.