switch ( integer-expression ) { case an-integer-constant : statements-for-case-1 break; case another-integer-constant : statements-for-case-2 break; ... default : default-statements }Don't forget the break statements! The integer expression must produce a value belonging to any of the integral data types (various size integers and char).
#include < stdlib.h >you can
#define INTEREST_RATE .06For integer constants, there is another way to do this, which is particularly useful if you have several related constants.
For instance, suppose you need to have some codes in your program to indicate whether a library book is checked in, checked out, or lost. Intead of
#define CHECKED_IN 0 #define CHECKED_OUT 1 #define LOST 2you can use an enumeration declaration:
enum { CHECKED_IN, CHECKED_OUT, LOST };The names in the list are matched up with 0, 1, 2, ...
Of course, this works most cleanly if the absolute values are irrelevant, just that they are different.
If you want to give specific, but distinct, values, you can do that too:
enum { Jan = 1, Feb, Mar, Apr };Jan is assigned the value 1, Feb is assigned the next value (2), etc. (You can assign two constants the same value, but it's probably NOT what you want to do -- confusing.)
Now you can use these enumeration constants, for instance, in a switch statement:
int status; /* some code to give status a value would go here */ switch (status) { case CHECKED_IN : /* do something for a checked in book */ break; case CHECKED_OUT : /* do something for a checked out book */ break; case LOST : /* do something for a lost book */ break; }
You can create an enumeration data type:
enum book_status { CHECKED_IN, CHECKED_OUT, LOST };Why bother to do this? Because you can then create variables of the enumeration type:
enum book_status status;"enum book_status" is the type, analogous to "int", and "status" is the variable name.
And why bother to do this? To get compiler support to help make sure these variables only take on prescribed values. For instance, the following will be allowed:
status = LOST;but the following will not:
status = 5;In fact, some compilers will not even allow
status = 0;
enum name-of-enumerated-type actual-enumerationThen to declare a variable of the type:
enum name-of-enumerated-type name-of-variableIt's rather unpleasant to have to carry around the word "enum" all the time. Instead, you can give a name to this type you have created, and subsequently just use that type -- without having to keep repeating "enum". For example:
enum book_status { CHECKED_IN, CHECKED_OUT, LOST }; typedef enum book_status BookStatus; ... BookStatus status;First, you define the type as before. Then use use the typedef statement, which causes "BookStatus" to be a synonym for "enum book_status".
The syntax to define a structure is:
struct structure-name { first-variable-declaration; second-variable-declaration; ... }; /* don't forget this semi-colon! */For instance:
struct student { int age; double grade_point; };Then you can declare a structure variable:
struct student stu;Notice that you have to carry along the word "struct".
To avoid this, you can use a typedef to declare a user-defined type, i.e., to provide a name that is a synonym for "struct student". For instance,
struct student { int age; double grade_point; }; typedef struct student Student; Student stu;Now you can access the pieces of a struct:
stu.age = 20; stu.grade_point = 3.0; sum_ages = sum_ages + stu.age;Notice the dot notation (analogous to indicating instance variables of objects in Java).
You can also have the entire struct on either the left or the right side of the assignment operator:
stu1 = stu2;The result is like primitive types in Java! stu1.age is changed to hold the value of stu2.age and stu1.grade_point is changed to hold the value of stu2.grade_point. However, stu2 is not changed.
*** Draw memory diagram, compare and contrast with Java.
Structures can be passed as parameters to functions:
void print_info(Student st) { printf("age is %i, GPA is %f.\n", st.age, st.grade_point); return; }Then you can call the function:
print_info(stu);But if you put the following line of code after the printf in print_info:
st.age = 2 * st.age;the change will NOT be visible back in the main program. You will have only changed the formal parameter copy, not the actual parameter.
You can return a structure from a method also. Suppose you have the following method:
Student initialize(int old, double gpa) { /* formal parameters */ Student st; /* local variable */ st.age = old; st.grade_point = gpa; return st; }Now you can call the method:
Student stu; int young = 18; double grades = 4.0; stu = initialize(young, grades);What happens is this:
The copying of formal parameters and return values can be avoided by the use of pointers, as we shall see.
data-type array-name [ size ];For example:
int ages[100];Note the difference from Java in where the size is placed!! It goes after the name of the array.
As in Java, numbering of entries begins with 0. Also, as in Java, the entries are accessed like ages[3].
If you have an array of structures, you can access a particular field of a particular entry like this:
Student roster[100]; roster[0].age = 20;
Unlike Java, you must specify the size of the array at compile time. This means you have to (over)estimate how big of an array you will need. Some of the wasted space can be reduced using pointers, as we will see.
You can declare a two-dimensional array (and higher): e.g.,
double grades[30][3];
Two things you CANNOT do: