| CSC 161 | Grinnell College | Spring, 2012 |
| 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). Also, once declared, the data type cannot change within the procedure. Thus, when you start programming in C, you need to be familiar with the few primitive types of variables provided to manage data with. Also, in some types of processing, C allows for many implicit and explicit type conversions between these storage classes. In this lab you will perform different legal, illegal, and unusual 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) */
In reviewing these data types,
As we shall discuss in several weeks, these types of data are represented in different ways within a computer. For example, each data type may take up a different amount storage. Each data type also has a limited range of values. For example, an int has values between -2147483648 (INT_MIN in limits.h) and 2147483647 (INT_MAX in limits.h), inclusive.
When the numbers in an expression are all ints, then the arithmetic operations (e.g., +, -, *, / (quotient), and % (integer remainder) are all performed as integers (without decimal points).
Include the following code segment in a C program, compile the program, and observe the results:
int a = 5;
int b = 6;
int c = 7;
int d = a + b + c;
int e = (a + b + c) / 10;
int f = (a + b + c) % 10;
printf ("a=%d, b=%d, c=%d, d=%d, e=%d, f=%d\n", a, b, c, d, e, f);
Review what is printed and explain each result.
Change the declarations above, so that int e and int f are declared as doubles like this:
int a = 5;
int b = 6;
int c = 7;
int d = a + b + c;
double e = (a + b + c) / 10;
double f = (a + b + c) % 10;
printf ("a=%d, b=%d, c=%d, d=%d, e=%lf, f=%lf\n", a, b, c, d, e, f);
Compile and run the revised program, and explain what happens.
Change the division in step 2, so that division is by 10.0 rather than 10.
Write a simple C program that declares a char,
an int, and a double .
See what happens when you try to assign the value of each
one to one of the other types.
int to a char.
char (example 7) to an int.
double(example 7.12) to an int.
char (example 58) to a double.
You may recall there is a fourth type, float, that you didn't use in the last few steps. doubles are almost always 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. We will discuss details of the various data types, including doubles and floats, in a few weeks.
Download the program conversion-errors.c. Compile it and run it. It declares one of each of the primitive types of variables and displays their contents before and after initialization. How did not initializing effect the values?
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 x;
x = 5 + 4 * 3 + 1
int y; y = (5 + 4) * (3 + 1)
An integer is a whole number. This is why whenever you try to assign an integer a number with a decimal part, for example by diving 5 by 2 , the program will truncate this value. This means that it will throw the digits after the decimal point away. The rounding will always be down.
int a = 13; int b = 3; int c; c = a/b;
double x;
x = 13.0/3;
printf("%f\n", x);
Let's say you have a counter that counts down the number of days you have left for the summer. Let's call the counter days. 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 I am 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:
There are two ways to increment a variable: x++ and ++x. These should not be confused with each other.
Here is an example program. Examine the output to differentiate between these two incrementers.
int a,b,c;
a = 0;
b = x++;
c = ++x;
printf(" a = %d b = %d c = %d\n", a,b,c);
a = 0;
b = ++x;
c = x++;
printf(" a = %d b = %d c = %d\n", a,b,c);
In C, it is possible to add an integer value to a char value.
Add the following lines of code after the /* Add code here */ comment:
c = c + 7;
printf("c + 7 = %c\n", c);
Add 7 to c again (you can just copy the two lines again) and a print out the value.
Set c back equal to zero and print out the result:
c = 0;
printf("char c = %c\n", c);
You may notice the print statement doesn't print out c as being zero.
Why is it blank to the right of the equals sign? Behind the scenes, a char is really just a small integer. This is why an expression such as: c + 7; is legal. The only difference is how printf interprets the value stored in the char variable c.
Set c equal to 48 and print it again.
You will notice it is now a zero again.
As you may have encountered in the previous exercise, c is pretty forgiving as to what types can be assigned to each other. Whether you realized it or not, the assignment operator, =, converts between certain types silently because they are easily compatible -- such as chars and ints. Even in the case of types that are less compatible such as ints and doubles, if you try to assign a double value to an int variable, the compiler does not complain. It simply drops the floating point (the decimal).
If you didn't try that above, try it now.
i = 2.7;
printf("int i = %lf", i);
The process of converting between variable types is called casting. If you don't have to say anything for the process to take place, it is called implicit casting. This is the case with all the math operations in c. The +, -, *, /, and = operators all implicitly cast across primitive types. The rules for implicit casting can be found in K&R appendix A6 (specifically section 5).
Now try explicit casting. This is where you tell c exactly what you want to happen. The line:
int x = (int) 3.14;
tells the compiler (but more importantly anyone reading the code)
that it is explicitly intended to convert the double (3.14) to an
integer and store it in the space x which is available for
integer data.
Why do you think explicit casting is necessary if the compiler already does it implicitly?
As hinted at, it is usually an effective way of informing anyone reading your code that you mean to do something that may be questionable such as dropping the decimal from a number. The second reason is that casting is necessary when dealing with more complicated data such as pointers and structs. The compiler will not let you convert between certain types unless you explicitly tell it that is what you want to do.
Finally, there is a danger in casting. As you experienced when playing with signed and unsigned characters, 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 will get undesired results. Also, if you cast signed numbers to unsigned types, you can encounter odd results. Remember, a signed type has one less bit (to make room for the sign bit) to represent data with.
If you called your signed integer s, your explicit cast and print statements would look something like this:
unsigned int u = (unsigned int) s;
printf("signed int s = %d\n", s);
printf("unsigned int s = %u\n", u);
* Note the use of %u to specify an unsigned int in the last printf statement.
Recall earlier that setting a char equal to 0 is not the same as setting it equal to '0'. As you may know, this is because the zero in single quotes is actually interpreted as the character zero. The character zero has the integral value 48. That means setting a char equal to 48 is the same as setting it equal to '0'.
Here is a simple program, ascii-print.c , that prints out all the ASCII values along with their corresponding characters. It uses a for loop, which we will be considering later this module.* Note that while chars can range from 0 to 127, the printable characters start at 32.
Development of laboratory exercises is an interative process. Prof. Walker welcomes your feedback! Feel free to talk to him during class or stop by his office.