#include < iostream.h> // The C++ I/O library /* comments in C look like this, and can go over many lines. */ // or they are single line comments like this: // PROGRAM TO ADD TWO NUMBERS main() { int i, j, k; cin >> i,j; // read in two ints k = i+j; cout << endl << "The sum of " << i << " and " << j << " is " << k; } |
TYPE | PC | SUN | COMMENT |
---|---|---|---|
long double | 80 | 64 | A very long float |
double | 64 | 64 | A double precision float |
float | 32 | 32 | A single precision float |
unsigned long | 32 | 32 | An int w/o sign |
long | 32 | 32 | A large int |
unsigned int | 16 | 32 | An unsigned int |
int | 16 | 32 | A signed int |
short int | 16 | 16 | a signed short int |
char | 8 | 8 | one byte |
int i, j, k; // Variable names must begin with an alpha. char c1,c2; // Note the order in a declaration: float x,y,z; // The type specifier (int, float) is followed double w; // by a list of variable names. |
We can assign variables of the same type:
x = y*z; i = j+k; |
Or we can use explicit conversion (Casts)
x = (float)i + y; // Changes i to a float before adding |
int i, j, k; char c1,c2; float x,y,z; double w; i = (x*j) + w; |
The assignment does the multiply using floats, then the add using doubles, the result is then converted to int.
z = (c1+j)*w; |
This assignment does the add using ints, the multiply using doubles, the
result IS then converted to float.
Operators and Precedence
Operator | Associativity | Type name |
---|---|---|
(, ) | left to right | parenthesis |
++, --, +, -, ! | right to left | unary |
*, /, % | left to right | multiplicative |
+, - | left to right | additive |
<, <=, >, >= | left to right | relational |
= =, != | left to right | equality |
&& | left to right | logical AND |
|| | left to right | logical OR |
=, +=, -=, *=, /=, %= | right to left | assignment |
Expression | Meaning |
---|---|
x = a + b*c/d - e; | (a + ((b*c)/d)) - e |
y = a%b/c*e; | ((a%b)/c)*e |
z = a = b*c; | a = b*c then z = a |
w = --a + a++; | a = a-1 then w = a+a then a = a+1 |
if (x= = y && a != b|| c>d) | if (((x= =y) && (a != b)) || (c > d)) |
x = a + b + c; | (a + b) + c |
x = f(a) + g(b); | no rule for order in which functions are called |
x = f(++a, h(a)); | no rule for order of evaluation of function arg's |
x += y -= z *= w; | x = x + (y = y - ( z = z*w)) |
cin >> x >> y >> z; cout << endl << "The answer is " << w; |
Denotation | Name> |
---|---|
\n | newline |
\0 | end of string (zero value) |
\t | tab |
\\ | backslash character |
\' | single quote |
\" | double quote |
She said, "A \ quotes the char that follows it."
on a newline, we would write:
cout << "\nShe said, \"A \\ quotes the char that follows it.\""; |
OR
cout << endl << "She said, \"A \\ quotes the char that follows it.\""; |
Notice that the field "endl" is an alternative to "\n".
Increment and Decrement Operators --, ++
++i; // means i = i + 1; i++; // also means i = i + 1; |
The position of the operator determines which value of i is used in an expression:
a = ++i + j++; |
Here i is incremented before it is used, and j is incremented after it is used.
If i=5 and j=8 before the assignment, we get i = i+1, then a = i + j, then j = j+1. So after the assignment, i=6, a=14, j=9
Avoid overly complex uses of these operators, such as:
a = x[++i] - y[10 - i--]; |
int a,b,c; if (b!=c) statement // same as if (b-c) statement if (b!=0) statement // same as if (b) statement if (b==c) statement // same as if (!(b-c)) statement |
if (Boolean expression) statement |
Or
if(Boolean expression) statement1 else statement2 |
if (a < b) x = y; // Note semicolon is part of statement1 else x = z; // so it appears even before else if (a == b) x = y; // Note == for the relation equals else if (a != c) x = z; if ((a < b) && (b < c)) u = v; // C uses short circuit evaluation else if (a >= b) u = -v; // of Boolean expressions. Once the else u = 0; // value of the result is known, // computation of the Boolean stops |
{statement statement statement ... }
if (a > b) { e++; // Increment e. if (c == d) { x = y; cout << "\nb < a"; } } else { cout << "\nb >= a"; // Indentation adds readability. x = -y; } |
char c1, c2, ck[20], cm, cn[10]; // means ck[0 ... 19], cn[0 ... 9] |
You can have rows of rows :
double xx[10][20]; // means xx[0 ... 9][0 ... 19], 200 elements for (i = 0; i < 10; i++) for (j = 0; j < 20; j++) xx[i][j] = 0.0; |
char message[17] = "\nThe end is near"; // includes 1 char for end marker message[1] = 't'; // the array begins in message[0] |
You can only initialize an array when it is declared, as in message above and:
int table[] = {0,1,2,4,8,16,32,64}; // size is figured by compiler int mat[2][3] = {{1,2,3},{4,5,6}}; |
for ( i = 0; i < 100; i++) statement // means: for i starting at 0, // while i < 100, increment // i by 1 on each iteration for( ; ; ) statement // means: loop forever for(i = 0;i < 20;i++) a[i] = 0; // means: a[0,1, ..., 19] = 0 while(Boolean expression) statement // execute statement zero or more // times while expression is true do statement while(Boolean Expression) // statement is executed at least // once, and repeatedly while // expression is true |
Example:
i = 0; while(a[i]) b[i] = a[i++]; // copies a into b until 0 is found |
Assignment statements return values, so we can do the following:
i = 0; while((b[i] = a[i++])); // copies a into b until 0 is copied |
Note the null statement. All the work is done in the assignment.
The parens around the assignment are needed, otherwise it would be
treated as a Boolean expression; "=" is not a Boolean operator
"Give me a break"
i = 0; for( ; ; ){ a[i] = b[i]; if (!a[i++]) break; // get out of for loop } i = 0; while (b[i]!=27){ if (b[i] == 0) break; // get out of while loop a[i] = b[i++]; } |
char ch; switch (ch) { case 'a': cout << ("\nIt was an a"; - break; // Goto end of switch statement case 'b': cout << "\nIt was a b"; - break; // Don't leave out the break /* code for other cases goes here */ default: // For cases not covered cout << "\nIt wasn't alphabetic"; break; } // End of switch |
return_type function_name(argument_list) statement |
Example:
int sum_square(int i, int j) { return i*i + j*j; } |
All arguments are passed by value, so the caller's arguments cannot be changed. If the function returns something there must be a return statement. This function would be called as follows:
sum_sq_ab = sum_square(a,b); |
Here, a and b can't be altered by the function.
Functions need not have arguments and need not return results:
void print_results(void) /* void means no arguments */ { cout << "\nThe earnings details are " << salary << ": " << tax; } |
Local variables can be declared in functions. Their scope is the body of the function:
int magnitude_ave(int a, int b, int c) { int sum; // Scope of variable name is limited to // block in which it is declared. sum = a + b + c; if (sum > 0) return sum/3; // Can have many return statements. // When one is executed the rest else // of the fn's code is skipped. return - sum/3; } |
The ANSI standard requires that a program file begins with prototypes for all functions declared in that file. The prototype for the above function would be:
int magnitude_average(int, int); |
OR
int magnitude_average(int x, int y); |
You can give your arguments names in the prototype. If you use
prototypes, then your function bodies can appear in any order in the
program source file.
Recursion
long factorial(int num){ if (num <= 1) return 1; // terminal condition else return num*factorial(num-1); // recursive call } |
The call:
factorial(3); |
returns 3*factorial(2) = 3*2*factorial(1) = 3*2*1 = 6
We must beware of situations where recursion seems the right technique, but may cause very long run time. The Fibonacci numbers are the infinite sequence: 1, 2, 3, 5, 8, 13, 21, 34, 55, where Fib(n) = Fib(n-1) + Fib(n-2). This suggests the following recursive function:
long fib(int n) { if(n<=1) return 1; else return fib(n-1) + fib(n-2); } |
The run time of this function is exponential in num, because the procedure keeps repeating the same calculations:
fib(6) = fib(5) + fib(4) = fib(4) + fib(3) + fib(3) + fib(2) = fib(3) + fib(2) + fib(2) + fib(1) + fib(2) + fib(1) + fib(1) + fib(0)Notice that the width of this tree doubles on each level. Its height is n. That implies 2n calculations.
A much better way is to iterate from 1 upwards:
long fib(int n) { long last=1, prev=0, newfib,i; // The run time of for(i = 0;i < n;i++) { // this function is newfib = last+prev; // O(n), linear prev = last; // in n. last = newfib; } return last; } |
int i, j, k, *pi, *pj; // 3 ints and 2 pointers to ints pi = &i; // pi is assigned the address of i pj = &j; // pj is assigned the address of j *pi = 3; // read as "what pi points to, becomes 3" *pj = *pi; // Same as j = i; |
An array name is really a pointer:
int a[10]; // Reserve space for 10 ints and // assign address of the first of them to a. int *b; // Declare a pointer to an int. b = &a[0]; // a and b are now identical in meaning, // b[3] is the same as a[3]. *b = 23; // Same as a[0]=23 or *a=23 or b[0]=23 *(b+1) = 67; // Same as a[1]=67 or *(a+1)=67 or b[1]=67 |
Pointers are especially useful in dynamic (heap) storage:
int *pi; pi = (int *)malloc(100*sizeof(int)); |
This statement reserves space for 100 ints on heap, pi points to the first. When calling malloc():
We have to give the size in bytes of the space needed: 100*sizeof(int)
We also have to cast the result to the pointer type needed: (int *)
Now we can write pi[k] or *(pi+k) to access a value from the array. The array remains on the heap until it is freed by free(pi).
Since function arguments are passed by value, we have to use pointers as arguments if we want to modify values owned by the caller :
void function swap(int *p1, int *p2) // Pass in the { // addresses of the int temp; // actual arguments. temp = *p1; // To swap the ints *p1 = *p2; // u and v, the call *p2 = temp; // would be } // swap(&u,&v); |
When passing arrays into functions, remember, an array name is really a pointer to the first element of the array.
void bubble_sort(int *pa, int size) { int i,j; for(i=size -1; i >= 0; i--) for(j=0; j < i; j++) if(pa[j] > pa[j+1]) swap(&pa[j],&pa[j+1]); } |
To sort the integer array table[20], the call would be:
bubble_sort(table, 20); |
OR
bubble_sort(&table[0], 20); |
struct Date { int day, month, year; char month_name[4]; }; // Note the semicolon |
Then we can declare structs as follows:
struct Date day1; struct Date day2 = {4, 11, 1933, "apr"}; // Can initialize them // just like arrays. struct Date *pd; // Can have pointers to them struct Date my_dates[20]; // Can have arrays of them |
We refer to elements of a struct by using the syntax struct_name.element_name:
day1.day = 5; my_dates[2].year = 1968; struct Date *pd; // A pointer to a Date. pd = (Date *)malloc(sizeof(Date)); // Make a Date on the heap // and make pd point to it *pd.month = 11; // and set its month to 11 |
When addressing a field of a struct via a pointer, we can use the right arrow (made from a minus sign followed by a greater than sign):
pd -> month = 10; // same as *pd.month = 10 |
void print_date(struct Date d) // can't change any part of d { // since it is passed by value cout << month_name << day << year; } |
To change the fields of the caller's struct, a pointer must be passed in:
void add_one_to_day(struct Date *ptrd) { ptrd -> day++; } |
Then in main(),
struct Date first = {1,1,1995,"Jan"}; add_one_to_day(&first); |
struct List_node { int data; struct List_node *next; }; |
Then we can declare a pointer to one of these:
struct List_node *head; /* points to the first node */ |
Here is a function that searches a linked list for a data value:
struct *List_node search(int key_value) { // Returns a pointer struct List_node *ptr = head; // which is NULL if while(ptr!=NULL) // search fails, or if (ptr -> data == key_value) // points to the node return ptr; // containing else ptr = ptr -> next; // the key_value return NULL; // if found } |
struct card { // definition int value; char suite[8]; }; typedef struct card Card; // declare synonym |
We can combine the definition of the struct and the typedef:
typedef struct { int value; char suite[8]; } Card; |
typedef struct Card { int value; char suite[8]; }; |
Either way, we can declare Cards as follows:
Card ace_spades = {1,"spades"},wild; |
The struct tag is not needed for typedefs in declarations or function argument lists.
typedef double Matrix[4][4]; void mat_mult(Matrix m1,Matrix m2,Matrix m3) { int i,j,k; for(i = 0;i < 4;i++) for(j = 0;j < 4;j++) { m3[i][j] = 0.0; for(k = 0;k < 4;k++) m3[i][j] += m1[i][k]*m2[k][j]; } } |
#include < math.h> // sin(), cos(), log(), etc. #include < stdlib.h> // malloc(), free(), exit() and lots more #include < string.h> // string copy and compare etc #include < time.h> // time structs and functions #include < assert.h> // support for assertions #include < ctype.h> // tests on chars and char conversions |
#define pi 3.14159 // replace all occurrences of pi by 3.14159 |
#define CIRCLE_AREA(x) PI * (x) * (x) |
then in the program text:
area = CIRCLE_AREA(4); // area = pi * (4) * (4) |
Since only constants are involved, the expression is evaluated at compile time. The parens around the variables in the #define statement force correct evaluation when the macro is called with an expression:
area = CIRCLE_AREA(c + 2); // area = pi*(c+2)*(c+2) |
We also have #undef to undefine a macro name.
#if !defined(NULL) // evaluates to 1 if defined, #define NULL 0 // and to zero if not #endif |
This code defines NULL to be 0 if it isn't already defined.
Every #if ends with a #endif.
#ifdef and #ifndef are short for #if defined and #if !defined.
We also have #elif and #else.
Say we have a header file called ray.h that is #included in file1.c and file2.c, and file2.c #includes file1.c. We don't want the compiler to read the header file twice, so we do the following:
#ifndef _rayh // On the first reading of this file, _rayh is not #define _rayh // defined, so this line defines it and the rest // of the header file is read /* body of header file goes here */ // If the file has already been read, _rayh is defined #endif // and the rest of the header file is skipped |