CS 330: Frequently Asked Questions

Steven J. Zeil

Last modified: Nov 14, 2020
Contents:

This is a collection of questions (and answers!) that have arisen repeatedly in some of my past classes.

1 General Programming Questions

1.1 I’m not sure I have a correct algorithm to solve this problem. How do I check before writing and debugging the code?

The worst thing you can do is to simply start throwing C++ statements around at random trying to guess at an algorithm design.

  1. Research - is there a known algorithm for addressing this kind of problem? Check your text and the lecture notes. Check the web.

    Even if you find something, you will likely have to adapt it to the details of the problem at hand, but this can get you started.

  2. Use a systematic procedure for design, such as stepwise refinement, to derive a new algorithm that solves your problem.

  3. Whatever you come up with, use desk checking to make sure you have something reasonable before you start sweating all the fine details of coding.

1.2 My program crashed. How do I find out why?

There’s no easy answer to that. Here’s what I do, though, when faced with a crash that I just don’t understand:

  1. Look at the output produced before the crash. That can give you a clue as to where in the program you were when the crash occurred.

  2. Run the program from within a debugger (or here and here ).

    Don’t worry about breakpoints or single-stepping or any of that stuff at first. Just run it.

    When the crash occurs, the debugger should tell you what line of code in what file eas being executed at the moment of the crash.

    Actually, it’s not quite that simple. There’s a good chance that the crash will occur on some line of code you didn’t actually write yourself, deep inside some system library function that was called by some other system library function that was called by some other … until we finally get back to your own code. That crash occurred because you are using a function but passed it some data that was incorrect or corrupt in some way.

    Your debugger should let you view the entire runtime stack of calls that were in effect at the moment of the crash. So you should be able to determine where the crash occurred. That’s not as good as determining why, but it’s a start.

  3. Take a good look at the data being manipulated at the location of the crash. Are you using pointers? Could some of them be null? Are you indexing into an array? Could your index value be too large or negative? Are you reading from a file? Could the file be at the end already, or might the data be in a different format than you expected?

    If you used a debugger to find the crash locations, you can probably move up and down the stack and to view the values of variables within each active call. This may give a clue about what was happening.

  4. Form some hypotheses (take a guess) as to what was going on at the time of the crash. Then test your hypothesis!

1.3 Why do compilers’ error messages often give the wrong line number, or even the wrong file?

A compiler can only report where it detected a problem. Where you actually committed a mistake may be someplace entirely different.

The vast majority of error messages that C++ programmers will see are

Let’s look at these from the point of view of the compiler.

1.3.1 Syntax errors

Assume that the compiler has read part, but not all, of your program. The part that has just been read contains a syntax error. For the sake of example, let’s say you wrote:

x = y + 2 * x // missing semi-colon

Now, when the compiler has read only the first line, it can’t tell that anything is wrong. That’s because it is still possible, as far as the compiler knows, that the next line of source code will start with a “;” or some other valid expression. So the compiler will never complain about this line.

If the compiler reads another line, and discovers that you had written:

x = y + 2 * x // missing semi-colon
++i;

it still won’t conclude that there’s a missing semi-colon. For all it knows, the “real” mistake might be that you meant to type “+” instead of “++”.

Now, things can be much worse. Suppose that inside a file foo.h you write

class Foo {
    ⋮
   Foo();
   int f();
   // missing };

and inside another file, bar.cpp, you write

#include "foo.h"

int g() { ... }

void h(Foo) { ... }

int main()  { ... }

Where will the error be reported? Probably on the very last line of bar.cpp! Why? Because until then, it’s still possible, as far as the compiler knows, for the missing “};” to come, in which case g, h, and main would just be additional member functions of the class Foo.

So, with syntax errors, you know only that the real mistake occurred on the line reported or earlier, possibly even in an earlier-\#include’d file.

1.3.2 undeclared and undefined symbols

See this discussion.

1.3.3 type errors

