CSCI 1300 Notes 9: From C to C++

 

The C++ programming language, which is used in most subsequent CS courses at CU, was developed as an extension to C, and has a great deal in common with it. In fact, many of the features of C++ are identical in C. This means that you can learn the basics of C++ with little effort, based on what you know about C. There are some differences, and these notes will tell you what those are. Some of the differences are superficial, requiring you to write the exact same thing in a slightly different way, but some are more significant.

 

The biggest difference between C++ and C is that C++ is an object oriented language, meaning that it supports entities called objects, which are much like structs but can contain functions as well as data. But objects are not emphasized in our first semester course, so with just a little introduction to C++ you’ll know what you need to go on into the second semester course, if you wish to do so.

 

While you can use objects in C++, you don’t have to, which means that basic C++ programming is very much like the C programming you’ve been doing.

 

Basic programming in C++

 

Here is a list of the differences between C and C++ that you need to know about.

 

Compiling. Instead of

 

gcc pgm.c -Wall –o pgm

 

use

 

g++ pgm.cpp –Wall –o pgm

 

Note that the file extension for C++ programs is .cpp, not .c.

 

#includes. Some header files are the same for C and C++, like <string.h>. But <stdio.h> won’t be recognized by the C++ compiler. Instead, use

 

#include <iostream.h>

 

if you are reading from the keyboard, and

 

#include <fstream.h>

 

if you are using files.

 

Constants. C++ has a nicer way of declaring constants than C: you can make them real information holders much the way you declare other things. You write

 

const int MAX=50; //C++

 

instead of

 

#define MAX 50 //C

 

Placement of declarations. In C++ you can put declarations wherever you want in a function, as long as info holders are declared before they are used. This is a big convenience, because you can put in a declaration close to where you need it, rather than having to remember to put it at the beginning of a function.

 

This is especially nice for loop variables: just declare the variable right in the for:

 

for(int i=0;i<3;i++)  //bad in C, good in C++

 

In C you have to declare i at the beginning of the function, which is a nuisance.

 

Struct types. In C++, once you define a struct type as in

 

struct point

{

      int x;

      int y;

};

 

you can use the type point in any further declarations, as in

 

point p;

 

without having to write struct point p, as you have to do in C. Again, this is a real improvement, since your code is less cluttered and easier to read.

 

The type bool. C++ has a special type, whose values are true and false, for use in tests. So you don’t have to play games with 1’s and 0’s. For example this is nice for flags, as in

 

bool foundit;

foundit=false; //not yet

 

Functions you use as tests in  C++  loops can return bools rather than the more cryptic ints needed in C.

 

By the way, ints can still be used in C++ tests, and they work the same as in C. So if you are converting C code you do not have to change anything to make it work, but using bools is better.

 

Input and output. Rather than using file pointers, C++ has things called streams. Think of a stream of data flowing into your program, from the keyboard or a file, or a stream of data flowing out of your program onto the display screen. There are special operators, >> for taking in, and << for putting out, that operate to get information from an inflowing stream or to put information into an outflowing stream. You use these operators instead of scanf(), fscanf(), and printf().

 

The >> and << operators can tell what to do with your data without using the format tags you have to use in C (More accurately, the compiler can tell what kind of data you are processing, and it sets up the >> and <<  operators to work properly with that kind of data. The C compiler knows nothing about what scanf() and printf() are doing; they’re just functions in a library, and so the C compiler can’t help do what you want.)

 

In C, you tell the system where to read from, file or keyboard, by choosing between scanf() and fscanf(). In C++, you use the same operator, >>, to read from both sources, but you specify different streams as the source. The stream cin represents the keyboard, and you set up your own stream and connect it to a file when you want to read from a file.

 

Here are some examples of input and output operations as you write them in C, and then the same operations written in C++.

 

Assume the declarations

            int i;

      float f;

      char c;

      char word[20];

      char line[80];

 


scanf(“%d”,&i);

scanf(“%f”,&f);

scanf(“%c”,c);

scanf(“%i %f”,&i,&f);

scanf(“%s”,word);

gets(line);

 

 

