Reading and writing in C

The language we've been learning, C++, was expanded from a previous language called C (get the joke?...). So that you have some exposure to C, we'll have the occasional lab pointing out the differences between what we know in C++, and how you do the same thing in C. Though C has a long history stretching back to the early 70s, it remains a lightning-quick language, and is still used heavily.

Part 1: Hello, world!

Here's a C language hello world program. In a directory called lab06, copy it into a file called hello.c (.c is the usual C file extension, just as .cpp is the usual C++ file extension).

#include <stdio.h>
int main() {
  printf("Hello, world!\n");
  return 0;
}

Note we don't have an endl command here - we just add the newline character (\n) to the end of our string to get the same effect.

To compile this, use gcc instead of g++ (because C++ is a superset of C, g++ will actually work, but won't constrain you to C-only conventions... and we want you to be aware of those constraints). Just like g++, you'll notice it creates an executable, which you run to run the program.

Now, let's suppose that we want this to print out Hello, world! five times. You might try the following program:

#include <stdio.h>

int main() {
  printf("Here it comes, 5 times\n");
  for (int i = 0; i < 5; i++)
    printf("Hello, world!\n");
  return 0;
}

If you compile this, you'll get an error (do that, and read the error). While there are a few different C standards (referenced in the error with the C99 mode text), "traditional" C (more-correctly called C89, or ANSI C) requires you to declare variables at the very top of a scope block. Our particular implementation of the compiler allows variables to be declared anywhere, with the exception of for loops, which may not be declared in the loop itself. So, our int i statement needs to be moved, like this:

#include <stdio.h>

int main() {
  int i;
  printf("Here it comes, 5 times\n");
  for (i = 0; i < 5; i++)
    printf("Hello, world!\n");
  return 0;
}

Compile that and run it.

Similarly, this would compile:

#include <stdio.h>

int main() {
  int i;
  printf("Here it comes, 5 times\n");
  for (i = 0; i < 5; i++) {
    int j = i;
    printf("Hello, world!\n");
  }
  return 0;
}

because int j occurs at the very top of the for loop - which is j's scope block.

So, in review, so far our differences are:

  1. gcc instead of g++
  2. variables declared at the top of scope blocks
  3. printf instead of cout
  4. stdio.h instead of iostream
  5. No using namespace std

Part 2: Printing variable values

Now, suppose we have the following program in C++, and we'd like to turn it into C:

#include <iostream>
using namespace std;

int main() {
  int n = 5;
  cout << "Here it comes, " << n << " times" << endl;
  for (int i = 0; i < n; i++)
    cout << "Hello, world!" << endl;
  return 0;
}

For this program, that first cout line requires us to print out the value of n. We can do that in C using printf, as well.

#include <stdio.h>

int main() {
  int n = 11;
  int i;
  printf("Here it comes, %d times\n", n);
  
  for (i = 0; i < n; i++)
    printf("Hello, world!\n");
  return 0;
}

Compile it and run it. Weird, right? The %d is known as a format specifier, and it tells the program how to display the values amongst the rest of the string. All the format specifiers can be seen here - as you can see, d is for "decimal integers" (decimal, as opposed to octal, or hexadecimal, rather than because it has a decimal - it doesn't, because it's an integer). If you replace %d with %o, then you'll get 11 in octal (which is 13), while if you replace it with %x, you'll get 11 in hex (which is b).

If you replace it with %f, you'll get a compilation error, because integers aren't floats (that is, numbers with an actual decimal point in them).

If you want to print out multiple numbers, then you need multiple specifiers:

#include <stdio.h>

int main() {
  int int1 = 3, int2 = 5;
  double doub1 = 4.3;
  printf("the integers %d, %d, and the double %lf walk into a bar...\n",
         int1, int2, doub1); //continuation of previous line
  return 0;
}

Compile it and run it. Notice that int1 goes into the first specifier, int2 into the second, and doub1 into the third - this happened because this is the order they're listed in after the string (%lf, by the way, refers to "long floats" aka doubles).

Action item: In a file called firstC.c, convert the following C++ program into C:

#include <iostream>
using namespace std;

int main() {
  int x = 3;
  double y = 5.2;
  cout << "The integer is " << x << ", and the double is " << y << endl;
  double sum = x+y;
  cout << "Their sum is " << sum << "." << endl;
  cout << "Here is that sum (" << sum << ") printed " << x << " times." << endl;
  for (int i = 0; i < x; i++)
    cout << "Ta-da: " << y << endl;

  return 0;
}

If in Taylor's section, submit with submit lab06

If in Chambers' sections, submit with submit lab06 firstC.c

Pointers

The next thing we'd like to do is read values in from the user, cin-style. To do this in C, though, we need to see for the first time, a Very Big Idea in programming - pointers. We are going to spend a LOT of time on pointers, but for now, let's keep them as simple as possible.

Variables store information in a particular memory location. Every memory location is numbered, usually using hex. We can see that location by using the & operator. Compile and run the following C++ program:

#include <iostream>
using namespace std;

int main() {
  int a = 3;

  cout << a << ' ' << &a << endl;

  return 0;
}

In that program, when the cout command runs, you first see the value of a (3), and then see the location of a. I have no way of predicting what that location will print out, since each of your operating systems will do things a little differently, but I know you'll get a large hex number out there.

It turns out we can store memory locations in a variable of their own, called pointers.

#include <iostream>
using namespace std;

int main() {
  int a = 3;        //The variable
  int* ptrToA = &a; //The pointer

  cout << a << ' ' << &a << ' ' << ptrToA << endl;

  return 0;
}

The * in the declaration means ptrToA is a pointer - the int* means when you go to the stored value, you'll find an int - so, ptrToA is a pointer to an integer.

This is going to be a powerful, powerful tool, but for now, we're just going to use it to read things in from the user.

Reading things in

If you think about the C++ statement cin >> x, what that actually means is "take the value off the text stream, and store it in the location used by x. C++ uses some fairly complex stuff (called pass-by-reference) to make this happen that we're not ready to appreciate yet, but it's a feature that C doesn't have. So in C, we have to explicitly say, "read in a value, and store it in this location, indicated by this pointer." We do this using the scanf function:

#include <stdio.h>

int main() {
  int x = 0;
  int* ptrToX = &x;
  printf("Please enter an integer: ");
  scanf("%d", ptrToX);

  printf("x is now %d\n", x);

  return 0;
}

Go ahead and compile and run that program, to see that indeed, an input integer reads in to x. The scanf line is saying, read in an integer ("%d", as with printf), and store it in the location indicated by ptrToX. ptrToX, of course, points to x. So, when we print out x, we see the value that has been read in.

Like with printf, a single scanf command can read in multiple values, as long as the number of specifiers and number of pointers is the same.

scanf can do some other cool things, too. Suppose you want the user to type in a command like Convert ___ dollars. and you want to burn the "Convert" and the "dollars." You can do that this way:

#include <stdio.h>

int main() {

  double money = 0;
  double* moneyPtr = &money;
  scanf("Convert %lf dollars.", moneyPtr); //burn "Convert" then read in the
                                           //double into the location
                                           //indicated by moneyPtr, then burn
                                           //"dollars."
  printf("Converting %lf dollars...\n", money);

  return 0;
}

Action item: In a file called temp.c, write a C program that performs temperature conversions like the following (the second of the three lines is user input):

taylor@kurt:~/zee/temp/firstC$ ./a.out 
Convert 85.2 degrees F
85.200000 degrees F is 29.555556 degrees C

and

taylor@kurt:~/zee/temp/firstC$ ./a.out 
Convert 29.6 degrees C
29.600000 degrees C is 85.280000 degrees F

For your reference, TC = (TF − 32) * (5 / 9).

If in Taylor's section, submit with submit lab06

If in Chambers' sections, submit with submit lab06 firstC.c temp.c