In C, input and output is traditionally done using the functions printf and scanf (along with getchar()). When reading from files, the routines fprintf and fscanf are used. Unfortunately, it's pretty easy to crash a program using these functions. For example,
int d = 3; char *str = "hi there!"; printf("d is %d, and str is %s\n", str, d);
will probably cause your program to crash, because the programmer mixed up the arguments to printf. The routine scanf is even worse: for example,
double d; float f; int i; scanf("%d", i); /* wrong, should be &i */ scanf("%f", &d); /* wrong, needed to specify %lf */ scanf("%f", &f); /* this one is OK */
The major problem is that you can usually only find these bugs at run-time. However, it would be much better for the compiler to be able to look at the statement scanf("%d", i) and determine that a pointer to an integer, not simply an integer, was required here. While you can't do that with C style operators (since the correct arguments to printf and scanf vary, depending on the formatting string), in C++ there are routines that will eliminate most of these bugs.
Let's look at an example:
#include <iostream.h> main() { int a, b; double d; char str[20]; cin >> a; // equivalent to scanf("%d", &a); cin >> b; // equivalent to scanf("%d", &b); cin >> d; // equivalent to scanf("%lf", &d); cin >> str; // equivalent to scanf("%s", str); cout << "The sum of a and b is"; cout << a + b; cout << "\n"; }
First, the file iostream.h is a standard C++ header file that defines cin, cout, and the operators << and >>. The expression cin >> a causes the program to read an integer into the variable a, from the standard input. Similarly, cin >> b reads an integer into b, but cin >> d interprets its input as a real number, and stores it in d. The next statement, cin >> str reads characters until a whitespace is encountered, and stores a null-terminated string in str (excluding the whitespace). Note that if you typed a very long string (i.e. more than 20 characters), your program could crash.
Output is much the same: with the expression cout << thing, the contents of thing is printed to the standard output. Note a couple of subtleties:
int ival = 'g'; // The ASCII code for g is 103, so ival = 'g' char cval = 'g'; // is equivalent to ival = 103. cout << ival; // treats ival as a number, and prints out 103 cout << cval; // prints out simply the letter g
A few other things: You can combine any number of output or input operations in a row e.g.
cin >> a >> b >> d >> str;is fine, and is equivalent to the four seperate lines above. Similarly, statements like
cout << "The sum of a and b is " << a+b << "\n";are permissible as well. (Note the space after the word ``is.'' Without extra space, i.e.,
int x = 23; cout << "The value of x is" << x << "\n";
you get
The value of x is23
You can test when you've run out of input:
int x; while(cin >> x) { ... }
will loop until there is no more input, or until an invalid string is entered (e.g. ``hi,'' which is not a valid integer).
It is also possible to make the << and >> output operators know how to output user-defined types: for example, if you create new datatypes, you can teach the << and >> operators how to input and output directly from those datatypes. (See the C++ book, or ask the teaching staff how this works, if you're interested).
If you want to do I/O to a file (i.e. the equivalent of fprintf), you can get declare variables like cout that are used just as cout is, but that cause the output to go to a particular file. You can do input from a file similarly. There is an example of this in Assignment 1. If you want to know more details, consult the C++ book, or the teaching staff.
Finally, in addition to sending output to cout, you can also send output to the ``standard error'' by using cerr e.g.
if(i < 0) { cerr << "Fatal error: i is less than zero!\n"; exit(1); }
This is analogous to using fprintf to stderr.
In C++ it is illegal to use a function without first declaring its type. In C, if you don't declare a function's type, certain defaults apply. This is not the case with C++. You must define a function before you can use it.
Every keyword, or reserved-word in C is also a keyword in C++. There are extra keywords in C++ that have no meaning in C. Since C++ is becoming prevalent, even when programming in C you should avoid the use of all of the following C++ keywords as function or variable names. In C++ you cannot use any of the following words as function or variable names:
asm delete if return try auto do inline short typedef break double int signed union case else long sizeof unsigned catch enum new static virtual char extern operator struct void class float private switch volatile const for protected template while continue friend public this default goto register throw
In C++, when you define a structure, the name of the structure is henceforth treated as a new datatype. For example:
struct complex { double x, y; }; complex a, b, c;
Also, note the semicolon at the end of the definition of the complex structure: this semicolon is required. After defining the structure, you can use complex as a datatype. Its still legal to say
struct complex a, b, c;if you want to. The above discussion applies to unions as well.
In C, we allocate space using malloc. For example,
double *dspace = (double *)malloc(sizeof(double) * 10);dynamically allocates space for 10 doubles. In C++, we use the construct new:
double *dspace = new double[10];To allocate space for the complex structure defined above, the syntax is simply
complex *cptr = new complex[37];
To return memory in C, we use free:
free(dspace);In C++, memory is returned by
delete dspace; /* or, delete []dspace; */or
delete cptr; /* or, delete []cptr; */
The format with square brackets is needed when you have an array of items each having its own destructor (we'll talk about destructors later on in the course). As in C, though, if you try to write past the end of the memory allocated by new, your program will probably not work correctly. Incidentally, the delete operator is guaranteed to have no effect when applied to a pointer whose value is zero (NULL). Handing delete a pointer that was not returned by new is a bad idea. To more easily catch cases when you've deleted memory but later tried to use it, the code
delete dspace; dspace = 0;
is recommended, since the use of dspace after the deletion will be easier to detect.
In C, a name can only be used once to define a function. For example, in C, the following is illegal:
double minimum(double x, double y) { if(x < y) return x; else return y; } int minimum(int x, int y) { if(x < y) return x; else return y; }
This is perfectly legal C++ code, however, and is known as an overloaded function. If you call minimum in your code, the compiler will decide which version of minimum to invoke based on the types of the arguments you supply to the function. Appropriate uses for overloading are reusing mathematical function names for operations on different data; for example, two versions of sqrt: one for real numbers, and one for complex numbers. Another typical use might be a function named node_count which returns the number of nodes in differing data structures such as linked lists, trees, or graphs. You will probably have little use for operator overloading in this class.
The reason we bring up the subject at all, is to make you aware of the following pitfall. Suppose that you declare in a header file defs.h a function foo as follows:
/* file defs.h */ int foo(double, int, char);
Now suppose that you wish to use the function foo in a file a.c:
/* file a.c */ #include "defs.h" void do_something() { int result; result = foo(3.4, 7, 'c'); }
Ok, no problems so far: the file a.c correctly includes defs.h so it can access the function foo. Suppose, though, you made a mistake in the file b.c, where foo is actually defined:
/* file b.c */ #include "defs.h" /* Here's my great implementation of foo: by juxtaposing the seventh and ninth bit from the third argument, I cleverly manage to ... */ int foo(double a, int b, int c) { ... }
The problem is that the C++ compiler thinks you're defining a function foo which takes a double and two integers. This is perfectly legal. The problem comes when you actually compile and try to run everything. At that point, the function named foo that takes a double, an integer, and a character, will be undefined, because in b.c you defined a different function foo.
Moral of the story: if you get undefined function errors from the compiler, make certain that you defined your function with the types you really meant to.
C++ allows two types of comments. In addition to the standard ``/*'' and ``*/'' comments of C, there is a special comment which is good only to the end of a line. In C++, characters occuring after the sequence ``//'' and on the same line are ignored, as long as the ``//'' does not occur in a string. For example:
/* If low is greater than high, interchange the values and set 'swapped' to 1. */ if(low > high) { tmp = low; // save low low = high; high = tmp; // now swap swapped = 1; // indicate we swapped the data }
One thing to be VERY careful of is not to use C++ style comments in #define statements, because most compiler systems don't mix the two together correctly. That is, for now (and probably for at least another 10 years!) don't write
#define MAX_LEN 128 // maximum length string
but use
#define MAX_LEN 128 /* maximum length string */
instead.