1.0 Introduction To C
1.0 Introduction To C
The very first thing you need to do, before starting out in C, is to make sure
that you have a compiler. What is a compiler, you ask? A compiler turns the
program that you write into an executable that your computer can actually
understand and run. If you're taking a course, you probably have one
provided through your school. If you're starting out on your own, your best
bet is to use Code::Blocks with MinGW. If you're on Linux, you can use gcc,
and if you're on Mac OS X, you can use XCode. If you haven't yet done so,
go ahead and get a compiler set up--you'll need it for the rest of the tutorial.
Intro to C
1
Let's look at the elements of the program. The #include is a "preprocessor"
directive that tells the compiler to put code from the header called stdio.h
into our program before actually creating the executable. By including
header files, you can gain access to many different functions--both the printf
and getchar functions are included in stdio.h.
The next important line is int main(). This line tells the compiler that there is
a function named main, and that the function returns an integer, hence int.
The "curly braces" ({ and }) signal the beginning and end of functions and
other code blocks. If you have programmed in Pascal, you will know them as
BEGIN and END. Even if you haven't programmed in Pascal, this is a good
way to think about their meaning.
The printf function is the standard C way of displaying output on the screen.
The quotes tell the compiler that you want to output the literal string as-is
(almost). The '\n' sequence is actually treated as a single character that
stands for a newline (we'll talk about this later in more detail); for the time
being, just remember that there are a few sequences that, when they
appear in a string literal, are actually not displayed literally by printf and that
'\n' is one of them. The actual effect of '\n' is to move the cursor on your
screen to the next line. Notice the semicolon: it tells the compiler that you're
at the end of a command, such as a function call. You will see that the
semicolon is used to end many lines in C.
Finally, at the end of the program, we return a value from main to the
operating system by using the return statement. This return value is
important as it can be used to tell the operating system whether our
program succeeded or not. A return value of 0 means success.
The final brace closes off the function. You should try compiling this program
2
and running it. You can cut and paste the code into a file, save it as a .c file,
and then compile it. If you are using a command-line compiler, such as
Borland C++ 5.5, you should read the compiler instructions for information
on how to compile. Otherwise compiling and running should be as simple as
clicking a button with your mouse (perhaps the "build" or "run" button).
You might start playing around with the printf function and get used to
writing simple C programs.
Comments are critical for all but the most trivial programs and this tutorial
will often use them to explain sections of code. When you tell the compiler a
section of text is a comment, it will ignore it when running the code, allowing
you to use any text you want to describe the real code. To create a comment
in C, you surround the text with /* and then */ to block off everything
between as a comment. Certain compiler environments or text editors will
change the color of a commented area to make it easier to spot, but some
will not. Be certain not to accidentally comment out code (that is, to tell the
compiler part of your code is a comment) you need for the program.
When you are learning to program, it is also useful to comment out sections
of code in order to see how the output is affected.
Using Variables
But first, before you try to receive input, you must have a place to store that
input. In programming, input and data are stored in variables. There are
several different types of variables; when you tell the compiler you are
declaring a variable, you must include the data type along with the name of
the variable. Several basic types include char, int, and float. Each type can
store different types of data.
3
A variable of type char stores a single character, variables of type int store
integers (numbers without decimal places), and variables of type float store
numbers with decimal places. Each of these variable types - char, int, and
float - is each a keyword that you use when you declare a variable. Some
variables also use more of the computer's memory to store their values.
It may seem strange to have multiple variable types when it seems like
some variable types are redundant. But using the right variable size can be
important for making your program efficient because some variables require
more memory than others. For now, suffice it to say that the different
variable types will almost all be used!
Before you can use a variable, you must tell the compiler about it by
declaring it and telling the compiler about what its "type" is. To declare a
variable you use the syntax <variable type> <name of variable>;. (The
brackets here indicate that your replace the expression with text described
within the brackets.) For instance, a basic variable declaration might look
like this:
int myVariable;
Note once again the use of a semicolon at the end of the line. Even though
we're not calling a function, a semicolon is still required at the end of the
"expression". This code would create a variable called myVariable; now we
are free to use myVariable later in the program.
While you can have multiple variables of the same type, you cannot have
multiple variables with the same name. Moreover, you cannot have variables
and functions with the same name.
4
A final restriction on variables is that variable declarations must come before
other types of statements in the given "code block" (a code block is just a
segment of code surrounded by { and }). So in C you must declare all of
your variables before you do anything else:
Wrong
#include <stdio.h>
int main()
{
/* wrong! The variable declaration must appear first */
printf( "Declare x next" );
int x;
return 0;
}
Fixed
#include <stdio.h>
int main()
{
int x;
printf( "Declare x first" );
return 0;
}
Reading input
Using variables in C for input or output can be a bit of a hassle at first, but
bear with it and it will make sense. We'll be using the scanf function to read
in a value and then printf to read it back out. Let's look at the program and
then pick apart exactly what's going on. You can even compile this and run it
if it helps you follow along.
5
#include <stdio.h>
int main()
{
int this_is_a_number;
So what does all of this mean? We've seen the #include and main function
before; main must appear in every program you intend to run, and the
#include gives us access to printf (as well as scanf). (As you might have
guessed, the io in stdio.h stands for "input/output"; std just stands for
"standard.") The keyword int declares this_is_a_number to be an integer.
This is where things start to get interesting: the scanf function works by
taking a string and some variables modified with &. The string tells scanf
what variables to look for: notice that we have a string containing only "%d"
-- this tells the scanf function to read in an integer. The second argument of
scanf is the variable, sort of. We'll learn more about what is going on later,
but the gist of it is that scanf needs to know where the variable is stored in
order to change its value. Using & in front of a variable allows you to get its
location and give that to scanf instead of the value of the variable. Think of it
like giving someone directions to the soda aisle and letting them go get a
coca-cola instead of fetching the coke for that person. The & gives the scanf
function directions to the variable.
When the program runs, each call to scanf checks its own input string to see
what kinds of input to expect, and then stores the value input into the
variable.
The second printf statement also contains the same '%d'--both scanf and
printf use the same format for indicating values embedded in strings. In this
case, printf takes the first argument after the string, the variable
6
this_is_a_number, and treats it as though it were of the type specified by
the "format specifier". In this case, printf treats this_is_a_number as an
integer based on the format specifier.
Of course, no matter what type you use, variables are uninteresting without
the ability to modify them. Several operators used with variables include the
following: *, -, +, /, =, ==, >, <. The * multiplies, the / divides, the -
subtracts, and the + adds. It is of course important to realize that to modify
the value of a variable inside the program it is rather important to use the
equal sign. In some languages, the equal sign compares the value of the left
and right values, but in C == is used for that task. The equal sign is still
extremely useful. It sets the value of the variable on the left side of the
equals sign equal to the value on the right side of the equals sign. The
operators that perform mathematical functions should be used on the right
side of an equal sign in order to assign the result to a variable on the left
side.
The other form of equal, ==, is not a way to assign a value to a variable.
Rather, it checks to see if the variables are equal. It is extremely useful in
many areas of C; for example, you will often use == in such constructions
as conditional statements and loops. You can probably guess how < and >
function. They are greater than and less than operators.
7
For example:
a < 5 /* Checks to see if a is less than five */
a > 5 /* Checks to see if a is greater than five */
a == 5 /* Checks to see if a equals five, for good measure */
Lesson 2: If statements in C
The ability to control the flow of your program, letting it make decisions on
what code to execute, is valuable to the programmer. The if statement
allows you to control if a program enters a section of code or not based on
whether a given condition is true or false. One of the important functions of
the if statement is that it allows the program to select an action based upon
the user's input. For example, by using an if statement to check a user-
entered password, your program can decide whether a user is allowed
access to the program.
Before discussing the actual structure of the if statement, let us examine the
meaning of TRUE and FALSE in computer terminology. A true statement is
one that evaluates to a nonzero number. A false statement evaluates to
zero. When you perform comparison with the relational operators, the
operator will return 1 if the comparison is true, or 0 if the comparison is
false. For example, the check 0 == 2 evaluates to 0. The check 2 == 2
evaluates to a 1. If this confuses you, try to use a printf statement to output
the result of those various comparisons (for example printf ( "%d", 2 ==
1 );)
8
When programming, the aim of the program will often require the checking
of one value stored by a variable against another value to determine
whether one is larger, smaller, or equal to the other.
Here are the relational operators, as they are known, along with examples:
> greater than 5 > 4 is TRUE
< less than 4 < 5 is TRUE
>= greater than or equal 4 >= 4 is TRUE
<= less than or equal 3 <= 4 is TRUE
== equal to 5 == 5 is TRUE
!= not equal to 5 != 4 is TRUE
It is highly probable that you have seen these before, probably with slightly
different symbols. They should not present any hindrance to understanding.
Now that you understand TRUE and FALSE well as the comparison operators,
let us look at the actual structure of if statements.
Basic If Syntax
Here, we're just evaluating the statement, "is five less than ten", to see if it
is true or not; with any luck, it is! If you want, you can write your own full
program including stdio.h and put this in the main function and run it to
test.
9
For example:
if ( TRUE ) {
/* between the braces is the body of the if statement */
Execute all statements inside the body
}
Else
Else if
Another use of else is when there are multiple conditional statements that
may all evaluate to true, yet you want only one if statement's body to
execute. You can use an "else if" statement following an if statement and its
body; that way, if the first statement is true, the "else if" will be ignored, but
if the if statement is false, it will then check the condition for the else if
statement. If the if statement was true the else statement will not be
checked. It is possible to use numerous else if statements to ensure that
only one block of code is executed.
10
Let's look at a simple program for you to try out on your own.
#include <stdio.h>
When using if statements, you will often wish to check multiple different
11
conditions. You must understand the Boolean operators OR, NOT, and AND.
The boolean operators function in a similar way to the comparison
operators: each returns 0 if evaluates to FALSE or 1 if it evaluates to TRUE.
NOT: The NOT operator accepts one input. If that input is TRUE, it returns
FALSE, and if that input is FALSE, it returns TRUE. For example, NOT (1)
evaluates to 0, and NOT (0) evaluates to 1. NOT (any number but zero)
evaluates to 0. In C NOT is written as !. NOT is evaluated prior to both AND
and OR.
AND: This is another important command. AND returns TRUE if both inputs
are TRUE (if 'this' AND 'that' are true). (1) AND (0) would evaluate to zero
because one of the inputs is false (both must be TRUE for it to evaluate to
TRUE). (1) AND (1) evaluates to 1. (any number but 0) AND (0) evaluates to
0. The AND operator is written && in C. Do not be confused by thinking it
checks equality between numbers: it does not. Keep in mind that the AND
operator is evaluated before the OR operator.
OR: Very useful is the OR statement! If either (or both) of the two values it
checks are TRUE then it returns TRUE. For example, (1) OR (0) evaluates to
1. (0) OR (0) evaluates to 0. The OR is written as || in C. Those are the pipe
characters. On your keyboard, they may look like a stretched colon. On my
computer the pipe shares its key with \. Keep in mind that OR will be
evaluated after AND.
Try some of these - they're not too hard. If you have questions about them,
feel free to stop by our forums.
A. !( 1 || 0 ) ANSWER: 0
B. !( 1 || 1 && 0 ) ANSWER: 0 (AND is evaluated before OR)
C. !( ( 1 || 0 ) && 0 ) ANSWER: 1 (Parenthesis are useful)
12
Lesson 3: Loops
Loops are used to repeat a block of code. Being able to have your program
repeatedly execute a block of code is one of the most basic but useful tasks
in programming -- many programs or websites that produce extremely
complex output (such as a message board) are really only executing a single
task many times. (They may be executing a small number of tasks, but in
principle, to produce a list of messages only requires repeating the operation
of reading in some data and displaying it.) Now, think about what this
means: a loop lets you write a very simple statement to produce a
significantly greater result simply by repetition.
One caveat: before going further, you should understand the concept of C's
true and false, because it will be necessary when working with loops (the
conditions are the same as with if statements). This concept is covered in
the previous tutorial. There are three types of loops: for, while, and
do..while. Each of them has their specific uses. They are all outlined below.
FOR - for loops are the most useful type. The syntax for a for loop is
The variable initialization allows you to either declare a variable and give it a
value or give a value to an already existing variable. Second, the condition
tells the program that while the conditional expression is true the loop
should continue to repeat itself. The variable update section is the easiest
way for a for loop to handle changing of the variable. It is possible to do
things like x++, x = x + 10, or even x = random ( 5 ), and if you really
wanted to, you could call other functions that do nothing to the variable but
still have a useful effect on the code. Notice that a semicolon separates each
of these sections, that is important. Also note that every single one of the
sections may be empty, though the semicolons still have to be there. If the
condition is empty, it is evaluated as true and the loop will repeat until
something else stops it.
13
Example:
#include <stdio.h>
int main()
{
int x;
/* The loop goes while x < 10, and x increases by one every
loop*/
for ( x = 0; x < 10; x++ ) {
/* Keep in mind that the loop condition checks
the conditional statement before it loops again.
consequently, when x equals 10 the loop breaks.
x is updated before the condition is checked. */
printf( "%d\n", x );
}
getchar();
}
This program is a very simple example of a for loop. x is set to zero, while x
is less than 10 it calls printf to display the value of the variable x, and it
adds 1 to x until the condition is met. Keep in mind also that the variable is
incremented after the code in the loop is run for the first time.
while ( condition ) { Code to execute while the condition is true } The true
represents a boolean expression which could be x == 1 or while ( x != 7 ) (x
does not equal 7). It can be any combination of boolean statements that are
legal. Even, (while x ==5 || v == 7) which says execute the code while x
equals five or while v equals 7. Notice that a while loop is like a stripped-
down version of a for loop-- it has no initialization or update section.
However, an empty condition is not legal for a while loop as it is with a for
loop.
14
Example:
#include <stdio.h>
int main()
{
int x = 0; /* Don't forget to declare variables */
This was another simple example, but it is longer than the above FOR loop.
The easiest way to think of the loop is that when it reaches the brace at the
end it jumps back up to the beginning of the loop, which checks the
condition again and decides whether to repeat the block another time, or
stop and move to the next statement after the block.
DO..WHILE - DO..WHILE loops are useful for things that want to loop at least
once. The structure is
do {
} while ( condition );
Notice that the condition is tested at the end of the block instead of the
beginning, so the block will be executed at least once. If the condition is
true, we jump back to the beginning of the block and execute it again. A
do..while loop is almost the same as a while loop except that the loop body
is guaranteed to execute at least once. A while loop says "Loop while the
condition is true, and execute this block of code", a do..while loop says
"Execute this block of code, and then continue to loop while the condition is
true".
15
Example:
#include <stdio.h>
int main()
{
int x;
x = 0;
do {
/* "Hello, world!" is printed at least one time
even though the condition is false */
printf( "Hello, world!\n" );
} while ( x != 0 );
getchar();
}
Keep in mind that you must include a trailing semi-colon after the while in
the above example. A common error is to forget that a do..while loop must
be terminated with a semicolon (the other loops should not be terminated
with a semicolon, adding to the confusion). Notice that this loop will execute
once, because it automatically executes before checking the condition.
Two keywords that are very important to looping are break and continue.
The break command will exit the most immediately surrounding loop
regardless of what the conditions of the loop are. Break is useful if we want
to exit a loop under special circumstances. For example, let's say the
program we're working on is a two-person checkers game. The basic
structure of the program might look like this:
while (true)
{
take_turn(player1);
take_turn(player2);
}
This will make the game alternate between having player 1 and player 2
take turns. The only problem with this logic is that there's no way to exit the
game; the loop will run forever! Let's try something like this instead:
16
while(true)
{
if (someone_has_won() || someone_wants_to_quit() == TRUE)
{break;}
take_turn(player1);
if (someone_has_won() || someone_wants_to_quit() == TRUE)
{break;}
take_turn(player2);
}
This code accomplishes what we want--the primary loop of the game will
continue under normal circumstances, but under a special condition (winning
or exiting) the flow will stop and our program will do something else.
Continue is another keyword that controls the flow of loops. If you are
executing a loop and hit a continue statement, the loop will stop its current
iteration, update itself (in the case of for loops) and begin to execute again
from the top. Essentially, the continue statement is saying "this iteration of
the loop is done, let's continue with the loop without executing whatever
code comes after me." Let's say we're implementing a game of Monopoly.
Like above, we want to use a loop to control whose turn it is, but controlling
turns is a bit more complicated in Monopoly than in checkers. The basic
structure of our code might then look something like this:
This way, if one player can't take her turn, the game doesn't stop for
everybody; we just skip her and keep going with the next player's turn.
17
Lesson 4: Functions in C
Now that you should have learned about variables, loops, and conditional
statements it is time to learn about functions. You should have an idea of
their uses as we have already used them and defined one in the guise of
main. Getchar is another example of a function. In general, functions are
blocks of code that perform a number of pre-defined commands to
accomplish something productive. You can either use the built-in library
functions or you can create your own functions.
Do not think that 'a' will change at random, it will be set to the value
returned when the function is called, but it will not change again.
arg_type just means the type for each argument -- for instance, an int, a
float, or a char. It's exactly the same thing as what you would put if you
were declaring a variable.
There can be more than one argument passed to a function or none at all
(where the parentheses are empty), and it does not have to return a value.
Functions that do not return values have a return type of void. Let's look at
a function prototype:
int mult ( int x, int y );
18
This prototype specifies that the function mult will accept two arguments,
both integers, and that it will return an integer. Do not forget the trailing
semi-colon. Without it, the compiler will probably think that you are trying to
write the actual definition of the function.
When the programmer actually defines the function, it will begin with the
prototype, minus the semi-colon. Then there should always be a block
(surrounded by curly braces) with the code that the function is to execute,
just as you would write it for the main function. Any of the arguments
passed to the function can be used as if they were declared in the block.
Finally, end it all with a cherry and a closing brace. Okay, maybe not a
cherry.
int main()
{
int x;
int y;
This program begins with the only necessary include file. Next is the
prototype of the function. Notice that it has the final semi-colon! The main
function returns an integer, which you should always have to conform to the
standard.
19
You should not have trouble understanding the input and output functions if
you've followed the previous tutorials.
Notice how printf actually takes the value of what appears to be the mult
function. What is really happening is printf is accepting the value returned by
mult, not mult itself. The result would be the same as if we had use this
print instead
printf( "The product of your two numbers is %d\n", x * y );
The mult function is actually defined below main. Because its prototype is
above main, the compiler still recognizes it as being declared, and so the
compiler will not give an error about mult being undeclared. As long as the
prototype is present, a function can be used even if there is no definition.
However, the code cannot be run without a definition even though it will
compile.
Prototypes are declarations of the function, but they are only necessary to
alert the compiler about the existence of a function if we don't want to go
ahead and fully define the function. If mult were defined before it is used,
we could do away with the prototype--the definition basically acts as a
prototype as well.
Return is the keyword used to force the function to return a value. Note that
it is possible to have a function that returns no value. If a function returns
void, the return statement is valid, but only if it does not have an
expression. In other words, for a function that returns void, the statement
"return;" is legal, but usually redundant. (It can be used to exit the function
before the end of the function.)
20
Another reason for functions is to break down a complex program into logical
parts. For example, take a menu program that runs complex code when a
menu choice is selected. The program would probably best be served by
making functions for each of the actual menu choices, and then breaking
down the complex tasks into smaller, more manageable tasks, which could
be in their own functions. In this way, a program can be designed that
makes sense when read. And has a structure that is easier to understand
quickly. The worst programs usually only have the required function, main,
and fill it with pages of jumbled code.
Switch case statements are a substitute for long if statements that compare
a variable to several "integral" values ("integral" values are simply values
that can be expressed as an integer, such as the value of a char). The basic
format for using switch case is outlined below. The value of the variable
given into switch is compared to the value following each of the cases, and
when one value matches the value of the variable, the computer continues
executing the program from that point.
switch ( <variable> ) {
case this-value:
Code to execute if <variable> == this-value
break;
case that-value:
Code to execute if <variable> == that-value
break;
...
default:
Code to execute if <variable> does not equal the value
following any of the cases
break;
}
The condition of a switch statement is a value. The case says that if it has
the value of whatever is after that case then do whatever follows the colon.
The break is used to break out of the case statements. Break is a keyword
that breaks out of the code block, usually surrounded by braces, which it is
in.
21
In this case, break prevents the program from falling through and executing
the code in all the other case statements. An important thing to note about
the switch statement is that the case values may only be constant integral
expressions. Sadly, it isn't legal to use case like this:
int a = 10;
int b = 10;
int c = 20;
switch ( a ) {
case b:
/* Code */
break;
case c:
/* Code */
break;
default:
/* Code */
break;
}
Below is a sample program, in which not all of the proper functions are
actually declared, but which shows how one would use switch in a program.
22
#include <stdio.h>
void playgame()
{
printf( "Play game called" );
}
void loadgame()
{
printf( "Load game called" );
}
void playmultiplayer()
{
printf( "Play multiplayer game called" );
}
int main()
{
int input;
printf( "1. Play game\n" );
printf( "2. Load game\n" );
printf( "3. Play multiplayer\n" );
printf( "4. Exit\n" );
printf( "Selection: " );
scanf( "%d", &input );
switch ( input ) {
case 1: /* Note the colon, not a semicolon */
playgame();
break;
case 2:
loadgame();
break;
case 3:
playmultiplayer();
break;
case 4:
printf( "Thanks for playing!\n" );
break;
default:
printf( "Bad input, quitting!\n" );
break;
}
getchar();
}
23
This program will compile, but cannot be run until the undefined functions
are given bodies, but it serves as a model (albeit simple) for processing
input. If you do not understand this then try mentally putting in if
statements for the case statements. Default simply skips out of the switch
case construction and allows the program to terminate naturally. If you do
not like that, then you can make a loop around the whole thing to have it
wait for valid input. You could easily make a few small functions if you wish
to test the code.
Lesson 7: Structures in C
Where Tag is the name of the entire type of structure and Members are the
variables within the struct. To actually create a single structure the syntax is
struct Tag name_of_single_structure;
24
For example:
struct example {
int x;
};
struct example an_example; /* Treating it like a normal variable
type
except with the addition of struct*/
an_example.x = 33; /*How to access its members */
int main()
{
struct database employee; /* There is now an employee variable
that has
modifiable variables inside it.*/
employee.age = 22;
employee.id_number = 1;
employee.salary = 12000.21;
}
The struct database declares that it has three variables in it, age,
id_number, and salary. You can use database like a variable type like int.
You can create an employee with the database type as I did above. Then, to
modify it you call everything with the 'employee.' in front of it. You can also
return structures from functions by defining their return type as a structure
type. For instance:
struct database fn();
I will talk only a little bit about unions as well. Unions are like structures
except that all the variables share the same memory. When a union is
declared the compiler allocates enough memory for the largest data-type in
the union. It's like a giant storage chest where you can store one large item,
or a small item, but never the both at the same time.
25
The '.' operator is used to access different variables inside a union also.
A quick example:
#include <stdio.h>
struct xampl {
int x;
};
int main()
{
struct xampl structure;
struct xampl *ptr;
structure.x = 12;
ptr = &structure; /* Yes, you need the & when dealing with
structures and using pointers to
them*/
printf( "%d\n", ptr->x ); /* The -> acts somewhat like the *
when
does when it is used with
pointers
It says, get whatever is at
that memory
address Not "get what that
memory address
is"*/
getchar();
}
26
Lesson 8: Arrays in C
Arrays are useful critters that often show up when it would be convenient to
have one name for a group of variables of the same type that can be
accessed by a numerical index. For example, a tic-tac-toe board can be held
in an array and each element of the tic-tac-toe board can easily be accessed
by its position (the upper left might be position 0 and the lower right
position 8). At heart, arrays are essentially a way to store many values
under the same name. You can make an array out of any data-type including
structures and classes.
Each of the bracket pairs is a slot in the array, and you can store information
in slot--the information stored in the array is called an element of the array.
It is very much as though you have a group of variables lined up side by
side.
This would make an integer array with 100 slots (the places in which values
of an array are stored). To access a specific part element of the array, you
merely put the array name and, in brackets, an index number. This
corresponds to a specific element of the array. The one trick is that the first
index number, and thus the first element, is zero, and the last is the number
of elements minus one. The indices for a 100 element array range from 0 to
99. Be careful not to "walk off the end" of the array by trying to access
element 100!
What can you do with this simple knowledge? Let's say you want to store a
string, because C has no built-in datatype for strings, you can make an array
of characters.
For example:
char astring[100];
27
will allow you to declare a char array of 100 elements, or slots. Then you can
receive input into it from the user, and when the user types in a string, it will
go in the array, the first character of the string will be at position 0, the
second character at position 1, and so forth. It is relatively easy to work with
strings in this way because it allows support for any size string you can
imagine all stored in a single variable with each element in the string stored
in an adjacent location--think about how hard it would be to store nearly
arbitrary sized strings using simple variables that only store one value. Since
we can write loops that increment integers, it's very easy to scan through a
string:
char astring[10];
int i = 0;
/* Using scanf isn't really the best way to do this; we'll talk
about that
in the next tutorial, on strings */
scanf( "%s", astring );
for ( i = 0; i < 10; ++i )
{
if ( astring[i] == 'a' )
{
printf( "You entered an a!\n" );
}
}
Let's look at something new here: the scanf function call is a tad different
from what we've seen before. First of all, the format string is '%s' instead of
'%d'; this just tells scanf to read in a string instead of an integer. Second,
we don't use the ampersand! It turns out that when we pass arrays into
functions, the compiler automatically converts the array into a pointer to the
first element of the array. In short, the array without any brackets will act
like a pointer. So we just pass the array directly into scanf without using the
ampersand and it works perfectly.
Also, notice that to access the element of the array, we just use the brackets
and put in the index whose value interests us; in this case, we go from 0 to
9, checking each element to see if it's equal to the character a. Note that
some of these values may actually be uninitialized since the user might not
28
input a string that fills the whole array--we'll look into how strings are
handled in more detail in the next tutorial; for now, the key is simply to
understand the power of accessing the array using a numerical index.
Imagine how you would write that if you didn't have access to arrays! Oh
boy.
Multidimensional arrays are arrays that have more than one index: instead
of being just a single line of slots, multidimensional arrays can be thought of
as having values that spread across two or more dimensions. Here's an easy
way to visualize a two-dimensional array:
[][][][][]
[][][][][]
[][][][][]
[][][][][]
[][][][][]
The syntax used to actually declare a two dimensional array is almost the
same as that used for declaring a one-dimensional array, except that you
include a set of brackets for each dimension, and include the size of the
dimension. For example, here is an array that is large enough to hold a
standard checkers board, with 8 rows and 8 columns:
int two_dimensional_array[8][8];
You can easily use this to store information about some kind of game or to
write something like tic-tac-toe. To access it, all you need are two variables,
one that goes in the first slot and one that goes in the second slot. You can
make three dimensional, four dimensional, or even higher dimensional
arrays, though past three dimensions, it becomes quite hard to visualize.
for instance,
/* set the first element of my_first to be the letter c */
my_string[0] = 'c';
29
<arrayname>[<arrayindexnumber1>][<arrayindexnumber2>] =
<whatever>;
Let me note again that you should never attempt to write data past the last
element of the array, such as when you have a 10 element array, and you
try to write to the [10] element. The memory for the array that was
allocated for it will only be ten locations in memory, (the elements 0 through
9) but the next location could be anything. Writing to random memory could
cause unpredictable effects--for example you might end up writing to the
video buffer and change the video display, or you might write to memory
being used by an open document and altering its contents. Usually, the
operating system will not allow this kind of reckless behavior and will crash
the program if it tries to write to unallocated memory.
You will find lots of useful things to do with arrays, from storing information
about certain things under one name, to making games like tic-tac-toe.
We've already seen one example of using loops to access arrays; here is
another, more interesting, example!
#include <stdio.h>
int main()
{
int x;
int y;
int array[8][8]; /* Declares an array like a chessboard */
30
Just to touch upon a final point made briefly above: arrays don't require a
reference operator (the ampersand) when you want to have a pointer to
them. For example:
char *ptr;
char str[40];
ptr = str; /* Gives the memory address without a reference
operator(&) */
As opposed to
int *ptr;
int num;
ptr = # /* Requires & to give the memory address to the ptr
*/
The fact that arrays can act just like pointers can cause a great deal of
confusion
Lesson 9: C Strings
This lesson will discuss C-style strings, which you may have already seen in
the array tutorial. In fact, C-style strings are really arrays of chars with a
little bit of special sauce to indicate where the string ends. This tutorial will
cover some of the tools available for working with strings--things like
copying them, concatenating them, and getting their length.
What is a String?
Note that along with C-style strings, which are arrays, there are also string
literals, such as "this". In reality, both of these string types are merely just
collections of characters sitting next to each other in memory. The only
difference is that you cannot modify string literals, whereas you can modify
arrays. Functions that take a C-style string will be just as happy to accept
string literals unless they modify the string (in which case your program will
crash). Some things that might look like strings are not strings; in particular,
a character enclosed in single quotes, like this, 'a', is not a string. It's a
single character, which can be assigned to a specific location in a string, but
which cannot be treated as a string. (Remember how arrays act like pointers
when passed into functions? Characters don't, so if you pass a single
31
character into a function, it won't work; the function is expecting a char*,
not a char.)
To recap: strings are arrays of chars. String literals are words surrounded by
double quotation marks.
"This is a static string"
Remember that special sauce mentioned above? Well, it turns out that C-
style strings are always terminated with a null character, literally a '\0'
character (with the value of 0), so to declare a string of 49 letters, you need
to account for it by adding an extra character, so you would want to say:
char string[50];
This would declare a string with a length of 50 characters. Do not forget that
arrays begin at zero, not 1 for the index number. In addition, we've
accounted for the extra with a null character, literally a '\0' character. It's
important to remember that there will be an extra character on the end on a
string, just like there is always a period at the end of a sentence. Since this
string terminator is unprintable, it is not counted as a letter, but it still takes
up a space. Technically, in a fifty char array you could only hold 49 letters
and one null character at the end to terminate the string.
can also be used as a string. If you have read the tutorial on pointers, you
can do something such as:
arry = malloc( sizeof(*arry) * 256 );
which allows you to access arry just as if it were an array. To free the
memory you allocated, just use free:
For example:
free ( arry );
32
Using Strings
Strings are useful for holding all types of long input. If you want the user to
input his or her name, you must use a string. Using scanf() to input a string
works, but it will terminate the string after it reads the first space, and
moreover, because scanf doesn't know how big the array is, it can lead to
"buffer overflows" when the user inputs a string that is longer than the size
of the string (which acts as an input "buffer").
There are several approaches to handling this problem, but probably the
simplest and safest is to use the fgets function, which is declared in stdio.h.
There are a few new things here. First of all, let's clear up the questions
about that funky FILE* pointer. The reason this exists is because fgets is
supposed to be able to read from any file on disk, not just from the user's
keyboard (or other "standard input" device). For the time being, whenever
we call fgets, we'll just pass in a variable called stdin, defined in stdio.h,
which refers to "standard input". This effectively tells the program to read
from the keyboard. The other two arguments to fgets, str and size, are
simply the place to store the data read from the input and the size of the
char*, str. Finally, fgets returns str whenever it successfully read from the
input.
When fgets actually reads input from the user, it will read up to size - 1
characters and then place the null terminator after the last character it read.
fgets will read input until it either has no more room to store the data or
until the user hits enter. Notice that fgets may fill up the entire space
allocated for str, but it will never return a non-null terminated string to you.
Let's look at an example of using fgets, and then we'll talk about some
pitfalls to watch out for.
For a example:
#include <stdio.h>
33
int main()
{
/* A nice long string */
char string[256];
getchar();
}
Remember that you are actually passing the address of the array when you
pass string because arrays do not require an address operator (&) to be
used to pass their addresses, so the values in the array string are modified.
The one thing to watch out for when using fgets is that it will include the
newline character ('\n') when it reads input unless there isn't room in the
string to store it. This means that you may need to manually remove the
input. One way to do this would be to search the string for a newline and
then replace it with the null terminator. What would this look like? See if you
can figure out a way to do it before looking below:
char input[256];
int i;
34
Here, we just loop through the input until we come to a newline, and when
we do, we replace it with the null terminator. Notice that if the input is less
than 256 characters long, the user must have hit enter, which would have
included the newline character in the string! (By the way, aside from this
example, there are other approaches to solving this problem that use
functions from string.h.)
strcmp will accept two strings. It will return an integer. This integer will
either be:
Negative if s1 is less than s2.
Zero if s1 and s2 are equal.
Positive if s1 is greater than s2.
Strcmp performs a case sensitive comparison; if the strings are the same
except for a difference in cAse, then they're countered as being different.
Strcmp also passes the address of the character array to the function to
allow it to be accessed.
char *strcat ( char *dest, const char *src );
strcpy is short for string copy, which means it copies the entire contents of
src into dest. The contents of dest after strcpy will be exactly the same as
src such that strcmp ( dest, src ) will return 0.
size_t strlen ( const char *s );
35
strlen will return the length of a string, minus the terminating character ('\
0'). The size_t is nothing to worry about. Just treat it as an integer that
cannot be negative, which is what it actually is. (The type size_t is just a
way to indicate that the value is intended for use as a size of something.)
int main()
{
36
char name[50];
char lastname[50];
char fullname[100]; /* Big enough to hold both name and
lastname */
getchar();
return 0;
}
37
Safe Programming
The above string functions all rely on the existence of a null terminator at
the end of a string. This isn't always a safe bet. Moreover, some of them,
noticeably strcat, rely on the fact that the destination string can hold the
entire string being appended onto the end. Although it might seem like you'll
never make that sort of mistake, historically, problems based on accidentally
writing off the end of an array in a function like strcat, have been a major
problem.
which will only copy len bytes from src to dest (len should be less than the
size of dest or the write could still go beyond the bounds of the array).
Unfortunately, strncpy can lead to one niggling issue: it doesn't guarantee
that dest will have a null terminator attached to it (this might happen if the
string src is longer than dest). You can avoid this problem by using strlen to
get the length of src and make sure it will fit in dest. Of course, if you were
going to do that, then you probably don't need strncpy in the first place,
right? Wrong. Now it forces you to pay attention to this issue, which is a big
part of the battle.
In this tutorial, you'll learn how to do file IO, text and binary, in C,
using fopen, fwrite, and fread, fprintf, fscanf, fgetc and fputc.
FILE *
For C File I/O you need to use a FILE pointer, which will let the program
keep track of the file being accessed. (You can think of it as the memory
address of the file or the location of the file).
38
For example:
FILE *fp;
fopen
To open a file you need to use the fopen function, which returns a FILE
pointer. Once you've opened a file, you can use the FILE pointer to let the
compiler perform input and output functions on the file.
In the filename, if you use a string literal as the argument, you need to
remember to use double backslashes rather than a single backslash as you
otherwise risk an escape character such as \t. Using double backslashes \\
escapes the \ key, so the string works as it is expected. Your users, of
course, do not need to do this! It's just the way quoted strings are handled
in C and C++.
fopen modes
The allowed modes for fopen are as follows:
Note that it's possible for fopen to fail even if your program is perfectly
correct: you might try to open a file specified by the user, and that file might
not exist (or it might be write-protected). In those cases, fopen will return 0,
the NULL pointer.
FILE *fp;
39
fp=fopen("c:\\test.txt", "r");
This code will open test.txt for reading in text mode. To open a file in a
binary mode you must add a b to the end of the mode string; for example,
"rb" (for the reading and writing modes, you can add the b either after the
plus sign - "r+b" - or before - "rb+")
fclose
When you're done working with a file, you should close it using the function
An example of fclose is
fclose(fp);
To work with text input and output, you use fprintf and fscanf, both of which
are similar to their friends printf and scanfexcept that you must pass the
FILE pointer as first argument. For example:
FILE *fp;
fp=fopen("c:\\test.txt", "w");
fprintf(fp, "Testing...\n");
Notice that fgetc returns an int. What this actually means is that when it
reads a normal character in the file, it will return a value suitable for storing
40
in an unsigned char (basically, a number in the range 0 to 255). On the
other hand, when you're at the very end of the file, you can't get a character
value--in this case, fgetc will return "EOF", which is a constant that indicates
that you've reached the end of the file. To see a full example using fgetc in
practice, take a look at the example here.
The fputc function allows you to write a character at a time--you might find
this useful if you wanted to copy a file character by character. It looks like
this:
Note that the first argument should be in the range of an unsigned char so
that it is a valid character. The second argument is the file to write to. On
success, fputc will return the value c, and on failure, it will return EOF.
41
size_of_elements is one. You can use the sizeof operator to get the size of
the various datatypes; for example, if you have a variable int x; you can get
the size of x with sizeof(x);. This usage works even for structs or arrays.
E.g., if you have a variable of a struct type with the name a_struct, you can
use sizeof(a_struct) to find out how much memory it is taking up.
e.g.,
sizeof(int);
The third argument is simply how many elements you want to read or write;
for example, if you pass a 100 element array, you want to read no more
than 100 elements, so you pass in 100.
The final argument is simply the file pointer we've been using. When fread is
used, after being passed an array, fread will read from the file until it has
filled the array, and it will return the number of elements actually read. If
the file, for example, is only 30 bytes, but you try to read 100 bytes, it will
return that it read 30 bytes. To check to ensure the end of file was reached,
use the feof function, which accepts a FILE pointer and returns true if the
end of the file has been reached.
fwrite is similar in usage, except instead of reading into the memory you
write from memory into a file.
For example,
FILE *fp;
fp=fopen("c:\\test.bin", "wb");
char x[10]="ABCDEFGHIJ";
fwrite(x, sizeof(x[0]), sizeof(x)/sizeof(x[0]), fp);
42
Lesson 11: Typecasting
Typecasting is a way to make a variable of one type, such as an int, act like
another type, such as a char, for one single operation. To typecast
something, simply put the type of variable you want the actual variable to
act as inside parentheses in front of the actual variable. (char)a will make 'a'
function as a char.
For example:
#include <stdio.h>
int main()
{
/* The (char) is a typecast, telling the computer to interpret
the 65 as a
character, not as a number. It is going to give the
character output of
the equivalent of the number 65 (It should be the letter A
for ASCII).
Note that the %c below is the format code for printing a
single character
*/
printf( "%c\n", (char)65 );
getchar();
}
One use for typecasting for is when you want to use the ASCII characters.
For example, what if you want to create your own chart of all 128 ASCII
characters. To do this, you will need to use to typecast to allow you to print
out the integer as its character equivalent.
#include <stdio.h>
int main()
{
for ( int x = 0; x < 128; x++ ) {
/* Note the use of the int version of x to output a
number and the use
43
* of (char) to typecast the x into a character which
outputs the
* ASCII character that corresponds to the current number
*/
printf( "%d = %c\n", x, (char)x );
}
getchar();
If you were paying careful attention, you might have noticed something kind
of strange: when we passed the value of x to printf as a char, we'd already
told the compiler that we intended the value to be treated as a character
when we wrote the format string as %c. Since the char type is just a small
integer, adding this typecast actually doesn't add any value!
On the other hand, it turns out that division between floating point numbers,
or even between one floating point number and an integer, is sufficient to
keep the result as a floating point number. So if we were performing some
kind of fancy division where we didn't want truncated values, we'd have to
cast one of the variables to a floating point type. For instance, (float)3/5
comes out to .6, as you would expect!
When might this come up? It's often reasonable to store two values in
integers. For instance, if you were tracking heart patients, you might have a
function to compute their age in years and the number of times they'd come
in for heart pain. One operation you might conceivably want to perform is to
compute the number of times per year of life someone has come in to see
their physician about heart pain. What would this look like?
/* magical function returns the age in years */
int age = getAge();
/* magical function returns the number of visits */
int pain_visits = getVisits();
44
float visits_per_year = pain_visits / age;
The problem is that when this program is run, visits_per_year will be zero
unless the patient had an awful lot of visits to the doc. The way to get
around this problem is to cast one of the values being divided so it gets
treated as a floating point number, which will cause the compiler to treat the
expression as if it were to result in a floating point number:
float visits_per_year = pain_visits / (float)age;
/* or */
float visits_per_year = (float)pain_visits / age;
This would cause the correct values to be stored in visits_per_year. Can you
think of another solution to this problem (in this case)?
The array of character pointers is the listing of all the arguments. argv[0] is
the name of the program, or an empty string if the name is not available.
After that, every element number less than argc is a command line
45
argument. You can use each argv element just like a string, or use argv as a
two dimensional array. argv[argc] is a null pointer.
How could this be used? Almost any program that wants its parameters to
be set when it is executed would use this. One common use is to write a
function that takes the name of a file and outputs the entire text of it onto
the screen.
#include <stdio.h>
46
}
fclose( file );
}
}
}
This program is fairly short, but it incorporates the full version of main and
even performs a useful function. It first checks to ensure the user added the
second argument, theoretically a file name. The program then checks to see
if the file is valid by trying to open it. This is a standard operation, and if it
results in the file being opened, then the return value of fopen will be a valid
FILE*; otherwise, it will be 0, the NULL pointer. After that, we just execute a
loop to print out one character at a time from the file. The code is self-
explanatory, but is littered with comments; you should have no trouble
understanding its operation this far into the tutorial. :-)
47