man
pages and do the labs.
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.
...
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";
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".
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.
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
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