Preview of Pointers

This is a very quick look at a couple of things you need to know to make sense of the man pages and do the labs.

Pointers

You already know a language like Java, so some of this is actually already familiar to you.

In C, declarations like

int n;
char c;
say that n is an int and c is a char.

Declarations like

int *p;
char *q;
say that p is a pointer to int and q is a pointer to char. In Java, we would have used the word "reference" instead of "pointer" (and, of course, in Java, you can have references/pointers only to objects, not to primitive types like int). Pointers, like references in Java, just hold memory addresses - they point to where something is stored (in this case, to where an int is stored or where a char is stored).

To specify the address of a variable like n, use the ampersand &, as in &n - this is "the address of n".
&n is an address/pointer value. And since n is an int, the type of &n is "pointer to int".

The ampersand & is used in the scanf function: consider the following code.

int n;
scanf("%d", &n);
scanf reads from standard input. The %d, says it's going to be reading an int value, i.e. characters like '0', '1', '2', ... that it can interpret as an int. The &n, says where to store this integer value, namely "at the address of n", or "where n is stored" or "in n".

And you already know about arrays. For example,
char s[20];
says that s is an array of char of size 20. In Java, the array s would be declared a bit differently, namely
char[] s = new char[20];
but in both C and Java s is now a reference/pointer to the first element in the array, i.e. an array name acts like a reference/pointer to the first element in the array. (Ok - there are some details/exceptions to explain at another time, but not here.)

It may seem odd that we don't write &s as the address of array s, i.e. the address of the first element of s. But we don't. (This was actually done in old, traditional C, but not any more.)

You will also see something like
char s[];
This says that s is an array of char, but doesn't specify the size. If this appears as a formal function parameter, as in

void f(char x[]){ ... function body here ... }
then it is replaced by the compiler with char *. In other words, there is no difference between defining a function with
void f(char x[]){ ... function body here ... }
and with
void f(char * x){ ... function body here ... }
And when you call this function, you can pass a previously declared array, as in this call:
f(s); where s is the array of 20 char we declared previously.

Knowing this this should help you better understand some of the man pages, like the one for gets. Here is part of that man page:

GETS(3)                   Linux Programmer's Manual                   GETS(3)

NAME
       ... gets ...

SYNOPSIS
       #include <stdio.h>

		...

       char *gets(char *s);

		...

DESCRIPTION

		...

       gets() reads a line from stdin into the buffer pointed to by s until
       either  a terminating newline or EOF, which it replaces with '\0'.  No
       check for buffer overrun is performed (see BUGS below).

       ...

RETURN VALUE

		...

       gets() ... returns s on success, and NULL on error or when  end
       of file occurs while no characters have been read.
...

What does this mean: "the buffer pointed to by s" means a char array used to hold the line just read. So you might call gets like this:
char buffer[20];
gets(buffer);
This reads the next line (which we hope will not be longer than 20) into the array called "buffer";

And what about the char * return value? gets normally returns a pointer to the array/buffer that it has just filled up. Or we could say it returns "a pointer to the first element of the array" or that it returns "the array".

But if there's an error or there is nothing left to read, i.e. end of file has been reached, then gets returns a different pointer, the one called NULL (declared in <stdio.h>. The NULL pointer is compatible with any type of pointer; i.e. it may be used where a pointer to char is expected or where a pointer to int is expected, and so on.

A note about the gets function: we mention it in this example just to make a point, and there may be an exercise involving it. BUT gets is deprecated and normally should not be used. You'll learn why later.

File Pointers and standard I/O

C has a type called FILE that's used to represent disk files. Say you have a file called "temp" on disk and you want to read from it. The "fopen" statements sets up a FILE structure to represent "temp" and returns a pointer to it, as in the following:
FILE * ptr = fopen("temp", "r")
The "r", says you want to read (as opposed to write) temp. The type of the variable ptr is FILE *, i.e. pointer to FILE, and it captures the return value of the fopen statement. It holds the address of the FILE structure then associated with the disk file called "temp". The fopen statement is the last time that the name "temp" is used. All the functions that then read the file use the variable called ptr instead, as in
int c;
c = fgetc(ptr);
fgetc reads the next byte from the file and returns it (as an int).

When you log in, the system has already opened 3 file streams for you, i.e. you have, without doing anything, 3 pointers of type FILE * available. These are
  • stdin - this is the "standard input" stream. Initially, the keyboard.
  • stdout- this is the "standard output" stream. Initially the monitor.
  • stderr- this is the "standard error" stream. Initially the monitor.
Look at the man page for getchar and fgetc. (They're on the same man page.) You'll see that getchar() is equivalent to fgetc(stdin). And putchar(n) does the same thing as fputc(n, stdout). And following the example above, fgetc(ptr) would return (as an int) the next byte from the file called "temp".

Knowing about the type FILE * should also help you understand the man page for fgets. Here is part of it:

char *fgets(char *s, int size, FILE *stream);
The third parameter is of type FILE * and says what file/stream you are reading form. If it is stdin, then you are reading form standard input. The first parameter specifies a char array that acts as a buffer where the lines read are stored. And the middle parameter limits the number of bytes read, so the input does not overflow the buffer (that was the problem with gets).

Instead of using gets(buffer), it's better to use fgets(buffer, size of the buffer, stdin), since gets doesn't care if the line read in is so large that if overflows the buffer (i.e. overwrites stuff that's stored after the buffer).

Return Values

The job of a function is often to return a value representing something. For example, the getchar() function returns a character read from standard input (promoted to an int). Sometimes the function needs to signal that something special has happened, for example, that an error has occurred or, for an input function like getchar(), that end of file has been reached (i.e. that there are no more characters to read).

In java, the function/method would signal an error by throwing an Exception/Error. C can't do that. Instead, C indicates both errors and non-error special conditions, like reaching end of file, by returning special values. Of course, a function is defined to return only one type of value, so a function like getchar() always returns an int, but some of the int return values are meant to be "normal" (representing the character read) and others are meant to indicate something unusual. And there should be no possible confusion about which is which.

The situation is similar for fgets. It normally returns a pointer to the start of the buffer it has just read a line into. But if the read was unsuccessful because of an error or because end of file was reached and there was nothing left to read, then it returns a different pointer, namely NULL.

See the man pages for getchar and fgets and look at the section titled RETURN VALUE.

Strings

C has no formal string type. A string is represented by an array of char with a trailing null character '\0' to mark the end of the string. For example, to make str the "string" holding "Example" you could do any of the following:
char str[] = {'E', 'x', 'a', 'm', 'p', 'l', 'e', '\0'};
char str[] = "Example";
char str[100] = {'E', 'x', 'a', 'm', 'p', 'l', 'e', '\0'}; /* only part of str "filled" */
or you could declare
char str[20];
and then read in a value using scanf
scanf("%s", str);
and enter the 7 letters E, x, a, m, p, l, e and then some whitespace at the keyboard. scanf will add the '\0' to mark the end of the string.

Functions that deal with "strings", take arrays of char (pointers to char) as their parameters, and expect that when scanning the array they will eventually hit a null character '\0'.

The standard C library has string handling functions. Read the text. You need to #include <string.h> to use them. See appendix B in the text. As a simple example, after the above declarations, you could write printf("%s\n", str), which would print out Example on a line. Or printf("The length of str is %d\n", strlen(str)), which would print out The length of str is 7 on a line. The strlen function does not count the trailing null character. It is not part of the string; it is there just to mark mark the end.

end of short blurb about pointers, file pointers, return values and strings