printf(“The value of i is %d and f is %f and word is %s\n”,i,f,word);

//pretend above is on one line

 

 

#include <stdio.h>

#include <stdio.h>

 

FILE *input;

input=fopen(“words.dat”,”r”);

fscanf(input,”%s”,word);

fgets(line,80,input);

 

 

 

 

 

cin>>i;

cin>>f;

cin>>c;

cin>>i>>f;

cin>>word;

cin.getline(line,80);

 

 

cout<<”The value of i is “<<i

  <<” and f is “<<f

  <<” and word is “<<word<<endl;

 

 

 

#include <iostream.h>

#include <fstream.h>

NOTE: See below on warning

 

ifstream input;

input.open(words.dat”);

input>>word;

input.getline(line,80);

 

 

 


Deprecated headers. The header files iostream.h and fstreams.h are now deprecated (meaning up-to-date code does not use them.) But our libraries do not seem to have the up-to-date header files available, so suppress the warning by adding the flag

Wno-deprecated

to your compile command.

 

Using << to display stuff. Notice that << not only has no format tags, it has no format string at all. You just use it to send the desired information to cout. You use a series of them to send more than one thing, which is how you mix the values of information holders with text you want to use to label the output, as in

 

cout<<”The value of i is “<<i<<endl;

 

instead of

 

printf(“The value of i is %d \n”, i);

 

Notice that you send endl to the stream when you want a newline. Also, as mentioned earlier, you just send i to cout, without having to say it’s an int in a format string. The compiler figures that out.

 

Using >> to read stuff in. A nice feature of the >> operator in C++ is that it can read into your information holders just given their names, without your having to use an & as you have to with C. So

 

cin>>i;

 

will read an int into i (assuming that’s what i is.) No & and no format tag needed to make this work.

 

Reading from a file. To read from a file in C++ you create a stream that’s connected to the file. Then you use >> to get information from the stream in the same way you use it to get information from cin. In the example above

 

ifstream input;

input.open(words.dat”);

input>>word;

 

The first line declares input to be an input file stream (that’s what ifstream stands for.) Now input is actually an object, which means it is like a struct except that it can have functions in it as well as data. Like a struct, you use the dot to refer to its parts. So input.open is the open function that’s part of input, and you give that function the name of the file you want. That opens the file, just as fopen() does in C, and connects the stream input to it. Once input is connected to the file, you use >> to read from it, in just the way you read from cin.

 

Checking the happiness of a stream. As we’ve seen, anytime you are working with a file things can go wrong. You may not be able to open the file, and if you can, you may or may not be able to read something from it, because you may have run out of data, or the next item in the file may be of the wrong type.

 

It’s much easier to deal with these things in C++ than in C. In C++ a stream is in one of two states, which you can think of as happy and unhappy. A stream is happy if it’s been able to do everything you wanted: you were able to open it, and when you’ve tried to read from it the data have been there and been of the correct type. But as soon as you try something that doesn’t work, the stream becomes unhappy. Conveniently, you can tell if a stream is happy or unhappy just by using the name of the stream as a test, like this:

 

ifstream input;

input.open(words.dat”);

if(input)

      cout<<”stream input is happy… open must have worked!”;

 

The test will be true if the stream is happy, and false if it’s unhappy.

 

The same test can be used to tell if a read has worked or not. Here’s a trytoread() function written in C and C++:

//C version

int trytoread(FILE *in, char word[])

{

      if(fscanf(in,”%s”,word)!=1)

            return 0; //didn’t read one item as requested

      return 1;

}

 

 

//C++ version

bool trytoread(ifstream& in, char word[])//bool is clearer than int

{

in>>word;  //try to read

if (in)

      return true; //stream was happy after trying the read, so

                  //read must have worked

return false;

}

           

Reference parameters. This is a biggie. As you know, a C function can’t change somebody else’s information holder unless it is given a pointer to it, either explicitly, using & in the argument, or implicitly, as happens with arrays. This is because all arguments in C have their values copied into the corresponding parameter information holders in the function. No matter what the function does with the copy, it won’t affect the original, unless the copy is a copy of a pointer, and the function uses the pointer to change the bits where the pointer points.

 