When you use the wrong object in an expression or try to apply the wrong operator/function to an object, the compiler may detect this as a type mismatch between the function and the expression supplied as the parameter to that function. These messages seem to cause students the most grief, and yet the compiler is usually able to give very precise descriptions of what is going wrong. The line numbers are usually correct, and the compiler will often tell you exactly what is going wrong. That explanation, however, may be quite lengthy, for three reasons:

  1. Type names, especially when templates are involved, can be very long and messy-looking.

  2. Because C++ allows function overloading (multiple functions with the same name, as long as they take different kinds of parameters), there may be many functions with the same name. The compiler will have to look at each of these to see if any one matches the parameter types you supplied. Some compilers report on each function tried, explaining why it didn’t match the parameters in the faulty call.

  3. If the function call was itself produced by a template instantiation or an inline function, then the problem is detected at the function call (often inside a C++ standard library routine) but the actual problem lies at the place where the template was used/instantiated. So most compilers will list both the line where the error was detected and all the lines where templates were instantiated that led to the creation of the faulty call.

So, to deal with these, look at the error message on the faulty function call. Note what function/operator name is being complained about. Then look at the line where the faulty call occurred. If it’s inside a template or inline function that is not your own code, look back through the “instantiated from” or “called from” lines until you get back into your own code. That’s probably where the problem lies.

Here’s an example taken from a student’s code:

g++ -g -MMD -c testapq.cpp
/usr/local/lib/gcc-lib/sparc-sun-solaris2.7/2.95.2/../../../../include/g++-3/
stl_relops.h: In function `bool operator ><_Rb_tree_iterator<pair<const 
 PrioritizedNames,int>,pair<const PrioritizedNames,int> &,pair<const 
 PrioritizedNames,int> *> >(const _Rb_tree_iterator<pair<const 
 PrioritizedNames,int>,pair<const PrioritizedNames,int> &,pair<const 
 PrioritizedNames,int> *> &, const _Rb_tree_iterator<pair<const 
 PrioritizedNames,int>,pair<const PrioritizedNames,int> &,pair<const 
 PrioritizedNames,int> *> &)':
adjpq.h:234:   instantiated from `adjustable_priority_queue<
 PrioritizedNames,map<PrioritizedNames,int,CompareNames,allocator<int> >,
 ComparePriorities>::percolateDown(unsigned int)'
adjpq.h:177:   instantiated from `adjustable_priority_queue<PrioritizedNames,
 map<PrioritizedNames,int,CompareNames,allocator<int> >,
 ComparePriorities>::makeHeap()'
adjpq.h:84:   instantiated from here
 /usr/local/lib/gcc-lib/sparc-sun-solaris2.7/2.95.2/../../../../include/
 g++-3/stl_relops.h:43: no match for `const _Rb_tree_iterator<pair<const 
 PrioritizedNames,int>,pair<const PrioritizedNames,int> &,pair<const 
 PrioritizedNames,int> *> & < const _Rb_tree_iterator<pair<const 
 PrioritizedNames,int>,pair<const PrioritizedNames,int> &,pair<const 
 PrioritizedNames,int> *> &'

Now, that may look intimidating, but that’s mainly because of the long type names (due to template use) and the long path names to files from the C++ standard library. Let’s strip that down to the essentials:

g++ -g -MMD -c testapq.cpp
stl_relops.h: In function `bool operator >:
adjpq.h:234:   instantiated from `percolateDown(unsigned int)'
adjpq.h:177:   instantiated from `makeHeap()'
adjpq.h:84:   instantiated from here
stl_relops.h:43 no match for ... < ...

This one is actually worse than most error messages, because it’s easy to miss the “<” operator amidst all the < ... > template markers.

The problem is a “no match for” a less-than operator call in line 43 of a template within the standard library file stl_relops.h. But that template is instantiated from the student’s own code (adjpq.h) and so the thing to do is to look at those three lines (234, 177, and 84) for a data type that is supposed to support a less-than operator, but doesn’t.

1.3.4 const errors

Technically, “const”-ness is part of a type, so while sometimes these get special messages of their own, often they masquerade as ordinary type errors and must be interpreted in the same way.

1.4 What is a segmentation error? Segementation fault? Bus error? Null reference error?

These are all various errors signaled by the underlying operating system when your program tries to retrieve from, store into, or execute instructions from an address that either doesn’t exist or is reserved for another program. In practical terms, these kinds of messages almost always arise because of

To debug these problems, concentrate on your pointers, arrays, and iterators. Add debugging output or use a debugger to find out which one is being used when the crash occurs. Then work backwards to figure out why that pointer/index/iterator has an invalid value. Keep in mind that you might not be using a pointer, array, or iterator directly, but might be using (abusing?) a function or class that does.

1.5 What are formal and actual parameters?

The formal parameters of a function are the parameter names that are declared in the function header and that are used when we write the function body. For example, in the code

int sequentialInsert (T a[], unsigned& n, const T& x)
// insert x into sorted position within a,
//   with a already containing n items.
//   Return the position where inserted.
{
  int i = n;
  while ((i > 0) && (x < a[i-1]))
      {
       a[i] = a[i-1];
       i = i - 1;
      }
  a[i] = x;
  ++n;
  return i;
}   

the formal parameters are named a, n, and x. The actual parameters of a call to a function are the values passed by the caller. So, in the call

k =  sequentialInsert (myArray, size-1, value);

the actual parameters are myArray, size-1, and value. Note that formal parameters are always just names. Actual parameters can be simple names or arbitrarily complicated expressions.

2 The C++ Programming Language

2.1 What’s the odd expression with the ‘?’ and ‘:’ ?

You’re probably looking at a conditional expression. It’s a convenient shorthand for an if-then-else structure inside an ordinary expression. A typical example would be

x = (x < 0.0) ? -x : x;

The part before the ‘?’ is the condition and is evaluated first. If the condition is true, then the then-part expression (between the ‘?’ and the ‘:’) is evaluated and the result used as the value of the entire expression. If, however, the condition is false, then the else-part (after the ‘:’) is evaluated and that result is used as the value of the entire expression. This assignment, then, replaces x by its own absolute value. The whole thing, then, is equivalent to

double temp;
if (x < 0.0)
  temp = -x;
else
  temp = x;
x = temp;

A conditional expression can apear inside other expressions, anywhere a value of the same type as its then-part and else-part might appear:

x = sqrt((x < 0.0) ? -x : x);

2.2 How do I convert a string to a character array (or a character array to string)?

To get a character array from a std::string, use the string’s c_str() function:

std::string str;
   ⋮
char* cstr = str.c_str();

You will often see this done with older library functions that were originally designed to work with character arrays and noy yet updated to work with strings:

copyFile.cpp
void copyFile (string fileName)
{
   ifstream input (fileName.c_str()); // open an input file;
   ofstream output ("output.txt"); // No c_str() required - "output.txt" is a
                                   //    character array, not a string.
   string line;
   getline (input, line);
   while (input) 
   {
      output << line << endl;
      getline (input, line);
   }   
}

Going in the opposite direction is even simpler. The string class has a constructor for building strings from character arrays, so this takes palce automatically most of the time:

void copyFile (string fileName);
   ⋮
copyFile ("input.dat");  // automatically converted to std::string

2.3 How do I convert a string to a number (or a number to a string)?

There are some quick-and-dirty functions for doing this in <cstdlib> for converting to numbers:

#include <cstdlib>
using namespace std;
   ⋮
int i = atoi("123");
double f = atof("3.14159");

Now, these functions actually convert from character arrays, not strings, so if you have a string you need to convert to a character array:

#include <cstdio>
#include <string>
using namespace std;
   ⋮
string s1 = "123";
string s2 = "3.14159";
   ⋮
int i = atoi(s1.c_str());
double f = atof(s2.c_str());

If you need to go in the other direction, or if you need to deal with strings with unusual formatting, then there is a more general technique. In fact, you can convert any datatype that has << and >> I/O operators to and from strings by reading from and writing into a string. The istringstream is an input stream that reads from a string:

#include <sstream> // provides istringstream and ostringstream
#include <string>
using namespace std;
   ⋮
string s1 = "123 3.14159";
   ⋮
istringstream in (s1); // create a stream reading from s1
int i;
double f;
in >> i >> f;  // i will contain 123 and f will contain 3.14159

The ostringstream is an output stream that writes into a string:

#include <sstream> // provides istringstream and ostringstream
#include <string>
using namespace std;
   ⋮
string s1;
ostringstream out (); // create a stream writing into a string
int i = 245;
double f = 1.2;
out << i << ":" << f;
string s = out.str();  // Retrieve the string we have written
cout << s << endl;     // Prints "245:1.2"

Again, let me point out that this stringstream approach can be used to convert between string and any data type that you can read and write.

2.4 What is the difference between a declaration and a definition?

Pretty much everything that has a “name” in C++ must be declared before you can use it. Many of these things must also be defined, but that can generally be done at a much later time. You declare a name by saying what kind of thing it is:

 const int MaxSize;          // declares a constant
 extern int v;               // declares a variable
 void foo (int formalParam); // declares a function (and a formal parameter)
 class Bar{<: ... :>};               // declares a class
 typedef Bar* BarPointer;    // declares a type name

In most cases, once you have declared a name, you can write code that uses it. Furthermore, a program may declare the same thing any number of times, as long as it does so consistently. That’s why a single .h file can be included by several different .cpp files that make up a program — most .h files contain only declarations. You define constants, variables, and functions as follows:

 const int MaxSize = 1000;                   // defines a constant
 int v;                                      // defines a variable
 void foo (int formalParam) {++formalParam;} // defines a function

A definition must be seen by the compiler once and only once in all the compilations that get linked together to form the final program. A definition is itself also a declaration (i.e., if you define something that hasn’t been declared yet, that’s OK. The definition will serve double duty as declaration and definition.).

2.5 What goes in a .h file? What goes in a .cpp file?

The short answer is that a .h file contains shared declarations, a .cpp file contains definitions and declarations that you don’t want to share..

It’s important that you understand difference between declarations and definitions.

Never, ever, ever name a .cpp file in an #include.

That defeats the whole purpose of a C++ program structure.

2.6 What is an inline function?

When we define a function, it is usually compiled into a self-contained unit of code. For example, a function

int foo(int a, int b)
{
  return a+b-1;
}

would compile into a block of code equivalent to

stack[1] = stack[3] + stack[2] - 1; 
jump to address in stack[0]

where the “stack” is the runtime stack a.k.a. the activation stack used to track function calls at the system level, stack[0] is the top value on the stack, stack[1] the value just under that one, and so on. A function call like

  x = foo(y,z+1);

would be compiled into a code sequence along the lines of

push y onto the runtime stack; 
evaluate z+1; 
push the result onto the runtime stack
push (space for the return value) onto the runtime stack 
save all CPU registers
push address RET onto the runtime stack 
jump to start of foo's body 
RET: x = stack[1] 
pop runtime stack 4 times 
restore all CPU registers

As you can see, there’s a fair amount of overhead involved in passing parameters and return address information to a function when making a call. The amount of time spent on this overhead is really all that large. If the function body contains several statements in any kind of loop, then the overhead is probably a negligable fraction of the total time spent on the call.

class Foo {
private:
   int bar;
public:
   int getBar ();
   void setBar (int);
};

int Foo::getBar ()  {return bar;}

void Foo::setBar (int b) {bar = b;}

But many ADTs have member functions that are only one or two lines long, and often trivial lines at that. For these functions, the overhead associated with each call may exceed the time required to do the function body itself. Furthermore, because these functions are often the primary means of accessing the ADT’s contents, sometimes these functions get called thousands of times or more inside the application’s loops.

For these kinds of trivial functions, C++ offers the option of declaring them as inline.

inlineFunct.cpp
class Foo {
private:
   int bar;
public:
   int getBar () {return bar;}  
   void setBar (int);
};

inline
void Foo::setBar (int b) {bar = b;}

An inline function can be written one of two ways. First, it can be written inside the class declaration. Second, we can place the reserved word inline in front of the function definition written in its usual place outside the class declaration. When we make a call to an inline function, the compiler simply replaces the call by a compiled copy of the function body (with some appropriate renaming of variables to avoid conflicts). So, if we have

inline int foo(int a, int b)
{
  return a+b-1;
}

and we later make a call

  x = foo(y,z+1);

This would be compiled into a code sequence along the lines of

evaluate z+1, storing result in tempB 
evaluate y + tempB - 1, storing result in x

Most of the overhead of making a function call hs been eliminated.

Inline functions can reduce the run time of a program by removing unnecessary function calls, but, used unwisely, may also cause the size of the program to explode. Consequently, they should be used only by frequently-called functions with bodies that take only 1 or 2 lines of code. For larger functions, the times savings would be negligible (as a fraction of the total time) while the memory penalty is more severe, and for infrequently used functions, who cares?

Inlining is only a recommendation from the programmer to the compiler. The compiler may ignore an inline declaration and continue treating it as a conventional function if it prefers. In particular, note that inlining of functions with recursive calls is impossible, as is inlining of most virtual function calls. Many compilers will refuse to inline any function whose body contains a loop. Others may have their own peculiar limitations.

2.7 Why is operator++ sometimes declared with an int parameter?

The ++ and -- operators are unusual in that they can be written in either prefix (++x, --x) or postfix (x++, x--) form. Whether you write ++x or x++, the value of x is increased by 1. But when you write a ++ expression inside another expression, its return value depends on whether you used the prefix or postfix form.

So, for example, the code

int i = 0; 
int j = 0;
cout << ++i << ' ' << j++ << endl;
cout << i << ' ' << j << endl;

would print

1 0
1 1

Now that’s all very well for the builtin ++ for int, but what happens when we write ++ for our own classes? Like most operators, ++ can be thought of as a shorthand for a function named operator++, so it’s not too hard to see that we can say:

class SomethingWeCanIncrement
{
  ⋮
  SomethingWeCanIncrement& operator++ (); 
  ⋮
};

but, somewhat late in the game, the designers of C++ realized that they had neglected to provide a syntax for indicating whether a unary operator was prefix or postfix. This is only a problem for ++ and --, because these are the only operators that can be written in both forms. The solution they came up with is a complete kludge. If you declare

const MyIncrementableClass operator++();
const MyIncrementableClass operator--();

you are declaring the prefix operators. If you declare

MyIncrementableClass operator++(int);
MyIncrementableClass operator--(int);

you are declaring the postfix operators.

What do you do with the int parameter for the postfix operators?

Absolutely nothing! It’s just a dummy parameter used to distinguish the prefix and postfix forms. Finally, note that the prefix forms return a reference. The postfix forms return a non-reference value. That’s because the prefix forms are returning the value that has been incremented/decremented. That value exists, so it’s easy to return:

class MyIncrementableClass {
   ⋮
  const MyIncrementableClass operator++() {
        ⋮
     // do what you need to do to increment it
        ⋮
     return *this;
  }
   ⋮

On the other hand, the postfix form returns the value before the increment/decrement takes place. Usually the only way to do that is to make a copy of that value first, then do the increment, then return the copied value.

class MyIncrementableClass {
   ⋮
  const MyIncrementableClass operator++(int) {
        MyIncrementableClass clone = *this; // save old value
        operator++();                       // increment this
        return clone;                       // return the old value
  }
   ⋮

2.8 I have other C++ questions.

Many questions about C++ can be answered at the C++ FAQ LITE. Information about the class and function templates in the C++ standard library can be found at SGI’s site.

3 C++ Compilers and Error Messages

3.1 How do I fix errors involving undeclared/undefined names? }

First, look very closely at the error messages. Does it say “undeclared” or “undefined”? These are two very different things, and understanding the difference is the key to fixing the problem.

So, if the compiler says that a function is undeclared, it means that you tried to use it before presenting its declaration, or forgot to declare it at all.

The compiler never complains about definitions, because an apparently missing definition might just be in some other file you are going to compile as part of the program.

But when you try to produce the executable program by linking all the compiled .o or .obj files produced by the compiler, the linker may complain that a symbol is undefined (none of the compiled files provided a definition) or is multiply defined (you provided two definitions for one name, or somehow compiled the same definition into more than one .o or .obj file).

For example, if you forget a function body, the linker will eventually complain that the function is undefined (but the name of the function may be mangled in the error message, see below). If you put a variable or function definition in a .h file and include that file from more than one place, the linker will complain that the name is multiply defined.

3.2 What should I do about the error message “warning: … is implicitly a typename” or “warning: implicit typename is deprecated”?

Actually, you could just ignore these, because they are only warnings, not errors. Your code will compile properly even with these. This error arises in certain uses of template parameters (or of names that are typedef’d in terms of a template parameter. For example,

template <class Container, class T>
void fillContainer (Container& c, T value)
{
   Container::iterator b = c.begin();
   Container::iterator e = c.end();
   fill (b, e, value);
}

will probably get this warning from g++ complaining about the mentions of “Container::iterator”. The fix is

template <class Container, class T>
void fillContainer (Container& c, T value)
{
   typename Container::iterator b = c.begin();
   typename Container::iterator e = c.end();
   fill (b, e, value);
}

3.3 Why do I get a message about discarding qualifiers?

The message

In some-function-name, passing const some-type-name … discards qualifiers

occurs when you try to pass a const object to a function that might try to change the object’s value. For example, if you have a class C:

class C {
public:
   C();
   int foo ();
   void bar (std::string& s);
   ⋮  
}

and you try to compile the following code:

void baz (const C& c1, C& c2, const std::string& str)
{
  int i = c1.foo();   // error!
  int j = c2.foo();
  c2.bar(str);        // error!
    ⋮

then a C++ compiler should flag the 1st and 3rd line indicated above. The g++ compiler will say something along the lines of

In function 'void baz(const C&, C&, const std::string&)': 
   passing 'const C' as 'this' argument of 'int C::foo()' discards qualifiers 

In function 'void baz(const C&, C&, const std::string&)':
  passing 'const std::string' as argument 1 of 
  'void C::bar(std::string&)' discards qualifiers

The first message complains that you have passed a const object as the left-hand parameter (implicitly named this) to the function foo, which has not promised to leave that parameter unchanged. You have, in effect, tried to discard the “qualifier” (the word “const”) in the const C& datatype. The second message makes a similar complaint about the string parameter being passed to bar. Again, the object being passed is marked as const, but the declaration of bar suggests that bar is allowed to change the string it receives as a parameter. To get rid of this message, you must examine what it is you are trying to do and determine whether:

constfix1.cpp
class C {
public:
   C();
int foo () const;  // don't change the object it's applied to
void bar (const std::string& s);  // don't change s
   ⋮  
}
constfix2.cpp
void baz (C& c1, C& c2, std::string& str)
{
  int i = c1.foo();   // ok
  int j = c2.foo();
  c2.bar(str);        // ok
    ⋮
constfix3.cpp
void baz (const C& c1, C& c2, const std::string& str)
{
  C c1Copy = c1;
  int i = c1Copy.foo();   // ok
  int j = c2.foo();
    ⋮

3.4 How do I fix errors “No match for…” or “Cannot bind …”

These are variation on the messages saying a symbol is undeclared. In particular, you will get this message when you call a function, and there are one or more functions with that name, but your set of actual parameters’ data types do not match up with the formal parameter list of any of the declared functions.

Either

This can be one of the longer error messages you will ever get, as g++ tries to list out all the functions with the same name that it knows, with the data types of all their parameters, as a way of showing you all your existing options. Although the list can be a bit daunting, if you are running in a support environment (e.g., emacs) that let’s you step from message to message while displaying the relevant line of code, this list can actually be quite helpful.

4 The Java Programming Language

4.1 How do I determine the starting class of a Java program?

A starting class for a program must contain a function of the form

static int main (String[] args) {
   ⋮
}

This is quite similar to the main() function used in C++, though because Java does not allow stand-alone functions, the main() function in Java must be a class member. If a project is supposed to provide more than one program, then there may be many different starting classes within the project. A good way to list them quickly (on a Unix/Linux system) is

find yourProjectDirectory -name '*.java' | xargs grep main