Through the 1960s, and into the early 1970s, operating systems were written in assembler language. The C programming language was originally developed as a language to replace assembler in systems programming. It was very successful, making system code portable and easier to write and read.
C began to grow in popularity not just for systems programming, but as a general purpose programming language. Today C is one of the most used programming languages. The C programmer must realize, however, that C was designed to replace assembler language, and that in several important ways, it retains a very low level view of the machine. This language summary will discuss, among other things. some of the low level characteristics of C.
The C++ programming language was designed as a higher level version of C, providing support for object-oriented programming. Some features of C++ are discussed in this summary, and some are discussed under classes, later. C++ retains almost all of C, however, and therefore is a mixture of low level and high level features. Often, the programmer has a choice of using low level or high level approaches.
int x;you have declared a variable x of type int. If you write
Int x;you probably get a compile error, since there is no type Int.
/* Here is a C-style comment */ // Here is a C++ style comment.
apostrophe the_list horse43 xare all names that can be given to things. Names cannot contain spaces. You should avoid names that begin with an underscore, since they are often used to name special things in the system.
Certain words are special in the language, and you cannot use them to name things. These reserved words are shown in bold face in programs in this summary.
Address | Value (for example) | |
0 | 20 | |
1 | 46 | |
2 | -5 | |
3 | 0 | |
... | ... |
Typically, each memory address holds one byte, which can hold a number from -128 to 127. Some operations use several (typically 4) consecutive bytes called a word. In a four-byte word, we can store an integer from -2,147,483,648 to 2,147,483,647.
The operating system breaks the memory for a program up into three parts: the static part, the stack and the heap.
When a function is called, it is automatically allocated as much memory as it needs in the stack. When the function leaves, all of its memory in the stack is automatically deallocated, to be reused later for another function.
An expression can both compute a value and have an effect. When an expression has an effect, that effect is called a side-effect. For example, if variable n has value 5, then expression n++ has value 5, but also has the side-effect of setting n to 6.
Any expression can be a statement. So, for example, n++ is an expression, but you can write it as a statement:
n++;When an expression is used as a statement, the expression is computed, but its value is ignored.
You can create one large statement, called a compound statement, or a block, from several smaller ones by enclosing the statements in {...}. For example,
{ n++; m++; }is a compound statement. The statements in the compound statement are executed in the order in which they are written.
Note: Each statement generally ends on a semicolon. An exception is a compound statement, which ends on }.
Operations on integers include
+ | addition, |
- | subtraction, |
* | multiplication, |
% | x % y is the remainder when you divide x by y, |
/ | x/y is the quotient when you divide x by y (an integer) |
Caution: 2/3 is 0, since the result of dividing two integers is an integer.
enum Color { red, orange, yellow, green, blue, indigo, violet };creates a type called Color (in C++) or enum Color (in C or C++). The compiler defines red to be 0, orange to be 1, etc., through violet, which is defined to be 6.
float | Real numbers stored in four bytes. These offer about 7 digits of precision. |
double | Real numbers stored in eight bytes. These offer about 15 digits of precision. |
long double | Real numbers stored in more than eight bytes. This is not available with all compilers. |
Operations on floating point types are the same as those for integers, except that % is not available, and division produces a floating point number. 2.0/3.0 yields 0.666666666666667.
Each pointer has a type. If T is a type, the type T* is the type of a pointer that points to something of type T. For example, type int* is the type of a pointer to an int.
Operations on pointers are as follows.
* | *p is the content of the memory at address p.
The number of bytes that you get depends on the type of p.
For example, if p has type char*, then *p is a one-
byte quantity (of type char).
If p has type long int*, then *p is a four-byte quantity
(of type long int.)
If p has type T*, then *p always has type T, for any T. |
|
+ | Suppose that p is a pointer pointing to a type that occupies b bytes, and suppose that k is an integer. Then p+k is memory address p + bk. For example, suppose that p is a pointer with memory address 100. If p has type char*, then p+2 is a pointer, also of type char*, with memory address 102. If p has type long int*, then p+2 is a pointer, also of type long int*, with memory address 108. Notice that, if p has type long int*, then p+1 points to the next long int in memory, just after the one pointed to by p, without overlap. | |
- | You can subtract an integer from a pointer. The result is similar to addition. For example, if p has type int*, then p-1 points to the int that occurs just before the int pointed to by p. | |
[ ] | p[i] abbreviates *(p+i). So p[0] is the same as *p. p[1] is the thing in memory just after the thing pointed to by p. |
Note: Memory address 0 is not available to application programs. It is used as a special pointer value, called the null pointer. Normally, NULL is another name for 0. You use it when you want the null pointer.
T x;You can declare several variables at once. For example, to declare char variables c and d, and long int variables x and y, you can write
char c,d; long int x,y;When you write variable declarations outside a function, the variables will be allocated in the static area of memory. When you write them inside a function, the variables are automatically allocated in the function's stack frame, in the run-time stack. So writing
long int n;inside a function causes four bytes to be allocate in the stack, when the function encounters this variable declaration.
In C++, a variable can be declared anywhere. In C, a variable can only be declared outside a function or at the start of a function or block.
You can declare variables inside blocks. A variable declared in a block is only accessible within the innermost block that contains the declaration. For example, if you write
{int x; ... }then you can use x within this block, but not outside. This is a convenient way to declare variable that are only used for a short part of a function.
long int n = 0;not only allocates memory for variable n, but it sets that memory to 0. Outside a function, the initial value of a variable must be a constant, such as 0. Inside a function, the initial value of a variable can be given by any expression. For example, you can write
long int n = a + b;as long as the values of a and b are known.
Sometimes you want a "variable" that you never intend to change, after giving it an initial value. you can use modifier const for such variables. For example,
const long int size = 40;declares size to be 40, and does not allow size to be changed.
You can declare pointer variables to be constant, but the const modifier means something different for a pointer from what it means for a nonpointer. If you declare
const char* p;then you are allowed to change p, but you are not allowed to use p to change the memory that p points to. So you can get p[1], but you cannot set p[1] = 'a'.
Caution: If you do not initialize a variable, it has a "junk" value. Do not use the value of a variable before you put something in the variable.
n = a + 1;computes a+1 and puts the result in variable n. Statement
n = n + 1;adds one to the value of variable n, and puts the result back into n.
An assignment is an expression. That is, it has a value. The value is the value just put into the variable. So, for example,
n = 0is an expression that has value 0, but also has a side-effect: it puts 0 in variable n.
Note: you only include the type of a variable when you are creating the variable. So
long int n = 0;creates variable n and sets it to 0. But
n = 0;sets a previously created variable n to 0.
When you assign a value to a pointer variable, you change the pointer variable itself. If you what to modify the memory pointed to by p, assign to *p. For example,
*p = 20;goes to the memory whose address is in variable p and puts 20 there.
Abbreviation | Meaning | |
n += k | n = n + k | |
n -= k | n = n - k | |
++n | n = n + 1 (where the value of expression ++n is the value of n after the assignment) | |
n++ | n = n + 1 (but the value of expression n++ is the value of n before the assignment) | |
--n | n = n - 1 (where the value of expression --n is the value of n after the assignment) | |
n-- | n = n - 1 (but the value of expression n-- is the value of n before the assignment) |
int *x, *y;then you declare x and y each to be pointers to integers. If you write
int *x, y;then you declare x to have type int*, but y to have type int. There is no * on y, so y is not a pointer. Spacing in your program does not affect this. So if you write
int* x, y;it might look like x and y are each pointers, but in fact only x is a pointer; y is an int. To avoid this problem, you might want to declare only one pointer variable per line. Alternatively, use a typedef, discussed in Section 1.7.
What if you have a pointer variable p, and you get its address? If p has type int*, then &p has type int**. That is, it is a pointer to a pointer. &p is the address of a variable that contains a pointer value.
Caution: Be careful how you use the address of a variable. If the variable is in the stack, then it will be deallocated as soon as the function that created that variable returns. The address will no longer be valid.
To declare an array, you must give the size. Write
int x[20];to declare an array x of 20 integers. Some versions of C++ allow the size to be any expression, but others require the size to be a constant, not something that you compute. When the size is a constant, you almost always use a named constant to select the size. That way, it is easy to modify the size later. You write, for example,
const int namesize = 40; char name[namesize];If you are not declaring an array, but are merely refering to one that is created elsewhere (as you do, for example, in a parameter list of a function heading) you do not need to give the size. You can write int x[] to indicate that x is an array of integers.
struct MyStruct { long int size; char* name; };In C++, this declaration causes you to get a type called MyStruct. You would write
MyStruct s;to declare a variable s of type Mystruct. (Variable s occupies eight bytes of memory, four for the long int and four for the char*.)
In C++, you can write the word struct explicitly when you declare a variable, as in
struct MyStruct s;and this form is required in C.
The parts of a structure (size and name in the example) are called fields. To get the fields of a structure, you use "dot" notation. s.name is the name field, and s.size is the size field. For example,
s.size = 6; s.name = "George";puts values into the structure. Note that no values are in the structure until you put something there.
You can copy the value of one structure into another. For example, if you declare variables s and t as having type MyStruct, then
s = t;has the same effect as
s.size = t.size; s.name = t.name;That is, the entire contents of structure t is copied into the memory occupied by structure s. If you declare structure
struct Complex { double re, im; };and you declare
Complex z,w;then
z = w;has the same effect as
z.re = w.re; z.im = w.im;This is an important difference between arrays and structures. To put it succinctly, the size of a structure is part of its type, but the size of an array is not part of the array's type. So copying a structure copies all of the memory occupied by the structure. But if p and q are pointers to arrays, then
p = q;only makes p point to the same array as q. It is the pointer that is copied, not the arrays themselves.
Note: A structure type is not the same as a structure variable. It would make no sense to write MyStruct.name since MyStruct is a type, not a variable, and has no name field to select. To use a structure, declare a variable of the structure type.
struct Complex { double re,im; Complex() { re = 0.0; im = 0.0; } };Another option is to ask for the initial values when a variable is created. That can be done by creating a constructor that has parameters. For example, you might declare the complex number structure as follows.
struct Complex { double re,im; Complex() { re = 0.0; im = 0.0; } Complex(double r, double i) { re = r; im = i; } };Now
Complex z, x(1,2);declares variable z to be initialized by the constructor without parameters (and hence to have both its real and imaginary parts initialized to 0) and x to be initialized the the constructor with parameters (and hence to have a real part initialized to 1, and an imaginary part initialized to 2).
MyStruct* p;Initially, p has a junk value, so we would not want to look to see where it points. Let us presume that p has been given a value somehow, and we want to get the size field of the structure to which p points. That structure is *p. We can write (*p).size. For example,
n = (*p).size;The parentheses are required. Writing *p.size will not get you what you want. There is a convenient abbreviation that you can use to avoid the parentheses. p -> size abbreviates (*p).size. For example, you can write
n = p->size;Note: The -> notation is used only when you have a pointer to a structure. If s is a structure, not a pointer, then use dot notation such as s.name, not s->name.
Address | Content | |
p | 'a' | |
p+1 | 'b' | |
p+2 | 'c' | |
p+3 | 0 |
Note that the 0 is not the character '0', but is the integer 0. It is called the null character. When a null character is at the end of a string, it is called the null terminator of the string. The null character can be written '\0'.
When you write "abc" in a C/C++ program, the compiler allocates four bytes, and fills them as shown above, with 0 in the last byte. The type of "abc" is char* -- a pointer to the first byte of the string.
In general, strings are stored in two parts: the pointer, which serves as the handle of the string, and the memory that is pointed to, which holds the content of the string. When you deal with strings, you must be aware of these two parts. Even though you often think of a variable of type char* as holding a string, it does not actually point to any string until you set it to point to appropriate memory.
If you want to put a double quote character in a string constant, precede it with a backslash.
typedef oldT newT;declares name newT to be a new name for a previously declared type oldT. For example, you might declare a pointer type.
typedef char* charptr;Now, if you write
charptr x,y;both x and y will have type char*. You can get around some difficulties of C/C++ using typedefs. For example, there is no type for true/false data. Instead, a truth value is just an integer, with true being 1 and false being 0. You can write
typedef char bool;to define type bool to be the same a char. Some implementations of C++ define this type bool for you. We will assume a file bool.h that defines this. Additionally, we will presume that bool.h defines TRUE to be the constant 1, and FALSE to be the constant 0.
new Tallocates enough memory in the heap for something of type T, and returns the address of that memory, of type T*. For example, to get a pointer to a new MyStruct, you might write
MyStruct* ms = new MyStruct;As you can see, ms is a variable of type MyStruct*. That is, it is a pointer to a MyStruct. It is initialized to point to newly allocated memory in the heap. As another example, consider
Complex* c = new Complex;which creates a variable c of type Complex*, and initializes it to the address of some newly allocated memory in the heap. It is very common to have pointers to structures, and to allocate those structures in the heap. If you like, however, you can allocate a variable of a simple type in the heap. For example,
long int* p = new long int;You can allocate an array in the heap as well. To allocate an array of 20 chars, you might write
char* p = new char[20];Notice that p has type char*. Here, p will be set to the address of the first byte of the array. To set the third byte of the array to 'a', you would write
p[2] = 'a';(Recall that the first byte is p[0].) One nice feature about allocation in the heap is that you can compute the amount of memory to allocate. If integer variable n has a value, then you can write
char* s = new char[n];to allocate n bytes in the heap.
char* p = (char*) malloc(20);Note the "cast" (char*), which tells the compiler to consider the pointer to be a char* pointer. malloc returns a pointer of an unknown kind, and you must tell the compiler what kind it should be. Be very careful with malloc. It allocates a given number of bytes. If you want to allocate a single MyStruct value in a C program, you would write
struct MyStruct* ms = (struct MyStruct*) malloc(sizeof(struct MyStruct));where sizeof(T) is the number of bytes needed by something of type T. As you can see, the C++ new operator is much nicer.
delete p;to return that memory to the system. If you allocated using new and you allocated an array, as in
char* p = new char[30];then delete the memory using
delete[] p;If you allocate that memory using malloc, use
free(p);to return the memory to the system. It is very important that you deallocate memory in a way that complements the way in which you allocated it. If you used malloc to allocate, use free to deallocate. Never deallocate a pointer that points into the run-time stack.
Be very careful about deallocating memory. Keep in mind that you can have more than one variable that holds a pointer to a given piece of memory. If you deallocate the memory, all of those pointers become stale. They continue to hold memory addresses, but the memory to which they refer does not belong to you any more. You have no idea what that memory will be used for after it has been deallocated. A pointer that points to deallocated memory is called a dangling pointer. Problems that occur from the use of dangling pointers are among the most devastating, and most difficult to eliminate, of all the problems that you can have. By avoiding them with care, you will not be subjected to the pain of finding them by laborious debugging. (Note that there is nothing terribly wrong with just having a dangling pointer in your program, as long as you do not use it.)
If you fail to deallocate memory that you have allocated, and you let that memory become inaccessible to you, the system will not take that memory back automatically. The system will presume that your program still needs the memory. The result is a memory leak. Memory leaks are quite benign for programs that do not use much memory, but can be deadly in long runs, when your program will run out of memory.
if(condition) statement else statementThe first statement is done if the condition is true, and the second is done if the condition is false. The else part is optional. If it is omitted, then nothing is done when the condition is false. Typically, the statements are compound statements. Here is an example.
if(x == 0) { y = 0; } else { y = 1/x; }The parentheses around the condition are required.
Note: The condition being tested is any integer or pointer expression. If the condition has value 0, then it is considered to be false. If the condition has any nonzero value, then it is considered to be true. This can be confusing. For example, cin.fail is a function that will tell you whether the most recent operation on cin failed. In C/C++, a function is represented by the address where the machine-language code of the function is loaded. Since an address is a pointer, a function is a pointer. If you write
if(cin.fail) { ... }you are not running function cin.fail at all, but are asking whether the pointer cin.fail is not null. Of course, it is not. The correct way to ask whether the most recent operation on cin failed is
if(cin.fail()) { ... }
== | equality test |
!= | inequality test |
> | test for greater |
< | test for less |
>= | test for greater or equal |
<= | test for less or equal |
&& | 'and'. Expression A && B is evaluated by first evaluating A. A has value 0, then A && B also has value 0, and B is not evaluated. Otherwise, B is evaluated; if B has value 0, then A && B has the same value 0, and otherwise has value 1. |
|| | 'or'. Expression A || B is evaluated by first evaluating A. If A has a nonzero value, then A || B has value 1, and B is not evaluated. Otherwise, A || B has value 1 if B is nonzero and value 0 if B is zero. |
! | 'not'. Expression !A is 0 if A is nonzero, and is 1 if A is 0. |
Caution: You can use the comparison operations on pointers. They compare addresses. For example, if p and q each have type char*, then testing whether p == q will test whether p and q are the same pointer. It will not test whether the strings pointed to by p and q are the same.
Caution: A frequent error is to confuse = and ==. If you write
if(x = y) ...then you will not get a test to see if x and y have the same value. Instead, you will get an assignment, putting the value of y into x, and that value will be tested. This is a serious error. You should learn to check your program carefully for this. One way to avoid this problem is, when doing a comparison that involves a constant, to write the constant first. That way, if you accidentally write = instead of ==, you will get a compile error. For example, write
if(0 == x) ...
condition ? val1 : val2is val1 if condition is nonzero, and is val2 if condition is 0. For example, expression
x > y ? x : yevaluates to the larger of x and y.
switch (x) { case -1: printf("Less than zero\n"); break; case 0: printf("Equal to 0\n"); break; case 1: printf("Greater than 0\n"); break; default: printf("Not one of the things I know about\n"); }
Note: You must include break; after each case. If you do not, then the program will keep going into the next case. For example, if you run the following with day = 5
switch(day) { case 5: printf("five gold rings\n"); case 4: printf("four calling birds\n"); case 3: printf("three french hens\n"); case 2: printf("two turtledoves\n"); case 1: printf("and a partridge in a pear tree\n"); }the program prints
five gold rings four calling birds three french hens two turtledoves and a partridge in a pear treeNote: If there is no default case, then nothing is done if none of the cases match.
Note: You can have two or more cases do the same thing. Just put them in a row, as in
case 4: case 5: text for these casesNote: You can only use a switch when the type of the value being tested is an integer type.
while (condition) statementdoes the statement (called the loop body) repeatedly until the condition is tested and found to be true (nonzero). For example, suppose that s is a pointer to a null-terminated string. The following sets n to the length of string s.
char* p = s; n = 0; while(*p != 0) { p++; n++; }The condition is tested before the loop is entered for the first time. If the condition is false immediately, then the loop body is not performed at all.
do statement while (condition);does the statement repeatedly until the condition is true (nonzero), but tests the condition for the first time only after it has already done the statement once.
for (A; B; C) statementabbreviates
A; while (B) { statement C; }For example, here is a statement that prints the numbers from 0 to 9.
for(i = 0; i < 9; i++) { cout << i << endl; }This for-loop abbreviates
i = 0; while(i < 9) { cout << i << endl; i++; }Note: A for-loop has just three parts, separated by semicolons, within its parentheses. You can omit any part, but must not omit the semicolons. If you omit the test (the middle part), you get an infinite loop. Note: In C++, if you declare any variables within the parentheses after for, those variables are only available within the for-loop. In C, those variables can be used after the for loop as well. (Some implementations of C++ behave like C in this respect.)
return-type function-name(parameters) { statements }Within the statements, you can use a return statement to cause the function to stop computing and to return a value. Here is an example, a function that returns three times its parameter.
int triple(int x) { return 3*x; }To use function triple in an expression, just provide a value for the parameter. For example, statement
n = triple(5);sets n = 15. Note: In the definition of function triple, parameter x is called a formal parameter. It is just a place holder. In the expression triple(5), the number 5 is called the actual parameter. It is the value that is used in place of the formal parameter.
You can use functions in arbitrarily complicated expressions. For example, statement
m = n + triple(k + 2);sets m = n + 3(k+2). Another example is a function that computes the length of a null-terminated string. We use the loop from the loop section.
int length(char* s) { char* p = s; int n = 0; while(*p != 0) { p++; n++; } return n; }To use the length function, just give it a pointer to a null-terminated string. For example,
r = length("abcd");would set r = 4.
Here is a function that takes two parameters, and returns the larger one. Note that the parameters are separated by a comma, and each must have a type.
int max(int x, int y) { if(x > y) return x; else return y; }For example,
r = max(5,8);would set r = 8. We can use function max to create another function max3, which computes the largest of three values.
int max3(int x, int y, int z) { return max(x, max(y, z)); }
int max3(int x, int y, int z);In a prototype, you may include names of parameters, as is done above, or you can omit them, as in
int max3(int, int, int);
void exit(int status);You do not need to have a return statement in a function that returns void. If you like, you can include one, but it should just say
return;
Call-by-value. Call-by-value is the default calling mode. When you pass a parameter, the value of that parameter is sent to the function. The formal parameter acts as a variable local to the function that is initialized with the value of the actual parameter. If a structure is passed by value, the contents of the structure is copied into the formal parameter.
Call-by-pointer. Call-by-pointer is a special case of call-by-value in which the value that is passed is a pointer. When you have a pointer, you can do whatever you like with the memory that the pointer points to. For example, consider function increment.
void increment(int* p) { (*p)++; }Function increment adds one to the variable that points to. For example, in
{int x = 0; increment(&x); ... }the value of x would be 1 just after the call to increment. Notice that we must pass a pointer to function increment, so we use &x to get a pointer to variable x. If you already had a pointer, you would just pass that pointer value. For example,
{int* p = new int; *p = 0; increment(p); ... }Here, after the increment call, variable *p holds 1. (Variable p holds a pointer to variable *p.) What would happen if we did this?
{int* p = 0; increment(p); ... }The line int* p = 0 sets pointer variable p to 0. So p contains a null pointer. When function increment does *p, it will cause the program to abort. (You will get a memory protection error, since memory address 0 is not accessible to you.)
Call-by-pointer is the default mode for arrays, since an array is the same as a pointer to its first member. Sometimes, you want to pass an array as a parameter, but do not want to allow the array to be changed. To do that, use a constant pointer. For example, the prototype for the function length written previously should really have been
int length(const char* p);since the length function does not change the array that is passed to it.
Call by Reference. When you pass a variable as a parameter, it can be awkward to deal explicitly with pointers. C++ (but not C) has a way to pass variables in a more convenient way. Use & instead of * in the type of the parameter. Then the parameter is a reference parameter, and it is thought of as a variable that is being passed. For example, function increment can be written using call-by-reference.
void increment(int& x) { x++; }Notice that variable x is now just an integer variable, not a pointer variable. To use this new increment function, you do something like this.
{int n = 0; increment(n); ... }You do not pass &n now. You just pass n as the actual parameter. The compiler passes the variable n to function increment. Within function increment, formal parameter x just names variable n. So after the call to increment, n has value 1.
When the compiler implements call-by-reference, it really just does call-by-pointer, but it inserts the & and * operations where needed automatically.
Return-by-pointer. You can have a function return a pointer. Then, the caller gets access to the memory that the pointer points to. A pointer returned by a function should usually point into either static memory or into the heap. Returning a pointer into the stack is asking for trouble, except when carefully controlled. Here is a function that allocates an integer variable in the heap, sets it to 0, and returns a pointer to the new integer variable. It also checks that the system did not run out of memory.
int* allocInt() { int* p = new int; if(NULL == p) exit(1); else { *p = 0; return p; } }
Return-by-reference. In some circumstances, you would like a function to return a variable, so that a function call expression can be used just like a variable. In such circumstances, you use return-by-reference. Often, the reference that is returned is the same as one of the reference parameters. Here is a function that increments a variable, and returns the variable.
int& increment(int& x) { x++; return x; }Now you can write increment(increment(x)) to add two to x, if you were so inclined.
str << xprints the value of x onto stream str, and returns str by reference. You can write many different kinds of things, such as integers, characters and strings. Because expression str << x returns str, you can perform several writes in one statement. For example,
str << x << y;is understood to mean
(str << x) << y;which first prints x on stream str, then prints y on the same stream.
Stream cout refers to the standard output (usually the terminal). So
int n = 2; char c = 'a'; cout << n << c;writes 2 and a, with no space between them. You can include null-terminated strings as well, as in
cout << "Thank you for running me" << endl;The endl constant is just "\n". Some other operations using cout are as follows.
ofstream myout("myfile:, ios::trunc | ios::out);declares variable myout of type ofstream. It also uses a constructor that ties this variable to the system file called "myfile". The file will be emptied, if it already exists. Anything that you write will be written into that file. When you are done writing to the file, close it, using the close function.
To write in C style, you can use library functions printf and fprintf. Include header file stdio.h to use these. The first parameter to printf is a format, a string that tells what the output should look like. Within the format are sequences that start with %, and that indicate that the value of the next parameter should be put here. For example,
int m = 3; int n = 5; printf("I have n = %d and m = %d\n", n, m);would print
I have n = 5 and m = 3
Here are some of the formats that are available.
FILE* f = fopen("filename", "w");to open a file for writing. Now use
fprintf(f, format, ...);to print on the file. Finally, to close the file, use
fclose(f);For example, here is a function that writes file "test.out", putting string
abcdefghijklmnopqrstuvwyzin the file.
void print_alphabet() { char c; FILE* outf = fopen("test.out", "w"); for(c = 'a'; c <= 'z'; c++) { fprintf(outf, "%c", c); } fprintf(outf, "\n"); fclose(outf); }See documentation for fopen for more on opening files.
int n; char c; cin >> n >> c;reads an integer, and puts it into variable n, and then reads a character, and puts it into variable c. If you read a string (char*) using >>, characters will be read up to a space or newline, and put into the string. Be sure that there is enough room in the arrray! If you read a character, you will get one character, except that white-space characters (blank, tab, newline) are skipped.
Some other operations using cin are as follows.
ifstream f(name, ios::in);creates variable f, a stream bound to file name. You can use the same operations on f as on cin. For example,
ifstream ins("test.txt"); int k; ins >> k; ins.close();creates a stream called ins and reads an integer from it. When you are done with a stream, close it, as is done above. But don't close it until you are done reading from it. The example only wants to read one integer, and no more.
scanf("%d%d", &m, &n);Notice the need to use &. The parameters after the format are call-by-pointer. If you use format %s, then a string will be read up to a space or newline.
To read from a file, you must open the file for reading, and use fscanf. For example,
int n; FILE* inf = fopen("infile.txt", "r"); fscanf(inf, "%d", &n); fclose(inf);reads a single integer from file infile.txt. A handy function is gets. gets(f,s) reads a line from file f, and puts it into array s. The newline character in s is replaced by a null character, so that s becomes a null-terminated string.
#includethe preprocessor inserts the contents of file stdio.h in place of the include line. Include lines can have the file name enclosed in <...> or in "...". If you use <...>, the preprocessor looks for the file in the directory that has standard header files. If you use "...", the preprocessor looks for the file in the current directory. The preprocessor provides a macro facility. If you write
#define NULL 0for example, each occurrence of NULL will be replaced by 0 by the preprocessor. Common substitutions are
#define TRUE 1 #define FALSE 0We assume that header file bool.h define TRUE, FALSE and NULL. Note that there is no semicolon. If you write
#define TRUE 1;then each occurrence of "TRUE" gets replaced by "1;", which is probably not what you want.
Note: TRUE will only be recognized by the preprocessor when it is not part of a longer word. If you write TRUE_LIES, for example, the preprocessor will not make a change.
Macros can have parameters. You can write
#define max(x,y) ((x) > (y) ? (x) : (y))to create a macro for computing the maximum of two values. max(n,0) would be replaced by (n) > (0) ? (n) : (0). You should always put parentheses around arguments in macros, as has been done here, or you can get very strange effects from the textual substitution done by the preprocessor.
Note: Preprocessor directives start with a # in column 1. Don't indent the #. #define lines are one line long. The preprocessor is not free-form.
The preprocessor can also be used to make decisions at compile time. A common kind of decision is to ask whether a symbol is defined. A definition of NULL might look like this.
#ifndef NULL #define NULL 0 #endifThis says to compile what is between #ifndef NULL and #endif only if NULL is not a defined preprocessor macro. This kind of thing is commonly used in header files (described in the next section) to prevent them from being read more than once by the compiler. A file called myhead.h typically has the form
#ifndef MYHEAD_H #define MYHEAD_H ... #endifThe first time the file is read, and MYHEAD_H is defined. Subsequent times, the entire file is skipped.
// File: count.h // count_chars(str) returns the number of occurrences of // character char_to_count in null-terminated string str. extern char char_to_count; int count_chars(char* str);
// File: count.cpp #include "count.h" char char_to_count; // count_chars(str) returns the number of occurrences of // character char_to_count in null-terminated string str. int count_chars(char* str) { int count = 0; char* p; for(p = str; *p != 0; p++) { if(*p == char_to_count) count++; } return count; }
When you compile your files, you produce object files, with extension .o or .obj. You then run a linker to link the object files together into a single executable file.
Note: If you declare an array in a module, as in
int A[30];then the extern declaration should have the same form, but need not include the size. For example, you might write
extern int A[];An array is a pointer constant. Do not put
extern int* A;since that tells the compiler that A is a pointer variable, and causes confusion.
int main(int argc, char* argv[])where array argv holds the parts of the command line that was used to invoke this program, and argc is the number of members of argv. argv[0] is the name of the command. If you type command
myprog -d test.txtthen argc is 3 and argv is as follows.
argv[0] = "myprog"The return result of main is the exit status of the program. Normally, it is 0. A nonzero value indicates that an error occurred during the program's execution.
argv[1] = "-d"
argv[2] = "test.txt"
Note: If you do not use the parameters argc and argv, you can omit them, and write
int main()