This mechanism used in C is called pass by value, because only values get passed to the function. C++, like many other languages, has another mechanism, called pass by reference, as well as pass by value. With pass by reference, a pointer to the argument is given to the function, without your having to write an &.

 

If you don’t do anything special in C++, you get the same pass by value behavior you are used to in C. In fact, C++ even has the same treatment of arrays as C does, where the value of an array is a pointer to its first element, so pointers are passed for array arguments without your doing anything special. And you can declare parameters to be pointers, and use & and * in the way you are used to, the same way in C++ as in C, if you want.

 

But in C++ you have an alternative. If you want a function to be able to change an information holder it is given, but you don’t want to write &s and *s, you can use a reference parameter. When you pass an argument to a reference parameter you get pass by reference: a pointer is passed to the function, without your having to write an &.

 

Here’s an example. In C, if I want the function addone() to change an information holder, I have to do this:

 

void addone(int *x)

{

      *x=*x+1;

}

 

int n=2;

addone(&n);

//n now contains 3

 

In C++ I can do this:

 

 

void addone(int& x)//note the magic & in the parameter declaration

{

      x=x+1;  //x used here refers to the argument, though no * is used

}

int n=2;   

addone(n);     //you say just n, but secretly a pointer to n is passed

//n now contains 3

 

The & after int in the declaration of the parameter x is what triggers the different behavior: it marks x as a reference parameter. Behind the scenes, that means that the function is going to get a pointer to an int, rather than an int, but that you can use just x to refer to what that pointer points to, rather than having to use * as you would if you were passing a pointer yourself. Also, when you call addone() the pointer to n is passed without your having to write &n.

 

You can decide what you think of this. On the one hand, it’s nice not to have to write so  many *s and &s. In fact, the reason you don’t have to use & with >> the way you do with scanf() is that >> uses reference parameters… when you write cin>>foo; the >> operator gets a pointer to foo to work with. And that’s nice: how many times have you forgotten the & in scanf()?

 

On the other hand, it makes it harder to understand how things really work. Actually, this difference is one of the reasons I like to start people off with C rather than C++. Once you understand how pointers work in C, you can understand what is happening in C++ pretty easily, even though it happens  secretly. If you start with C++ things are more mysterious.

 

Even after you are fully comfortable with C++ there is a residual disadvantage, I feel. In C you can always tell when a function can change an information holder that you give it, without having to read the definition or declaration of the function. If the argument is an array, you know a pointer is being passed, so you have to assume the array may be changed. If you pass an int, or a float, or a struct, or anything else that isn’t an array, you know there’s no worry: the function can’t change it. If you are dealing with a function that can change one of those things, you’ll be passing a pointer rather than the thing itself, and you have to put that & in the call. No function can change things behind your back.

 

In C++ you can’t tell. If you write eggplant(n) you have no way of knowing if eggplant has an ordinary parameter, in which case it’s only getting a copy of the value of n, and you are safe, or a reference parameter, in which case it is perfectly able to change n on you. That is, you have no way of knowing unless you find and read the declaration of eggplant(). This isn’t a huge problem, by any means, but it’s less than ideal.

 

Here is a C example that passes a pointer, and the corresponding program written in C++, using a reference parameter:

 

//C version

#include <stdio.h>

void add3(int *pn);

int main()

{

      int i=13;

      printf(i is: %d\n”,i);

      add3(&i);

      printf(“after add3() i is: %d\n”,i);

      return 0;

}

void add3(int *pn)

{

      *pn=*pn+3;

}

 

 

//C++ version

#include <iostream.h>

void add3(int& n); //the & marks the reference parameter

int main()

{

      int i=13;

      cout<<”i is: “<<i<<endl;

      add3(i);   //I don’t have to write &i

      cout<<”after add3() i is: “<<i<<endl;

      return 0;

}

void add3(int& n) //the & marks the reference parameter

{

      n=n+3;  //I write n, not *n, and it “refers to” i (the argument)

}

     

Another point, one that you may feel is a huge plus for C++ and reference parameters. Remember the arrows that you have to use in C when you pass a pointer to a struct to a function, so that the function can change the struct? Here’s an example:

 

