C is a fairly unforgiving programming language–many assumptions you might make from your past experience in Java or other languages do not necessarily hold. Here are some of the most common issues might run into.

First Some Reference Material

Practical C Programming is a recommended textbook for Software Engineering 1, Computer Architecture, and Systems Programming. It has a lot of good material on C at both beginner and advanced levels. If you are a GW CS student, you will probably use this text in several different classes.

Essential C - This free reference guide covers the basics of C syntax. If you are in CS-2113, you should:

  • Briefly skim pages 3-14 to remind yourself about variable types and control structures. This will be a good reference if you need to look it up later. Take note of the various “pitfalls” it lists.
  • Read pages 15-23 carefully, but for now you can skip the sections on structs and multi dimensional arrays. Be sure that you read and understand the parts about regular arrays, pointers, and strings.
  • Read pages 33-40 to learn about how pointers and arrays are similar, and more about dynamic memory in C.

That’s only about 15 pages total! Easy Peasy!

Also, check out C Stdio for a reference listing all the functions included in the stdio.h library. Lots of good stuff, with examples!

for Loop Variables

From Java, you are probably used to running a for loop with code like this:

 // buggy code
 for(int i=0; i < 100; i++)
 {
   // do something here
 }

However, trying to compile this code with gcc may give you an error (depending on your compiler version and flags):

 $ gcc test.c
 test.c: In function ‘main’:
 test.c:27: error: ‘for’ loop initial declaration used outside C99 mode

For once, gcc is actually giving you a useful error—you are declaring a variable within the for loop even though you have not told the compiler to use the C99 standard. There are two ways to fix this problem. You can edit the code and change it to:

 int i;
 for(i=0; i < 100; i++)
 {
   // do something here
 }

or you can tell the compiler that you know about the C99 standard which introduced the ability to declare variables within for loops:

 $ gcc test.c -std=c99

The best practice is generally to declare your variables in advance as this will make your code usable even on machines that do not support C99.

printf() Specifiers

Another easy mistake to make is to use the wrong printf symbol when trying to write to the screen. Here are some of the basic datatypes which printf supports:

   %d       Signed integer.
   %o       Octal integer.
   %x       Hex integer.
   %u       Unsigned integer.
   %c       Character.
   %s       String.
   %f       double
   %e       double.
   %g       double.
   %p       pointer.

If you were to make a mistake like:

 // buggy code
 float pi = 3.14159;
 printf("my favorite number is pi=%d\n", pi); // oops! used %d instead of %f

your code will compile (although you should get a warning). If you actually wanted to print out the value of the float rounded down to the nearest integer, you would need to use:

 float pi = 3.14159;
 printf("pi rounded down is %d\n",(int) pi); //cast to (int)!

For more details on what printf can do, check this page.

Variable Initialization

Important for Exercise 1!

A common misconception is that all newly defined variables will be equal to zero the first time you access them. This is the case in languages like Java because you are working within a runtime system which can easily clear the value of a variable when it is created. In C, you are given direct access to the memory used by each variable and it is not necessarily cleaned before you get to it. This can lead to very surprising results. Consider the following:

 // buggy code
 int a;
 int b=0;

 printf("The value of a=%d and b=%d\n", a, b);

While you might expect this to print out “0 and 0” in fact you will probably get a garbage value for a. This is particularly important when you are working with arrays–do not assume that all entries in your array will initially be zero!

Return Values

Consider the following buggy code:

 int timesTwo(int i)
 {
   int result = 2*i;
   return result;
 }
 int timesThree(int i)
 {
   int result = 3*i;
   int temp = result * 1000;
   // oops! forgot to "return result"
 }

 int main()
 {
   int a=1;
   int b, c;
   b = timesTwo(a);
   c = timesThree(a);
   printf("a=%d, b=%d, c=%d");
 }

In this example we forgot to include a return call at the end of the timesThree() function. As a result, the compiler just guesses which variable it should return, and in this case will usually return the value of “temp” instead of “result”. This code produces no errors or warnings when compiled, so be sure to remember to add a return call!

Typos

Perhaps the easiest type of mistake to have is a simple misspelling. Suppose you accidentally spell printf incorrectly:

 print("oops, I forgot to include the f in printf");

If you compile this you will get an error like the following:

 Undefined                       first referenced
  symbol                             in file
 print                               /tmp/scratch.t7a4oI/ccIvqL46.o
 ld: fatal: Symbol referencing errors. No output written to a.out
 collect2: ld returned 1 exit status

This error is a little hard to read, but it does in fact include the information you need to find the bug. The error it reports is an Undefined symbol—this typically means you are referring to a function or variable which has not actually been defined. It also does have a line saying print, which is the symbol it can not define. Unfortunately, the compiler does not catch the error until it has reached the linking stage of compilation. That is why it cannot tell you where exactly in the .c file the error occurred, and instead only tells you about where the error was in the partially compiled object file.

Notes on Arrays

Using arrays in C is a little different from what you are used to in Java. First, a simple array must be declared with a constant value for its size:

 int myArray[100]; // create an array with 100 integer entries
 int hundred=100;
 int illegalArray[hundred]; // WILL NOT COMPILE

Second, remember that the values of an array may initially have some random data in it, so read the note on Variable Initialization above.

You may want to send arrays into and out of functions. Perhaps you tried something like this:

 int[] initArray() // WILL NOT WORK

to have a function which will return an array. Unfortunately, you cannot do this. Returning arrays typically requires the use of pointers (discussed in Lectures 2-3). A simpler option is to pass the array you’ve already created as a parameter to the function:

 #include <stdio.h>

 void setTo(int a[], int value)
 {
   /* Set all entries in an array to 'value'
       Note that myarray will be passed by reference,
       so changes will be permanent.  However, the "int value"
       parameter is passed by value, so changing it here
       would not impact "favoriteNumber" in the main() func.
   */
   int i;
   for(i=0; i < 10; i++)
   {
       a[i] = value;
   }
 }

 int main()
 {
   int i;
   int myarray[10];
   int favoriteNumber = 7;
   setTo(myarray, favoriteNumber);
   for(i=0; i < 10; i++)
   {
       printf("%d\n", myarray[i]);
   }
   return 0;
 }

Arrays are passed to functions by reference, meaning that a modification to the parameter will affect the original from the calling function. In contrast, most variables such as integers are passed by value—essentially creating a copy just for the function to use. Thus in the example above, the function is able to permanently modify the array myArray, but it would not be able to affect the value of favoriteNumber.