//C version

struct point

{

      int x;

      int y;

};

void origin(struct point *pp)

{

      pp->x=0;

      pp->y=0;

}

 

Now see what you write in C++:

 

//C++ version

struct point

{

      int x;

      int y;

};

void origin(point& p) //note the magic &

                      //that makes p a reference parameter

{

      p.x=0;

      p.y=0;

}

 

Voila! No arrows needed! That’s because the parameter p in the C++ version is a reference parameter, and can be used to refer to the struct argument as if it were an ordinary information holder in the function.

 

One last quirk. When you pass something by value, as you always do in C, the code that’s generated has to copy the value of the argument into the information holder in the function for the corresponding parameter. If you use pass by value in C++, the same thing happens. BUT if you are passing an object in C++ (there are no objects in C, so the issue doesn’t arise) it may not be possible to make a copy. That’s because making the copy involves using a copy function that’s found as part of the object itself (called a copy constructor) and some objects don’t have them! For these objects you just can’t pass them by value and so you have to use pass by reference. That’s why the trytoread() function shown earlier has a reference parameter for the input file stream:

 

bool trytoread(ifstream& in, char word[]);

 

 

Checklist: What to look for and change in your C code to change to C++

To get the details, refer back to the notes above.

 

C feature                                                                      What to do in C++

 

#include <stdio.h>                        #include <iostream.h>

                                                                                    (also need <fstream.h> for files)

                                                                                    (See above on deprecated headers.)

 

#define X 20                              const int X=20;

 

naming a struct type in a declaration                              don’t write struct in front of it               

or prototype, eg struct point mypoint;                  point mypoint;

 

scanf()                                   cin>>

printf()                                  cout<<                 

 

FILE *in;                                 ifstream in;                             

in=fopen(“words.dat”,”r”);                in.open(“words.dat”);                    

fscanf(in,”%s”,word);                     in>>word;

 

gets(line);                               cin.getline(line,80);

 

pointer parameter                                                         use reference parameter

e.g.void foo( int *pn)                                       void(int& n)

 

call using pointer parameter                                           call using reference parameter  

int m;

foo(&m);                                                                    foo(m);

 

using reference parameter in function

e.g. *pn=*pn+1;                                                          n=n+1;

 

values to use in tests

for example for flags                                                     (optional change)

int foundit;                              bool foundit

0 for false, nonzero for true             true, false

 

When you are changing a function, don’t forget to change the declaration of the function to match the function header!

 

Exercise 9-1:

Convert the following C program into C++. Test the converted version to be sure it works. (Note that moveto() and lineto() are function in the graphics library; you do not have to change any of the library functions.)

 

#include <winbgim.h>

#include <stdio.h>

struct point

{

     int x;

     int y;

};

int trytoread(FILE *fp, struct point *ppt);

int main()

{

     FILE *fp;

     struct point pt;

     fp=fopen("points.txt","r");

     if(!fp)

          exit(1);

     initwindow(300,300);

     moveto(150,150);

     while(trytoread(fp,&pt))

          lineto(pt.x,pt.y);

     getch();

     return 0;

}

int trytoread(FILE *fp, struct point *ppt)

{

     if(fscanf(fp,"%d%d",&(ppt->x),&(ppt->y))==2)

          return 1;

     return 0;

}

 

You'll need a file points.txt with contents something this to test this out (just a bunch of pairs of ints describing points):

 

50 50

50 150

150 50

 

Exercise 9-2: Preparing for Language Check 4

 

Of all these differences, the most important one for you to be comfortable with in going forward into C++ is reference parameters. So for LC4 you'll need to be able to convert a program fragment like this one into C++:

 

//in calling program

int a=2;

int b=1;

sort(&a,&b);

//a is now less than or equal to b

     

//function

void sort (int *px, int *py)

{

      int t;

      if(*px>*py)

      {

            t=*px;

            *px=*py;

            *py=t;

      }

}

 

 

What to turn in: Turn in listings and screen shots of sample runs of your C++ code for Ex 9- 1 and Ex 9-2.