Contents
i
ii CONTENTS
5 C Quick Reference 28
5.1 C Quick Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
The rst feature that can be observed in this program is These linking tasks are performed by a linker, which
that comments are enclosed by "/*" and "*/". Comments takes one or more object les and links them to binary
can go almost anywhere, since the compiler ignores them. library les to create an executable le that can actually
Another obvious feature is that individual statements in be run.
the program end with a ";". Either forgetting a ";" or
C has a large number of libraries and library functions. C
adding one where it isn't needed is a common C program-
ming bug. Lines of code are grouped into blocks by using by itself has few statements, so much of its functionality
is implemented as library calls.
curly brackets ("{ }").
C is case-sensitive. All C keywords are in lower-case. Commands intended for the C preprocessor, instead of
Program variable and function names in whatever case the C compiler itself, start with a "#" and are known as
desired, but by convention they should be in lower case. preprocessor directives or metacommands. The ex-
Constants, to be discussed a bit later, are upper case by ample program above has two such metacommands:
convention. #include <stdio.h> #dene PI 3.14
Not all the lines in a C program are executable statements.
Some of the statements shown above are preprocessor The rst statement, "#include <stdio.h>;", simply merges
directives. the contents of the le stdio.h into the current program
3
4 CHAPTER 1. INTRODUCTION TO C; FUNCTIONS; CONTROL CONSTRUCTS
le before compilation. The stdio.h le contains decla- The declarations of volume, radius, and result spec-
rations required for use of the standard-I/O library, which ify a oating-point variable. The declaration allows vari-
provides the printf()" function. ables to be initialized when declared if need be, in this
Incidentally, the stdio.h le, or header le, only con- case declaring radius and assigning it a value of 3.
tains declarations. The actual code for the library is con-All three of these declarations dene variables block
tained in separate library les that are added at link time.
scope. Variables with block scope exist only within the
Custom header les loaded with custom declarations can blocks that declare them. Variables of the same name
be created if needed, and then included in a program as could be declared in dierent blocks without interfer-
follows: ence. It is also possible to declare variables with le scope
#include mydefs.h that can be shared by all functions by declaring them out-
side of all blocks within a translation unit. By default, all
identiers with le scope are given external linkage. It is
Angle brackets ("< >") are not used with custom header only with the keyword static that an identier is given
les. They only are used to dene the default header internal linkage. Declarations like this:
les provided standard with a C compiler (and which, on
Unix/Linux systems, are found in directory "/usr/include extern int a;
).
A C program is built up of one or more functions. identify a as an int whose storage is dened elsewhere;
preferably in another translation unit.
The program above contains two user-dened functions,
main()" and sphere()", as well as the printf()" library /* global.c */ #include <stdio.h> void somefunc( void
function. ); int globalvar; void main() { extern int globalvar;
The main()" function is mandatory when writing a self- globalvar = 42; somefunc(); printf( "%d\n, globalvar );
contained program. It denes the function that is auto- } void somefunc( void ) { extern int globalvar; printf(
matically executed when the program is run. All other "%d\n, globalvar ); globalvar = 13; }
functions will be directly or indirectly called by main()".
A C function is called simply by specifying its name, with Besides the variable declarations, the example programs
any arguments enclosed in following parentheses, with above also feature a function declaration, or function
commas separating the arguments. In the program above, prototype, that allows the C compiler to perform checks
the printf()" function is called as follows: on calls to the function in the program and ag an error
if they are not correct:
printf( Volume: %f\n, volume );
oat sphere(oat);
Firing! BANG! BANG! BANG! BANG! BANG! Fired! /* fnarray.c */ #include <stdio.h> #dene SIZE 10 void
This program passes a single parameter, an integer, to the testfunc( int a[] ); void main() { int ctr, a[SIZE]; for( ctr
re()" function. The function uses a for loop to exe- = 0; ctr < SIZE; ++ctr ) { a[ctr] = ctr * ctr; } testfunc( a
cute a BANG!" the specied number of times -- more ); } void testfunc( int a[] ) { int n; for( n = 0; n < SIZE;
on for later. ++ n ) { printf( "%d\n, a[n] ); } }
This prints:
a=1 b=100 a=42 b=666 x=1 y=100
Arrays can be sent to functions as if they were any other 1.3 C Control Constructs
type of variable:
1.3. C CONTROL CONSTRUCTS 7
C contains a number of looping constructs, such as the a == b Is a equal to b"? a = b Assign value of b to a.
while loop:
/* while.c */ #include <stdio.h> void main() { int test = C also contains decision-making statements, such as if":
10; while( test > 0 ) { printf( test = %d\n, test ); test = /* if.c */ #include <stdio.h> #dene MISSILE 1 void
test - 2; } }
re( int weapon ); void main() { re( MISSILE ); } void
re( int weapon ) { if( weapon == MISSILE ) { printf(
If test starts with an initial value less than or equal to Fired missile!\n ); } if( weapon != MISSILE ) { printf(
0 the while loop will not execute even once. There is a Unknown weapon!\n); } }
variant, do, that will always execute at least once:
/* do.c */ #include <stdio.h> void main() { int test = 10; This example can be more easily implemented using an
do { printf( test = %d\n, test ); test = test - 2; } while( else clause:
test > 0 ); } /* ifelse.c */ void re( int weapon ) { if( weapon ==
MISSILE ) { printf( Fired missile!\n ); } else { printf(
The most common looping construct is the for loop, Unknown weapon!\n); } }
which creates a loop much like the while loop but in a
more compact form: Since there is only one statement in each clause the curly
/* for.c */ #include <stdio.h> void main() { int test; for( brackets aren't really necessary. This example would
test = 10; test > 0; test = test - 2 ) { printf( test = %d\n, work just as well:
test ); } } void re( int weapon ) { if( weapon == MISSILE ) printf(
Fired missile!\n ); else printf( Unknown weapon!\n
Notice that with all these loops, the initial loop statement ); }
does not end with a ";". If a ";" was placed at the end
of the for statement above, the for statement would
However, the brackets make the structure more obvious
execute to completion, but not run any of the statements and prevent errors if you add statements to the conditional
in the body of the loop.
clauses. The compiler doesn't care one way or another, it
The for loop has the syntax: generates the same code.
for( <initialization>; <operating test>; <modifying There is no elseif keyword, but if statements can be
expression> ) nested:
/* nestif.c */ #include <stdio.h> #dene MISSILE 1
All the elements in parentheses are optional. A for loop #dene LASER 2 void re( int weapon ) void main() {
could be run indenitely with: re( LASER ); } void re( int weapon ) { if( weapon
== MISSILE ) { printf( Fired missile!\n ); } else { if(
for( ; ; ) { ... }
weapon == LASER ) { printf( Fired laser!\n ); } else {
printf( Unknown weapon!\n); } } }
-- although using an indenite while is cleaner:
while( 1 ) { ... } This is somewhat clumsy. The switch statement does a
cleaner job:
It is possible to use multiple expressions in either the ini- /* switch.c */ void re( int weapon ) { switch( weapon )
tialization or the modifying expression with the ", oper- { case MISSILE: printf( Fired missile!\n ); break; case
ator: LASER: printf( Fired laser!\n ); break; default: printf(
/* formax.c */ #include <stdio.h> void main() { int a, b; Unknown weapon!\n); break; } }
for ( a = 256, b = 1; b < 512 ; a = a / 2, b = b * 2 ) {
printf( a = %d b = %d\n, a, b ); } } The switch statement tests the value of a single variable;
unlike the if statement, it can't test multiple variables.
The conditional tests available to C are as follows: The optional default clause is used to handle conditions
not covered by the other cases.
a == b equals a != b not equals a < b less than a > b
greater than a <= b less than or equals a >= b greater Each clause ends in a break, which causes execution to
than or equals break out of the switch. Leaving out a break can be
another subtle error in a C program, since if it isn't there,
execution ows right through to the next clause. However,
The fact that "==" is used to perform the equals test, this can be used to advantage. Suppose in our example the
while "=" is used as the assignment operator, often causes routine can also be asked to re a ROCKET, which is the
confusion and is a common bug in C programming: same as a MISSILE:
8 CHAPTER 1. INTRODUCTION TO C; FUNCTIONS; CONTROL CONSTRUCTS
9
10 CHAPTER 2. C VARIABLES, OPERATORS & PREPROCESSOR DIRECTIVES
The null is used by C functions that manipulate strings to a pointer to the string it denes. This means that in the
determine where the end of the string is, and it is impor- following operation:
tant to remember the null is there. char *p; p = Life, the Universe, & Everything!";
The curious reader may wonder why the strcpy()" func- -- p ends up being a pointer to the memory in which
tion is needed to initialize the string. It might seem to be
the C compiler stored the string literal, and p[0]" would
easier to do: evaluate to L. In a similar sense, the following opera-
char s[128] = This is a test!"; tion:
In fact, this is an absurd operation, but to explain why, the char ch; ch = Life, the Universe, & Every-
concept of pointers must be introduced. thing!"[0];
-- would put the character L into the variable ch.
C programs can dene pointers that contain the ad-
This is very well and good, but why care? The reason to
dress of a variable or
care is because this explains why the operation:
an array. For example, a pointer could be dened named: char s[128] = This is a test!";
Elements of the structure are accessed with a dot The program follows:
notation (m.name, m.age, and m.wage). /* vector.c */ #include <stdio.h> #include <math.h>
#dene PI 3.141592654 struct v { double i, j, k; };
A structure can be copied to another structure with a sin- void vadd( struct v, struct v, struct v* ); void vprint(
gle assignment statement, as long as the structures are of struct v ); void vsub( struct v, struct v, struct v* );
the same type: double vnorm( struct v ); double vdot( struct v, struct
v ); double vangle( struct v, struct v ); void vcross(
struct person m, n; ... m = n; struct v, struct v, struct v* ); int main() { struct v
It is also possible to declare arrays of structures: v1 = { 1, 2, 3 }, v2 = { 30, 50, 100 }, v3; double a;
printf( Sample Vector 1: " ); vprint( v1 ); printf(
struct person group[10]; ... strcpy( group[5].name, Sample Vector 2: " ); vprint( v2 ); vadd( v1, v2,
McQuack, Launchpad ); &v3 ); printf( Vector Add: " ); vprint( v3 ); vsub(
-- or even embed structures inside structure declarations: v1, v2, &v3 ); printf( Vector Subtract: " ); vprint(
v3 ); vcross( v1, v2, &v3 ); printf( Cross Product:
struct trip_rec { struct person traveler; char dest[50];
" ); vprint( v3 ); printf( "\n ); printf( Vector 1
int date[3]; int duration; oat cost; }
Norm: %f\n, vnorm( v1 ) ); printf( Vector 2 Norm:
-- in which case the nested structure would be accessed %f\n, vnorm( v2 ) ); printf( Dot Product: %f\n,
as follows: vdot( v1, v2 ) ); a = 180 * vangle( v1, v2) / PI ; printf(
struct trip_rec t1; ... strcpy( t1.traveler.name, Mar- Angle: %3f degrees.\n, a ); return 0; } void vadd(
tian, Marvin ); struct v a, struct v b, struct v *c ) /* Add vectors. */ {
c->i = a.i + b.i; c->j = a.j + b.j; c->k = a.k + b.k; }
The name of a structure denes a variable, not an ad- double vangle( struct v a, struct v b ) /* Get angle be-
dress. If the name of a structure is passed to a function, tween vectors. */ { double c; c = vdot( a, b ) / ( vnorm(
the function works only on its local copy of the structure. a ) * vnorm( b ) ); return acos( c ); } void vcross( struct
To return values, an address must be specied: v a, struct v b, struct v *c ) /* Cross product. */ { c->i
setstruct( &mystruct ); = a.j * b.k - a.k * b.j; c->j = a.k * b.i - a.i * b.k; c->k
= a.i * b.j - a.j * b.i; } double vdot( struct v a, struct
There is a shorthand way to get at the elements of a struc- v b ) /* Dot product of vectors. */ { return a.i * b.i +
ture with the pointer to the structure instead of the struc- a.j * b.j + a.k * b.k; } double vnorm ( struct v a ) /*
ture itself. If sptr is a pointer to a structure of type Norm of vectors. */ { return sqrt( a.i * a.i + a.j * a.j
person, its elds can be accessed as follows: + a.k * a.k ); } void vprint ( struct v a ) /* Print vector.
strcpy( sptr->name, Leghorn, Foghorn ); sptr- */ { printf( " I = %6.2f J = %6.2f K = %6.2f\n, a.i,
>age = 50; sptr->wage = 12.98; a.j, a.k ); } void vsub ( struct v a, struct v b, struct v
12 CHAPTER 2. C VARIABLES, OPERATORS & PREPROCESSOR DIRECTIVES
*c ) /* Subtract vectors. */ { c->i = a.i - b.i; c->j = a.j It is possible to dene custom enumerated types in
- b.j; c->k = a.k - b.k; } C. For example:
The concept of local and global variables should be enum day { saturday, sunday, monday, tuesday,
clear by now. It is wednesday, thursday, friday };
-- denes enumerated type day to consist of the val-
also possible to declare a local variable as static, mean- ues of the days of the week. In practice, the values are
ing it retains its value from one invocation of the function merely text constants associated to a set of consecutive
to the next. For example: integer values. By default, the set begins at 0 and counts
#include <stdio.h> void testfunc( void ); int main() { up, so here saturday has the value 0, sunday has the
int ctr; for( ctr = 1; ctr <= 8; ++ctr ) { testfunc(); } value 1, and so on. Any set of number assignments can
return 0; } void testfunc( void ) { static int v; printf( be specied if desired:
"%d\n, 2*v ); ++v; } enum temps { zero = 0, freeze = 32, boil = 220 };
This prints: Obviously much the same could be done with sets of
0 2 4 6 8 10 12 14 "#dene directives, but this is a much cleaner solution.
Once the type is dened, for example, variables of that
-- since the initial value of a integer is 0 by default. It is
type can be declared as follows:
not a good idea to rely on a default value!
enum day today = wednesday;
There are two other variable declarations that should The variable today will act as an int variable and will
be recognized, though allow the operations valid for int variables. Once more,
remember that C doesn't do much in the way of bounds
theres little reason to use them: register, which de- checking, and it is not wise to rely on the C compiler to
clares that a variable should be assigned to a CPU register, give warnings.
and volatile, which tells the compiler that the contents
of the variable may change spontaneously. Finally, a typedef declaration can be used to dene
There is more and less than meets the eye to these dec- custom data types:
larations. The register declaration is discretionary: the
variable will be loaded into a CPU register if it can, and typedef ch[128] str;
if not it will be loaded into memory as normal. Since a
good optimizing compiler will try to make the best use of Then variables of this type could be declared as follows:
CPU registers anyway, this is not in general all that useful str name;
a thing to do.
The volatile declaration appears ridiculous at rst sight,
something like one of those joke computer commands 2.2 C Operators
like halt and catch re. Actually, its used to describe a
hardware register that can change independently of pro-
gram operation, such as the register for a realtime clock.
Finally, C supports a set of bitwise operations: () [] -> . ! ~ ++ -- (cast) & sizeof - (minus prex) / % + -
a = ~a bit complement a = b << c shift b left by number << >> < <= > >= == != & ^ | && || ?: = += -= *= /= %=
of bits stored in c a = b >> c shift b right by number of >>= <<= &= ^= |= ,
bits stored in c a = b & c b AND c a = b ^ c b XOR c a = Of course, parentheses can be used to control precedence.
b | c b OR c If in doubt about the order of evaluation of an expression,
C allows math operations to be performed in a shortcut add more parentheses. They won't cause any trouble and
might save some pain.
fashion:
operation shortcut _________________________ a = a Advanced math operations are available as library func-
* b a *= b a = a / b a /= b a = a % b a %= b a = a + b tions. These will be discussed in a later chapter.
a += b a = a - b a -= b a = a << b a <<= b a = a >> b a
>>= b a = a & b a &= b a = a ^ b a ^= b a = a | b a |= b
_________________________ 2.3 C Preprocessor Directives
The C relational operations were discussed in the previous
chapter and are repeated here for completeness:
a == b equals a != b not equals a < b less than a > b We've already seen the "#include and "#dene prepro-
greater than a <= b less than or equals a >= b greater than cessor directives. The C preprocessor supports several
or equals other directives as well. All such directives start with a
These are actually math operations that yield 1 if true and "#" to allow them to be distinguished from C language
0 if false. The following operation actually makes syntac- commands.
tic sense: As explained in the rst chapter, the "#include directive
a = b * ( b < 2 ) + 10 * ( b >= 2 ); allows the contents of other les in C source code:
val = ABS(x++);
3.1 C Console IO board and echo it. kbhit() Check to see if a key has been
pressed.
The printf()" function, as explained previously, prints a
string that may include formatted data:
This chapter covers console (keyboard/display) and le printf( This is a test!\n );
I/O. You've already seen one console-I/O function,
-- which can include the contents of variables:
printf()", and there are several others. C has two sep-
arate approaches toward le I/O, one based on library printf( Value1: %d Value2: %f\n, intval, oatval );
functions that is similar to console I/O, and a second that The available format codes are:
uses system calls. These topics are discussed in detail
below. %d decimal integer %ld long decimal integer %c charac-
ter %s string %e oating-point number in exponential no-
Console I/O in general means communications with the tation %f oating-point number in decimal notation %g
computers keyboard and display. However, in most use %e and %f, whichever is shorter %u unsigned deci-
modern operating systems the keyboard and display are mal integer %o unsigned octal integer %x unsigned hex
simply the default input and output devices, and user can integer
easily redirect input from, say, a le or other program and
redirect output to, say, a serial I/O port: Using the wrong format code for a particular data type can
lead to bizarre output. Further control can be obtained
type inle > myprog > com with modier codes; for example, a numeric prex can
The program itself, myprog, doesn't know the dier- be included to specify the minimum eld width:
ence. The program uses console I/O to simply read its %10d
standard input (stdin)" -- which might be the keyboard,
a le dump, or the output of some other program -- and This species a minimum eld width of ten characters.
print to its standard output (stdout)" -- which might be If the eld width is too small, a wider eld will be used.
the display or printer or another program or a le. The Adding a minus sign:
program itself neither knows nor cares. %10d
Console I/O requires the declaration: -- causes the text to be left-justied. A numeric precision
#include <stdio.h> can also be specied:
Useful functions include: %6.3f
printf() Print a formatted string to stdout. scanf() Read This species three digits of precision in a eld six char-
formatted data from stdin. putchar() Print a single char- acters wide. A string precision can be specied as well,
acter to stdout. getchar() Read a single character from to indicate the maximum number of characters to be
stdin. puts() Print a string to stdout. gets() Read a line printed. For example:
from stdin. /* prtint.c */ #include <stdio.h> int main(void) { printf(
Windows-based compilers also have an alternative library "<%d>\n, 336 ); printf( "<%2d>\n, 336 ); printf(
of console I/O functions. These functions require the dec- "<%10d>\n, 336 ); printf( "<%10d>\n, 336 ); return
laration: 0; }
#include <conio.h> This prints:
The three most useful Windows console I/O functions are: <336> <336> < 336> <336 >
getch() Get a character from the keyboard (no need to Similarly:
press Enter). getche() Get a character from the key-
15
16 CHAPTER 3. C INPUT & OUTPUT
/* proat.c */ #include <stdio.h> int main(void) { printf( are comma-separated and swallow the comma when it is
"<%f>\n, 1234.56 ); printf( "<%e>\n, 1234.56 ); encountered.
printf( "<%4.2f>\n, 1234.56 ); printf( "<%3.1f>\n, If a format code is preceded with an asterisk, the data will
1234.56 ); printf( "<%10.3f>\n, 1234.56 ); printf( be read and discarded. For example, if the example were
"<%10.3e>\n, 1234.56 ); return 0; } changed to:
-- prints: scanf( "%d%*c%s, &val, name );
<1234.560000> <1.234560e+03> <1234.56> <1234.6> -- then if the two elds were separated by a ":", that char-
< 1234.560> < 1.234e+03> acter would be read in and discarded.
And nally: The scanf()" function will return the value EOF (an
/* prtstr.c */ #include <stdio.h> int main(void) { printf( int), dened in stdio.h, when its input is terminated.
"<%2s>\n, Barney must die!" ); printf( "<%22s>\n, The putchar()" and getchar()" functions handle single
Barney must die!" ); printf( "<%22.5s>\n, Barney
character I/O. For example, the following program ac-
must die!" ); printf( "<%22.5s>\n, Barney must die!" cepts characters from standard input one at a time:
); return 0; }
/* inout.c */ #include <stdio.h> int main (void) { un-
-- prints: signed int ch; while ((ch = getchar()) != EOF) { putchar(
<Barney must die!> < Barney must die!> < Barne> ch ); } return 0; }
<Barne > The getchar function returns an int and also termi-
Just for convenience, the table of special characters listed nates with an EOF. Notice the neat way C allows a pro-
in chapter 2 is repeated here. These characters can be gram to get a value and then test it in the same expression,
embedded in printf strings: a particularly useful feature for handling loops.
'\a' alarm (beep) character '\p' backspace '\f' formfeed '\n' One word of warning on single-character I/O: if a pro-
newline '\r' carriage return '\t' horizontal tab '\v' vertical gram is reading characters from the keyboard, most op-
tab '\\' backslash '\?' question mark '\'' single quote '\"' erating systems won't send the characters to the program
double quote '\0NN' character code in octal '\xNN' char- until the user presses the Enter key, meaning its not
acter code in hex '\0' null character possible to perform single-character keyboard I/O this
The scanf()" function reads formatted data using a syn- way.
tax similar to that of printf, except that it requires point- The little program above is the essential core of a
ers as parameters, since it has to return values. For ex- character-mode text lter, a program that can perform
ample: some transformation between standard input and stan-
dard output. Such a lter can be used as an element to
/* cscanf.c */ #include <stdio.h> int main(void) { int val;
char name[256]; printf( Enter your age and name.\n ); construct more sophisticated applications:
scanf( "%d %s, &val, name ); printf( Your name is: %s type le.txt > lter1 | lter2 > outle.txt
-- and your age is: %d\n, name, val ); return 0; } The following lter capitalizes the rst character in each
There is no "&" in front of name since the name of a word in the input. The program operates as a state ma-
string is already a pointer. Input elds are separated by chine, using a variable that can be set to dierent val-
whitespace (space, tab, or newline), though a count, for ues, or states, to control its operating mode. It has two
example "%10d, can be included to dene a specic eld states: SEEK, in which it is looking for the rst charac-
width. Formatting codes are the same as for printf()", ter, and REPLACE, in which it is looking for the end of
except: a word.
In SEEK state, it scans through whitespace (space, tab, or
There is no "%g format code. newline), echoing characters. If it nds a printing char-
acter, it converts it to uppercase and goes to REPLACE
The "%f and "%e format codes work the same. state. In REPLACE state, it converts characters to lower-
case until it hits whitespace, and then goes back to SEEK
state.
There is a "%h format code for reading short inte-
gers. The program uses the tolower()" and toupper()" func-
tions to make case conversions. These two functions will
If characters are included in the format code, scanf()" be discussed in the next chapter.
will read in the characters and discard them. For example, /* caps.c */ #include <stdio.h> #include <ctype.h> #de-
if the example above were modied as follows: ne SEEK 0 #dene REPLACE 1 int main(void) { int ch,
scanf( "%d,%s, &val, name ); state = SEEK; while(( ch = getchar() ) != EOF ) { switch(
state ) { case REPLACE: switch( ch ) { case ' ': case '\t':
-- then scanf()" will assume that the two input values
3.2. C FILE-IO THROUGH LIBRARY FUNCTIONS 17
case '\n': state = SEEK; break; default: ch = tolower( ch Rename a le. remove() Delete a le. fprintf() Format-
); break; } break; case SEEK: switch( ch ) { case ' ': case ted write. fscanf() Formatted read. fwrite() Unformatted
'\t': case '\n': break; default: ch = toupper( ch ); state =write. fread() Unformatted read. putc() Write a single
REPLACE; break; } } putchar( ch ); } return 0; } byte to a le. getc() Read a single byte from a le. fputs()
The puts()" function is like a simplied version of Write a string to a le. fgets() Read a string from a le.
printf()" without format codes. It prints a string that is All these library functions depend on denitions made in
automatically terminated with a newline: the stdio.h header le, and so require the declaration:
puts( Hello world!" ); #include <stdio.h>
The fgets()" function is particularly useful: it allows C documentation normally refers to these functions as
reads a line of text terminated by a newline. It is much performing stream I/O, not le I/O. The distinction is
less nicky about its inputs than scanf()": that they could just as well handle data being transferred
/* cgets.c */ #include <stdio.h> #include <string.h> #in- through a modem as a le, and so the more general term
clude <stdlib.h> int main(void) { char word[256], *guess data stream is used rather than le. However, we'll
stay with the le terminology in this document for the
= blue\n"; int i, n = 0; puts( Guess a color (use lower
case please):" ); while( fgets(word, 256, stdin) != NULL sake of simplicity.
) { if( strcmp( word, guess ) == 0 ) { puts( You win!" ); The fopen()" function opens and, if need be, creates a
break; } else { puts( No, try again. ); } } return 0; } le. Its syntax is:
This program includes the strcmp function, which per- <le pointer> = fopen( <lename>, <access mode> );
forms string comparisons and returns 0 on a match. This The fopen()" function returns a le pointer, declared
function is described in more detail in the next chapter. as follows:
These functions can be used to implement lters that op-
FILE *<le pointer>;
erate on lines of text, instead of characters. A core pro-
gram for such lters follows: The le pointer will be returned with the value NULL, de-
ned in stdio.h, if there is an error. The access modes
/* llter.c */ #include <stdio.h> int main (void) { char are dened as follows:
b[256]; while (( gets( b ) ) != NULL ) { puts( b ); } return
0; } r Open for reading. w Open and wipe (or create) for writ-
ing. a Append -- open (or create) to write to end of le. r+
The fgets()" function returns NULL, dened in Open a le for reading and writing. w+ Open and wipe
stdio.h, on input termination or error. (or create) for reading and writing. a+ Open a le for
The Windows-based console-I/O functions getch()" and reading and appending.
getche()" operate much as getchar()" does, except that
The lename is simply a string of characters.
getche()" echoes the character automatically.
It is often useful to use the same statements to communi-
The kbhit()" function is very dierent in that it only indi- cate either with les or with standard I/O. For this reason,
cates if a key has been pressed or not. It returns a nonzero the stdio.h header le includes predened le pointers
value if a key has been pressed, and zero if it hasn't. This with the names stdin and stdout. Theres no 't need
allows a program to poll the keyboard for input, instead of to do an fopen()" on them -- they can just be assigned to
hanging on keyboard input and waiting for something to a le pointer:
happen. As mentioned, these functions require the co-
nio.h header le, not the stdio.h header le. fpin = stdin; fpout = stdout;
-- and any following le-I/O functions won't know the dif-
ference.
3.2 C File-IO Through Library The fclose()" function simply closes the le given by its
Functions le pointer parameter. It has the syntax:
fclose( fp );
The fseek()" function call allows the byte location in a
le to be selected for reading or writing. It has the syntax:
The le-I/O library functions are much like the console-
I/O functions. In fact, most of the console-I/O functions fseek( <le_pointer>, <oset>, <origin> );
can be thought of as special cases of the le-I/O functions. The oset is a long and species the oset into the le,
The library functions include: in bytes. The origin is an int and is one of three stan-
fopen() Create or open a le for reading or writing. dard values, dened in stdio.h":
fclose() Close a le after reading or writing it. fseek() SEEK_SET Start of le. SEEK_CUR Current location.
Seek to a certain location in a le. rewind() Rewind a SEEK_END End of le.
le back to its beginning and leave it open. rename()
18 CHAPTER 3. C INPUT & OUTPUT
The fseek()" function returns 0 on success and non-zero The "%f and "%e format codes work the same.
on failure.
The rewind()", rename()", and remove()" functions There is a "%h format code for reading short inte-
are straightforward. The rewind()" function resets an gers.
open le back to its beginning. It has the syntax:
rewind( <le_pointer> ); Numeric modiers can be used, of course. The fscanf()"
function returns the number of items that it successfully
The rename()" function changes the name of a le: read, or the EOF code, an int, if it encounters the end
rename( <old_le_name_string>, of the le or an error.
<new_le_name_string> ); The following program demonstrates the use of
The remove()" function deletes a le: fprintf()" and fscanf()":
remove( <le_name_string> ) /* fprsc.c */ #include <stdio.h> void main() { int ctr, i[3],
n1 = 16, n2 = 256; oat f[4], n3 = 3.141592654f; FILE
The fprintf()" function allows formatted ASCII data out-
*fp; fp = fopen( data, w+" ); /* Write data in: dec-
put to a le, and has the syntax:
imal integer formats decimal, octal, hex integer formats
fprintf( <le pointer>, <string>, <variable list> ); oating-point formats */ fprintf( fp, "%d %10d %10d
The fprintf()" function is identical in syntax to printf()", \n, n1, n1, n1 ); fprintf( fp, "%d %o %x \n, n2, n2, n2
except for the addition of a le pointer parameter. For ); fprintf( fp, "%f %10.10f %e %5.4e \n, n3, n3, n3, n3
example, the fprintf()" call in this little program: ); /* Rewind le. */ rewind( fp ); /* Read back data. */
puts( "" ); fscanf( fp, "%d %d %d, &i[0], &i[1], &i[2] );
/* fprpi.c */ #include <stdio.h> void main() { int n1 = 16; printf( " %d\t%d\t%dn, i[0], i[1], i[2] ); fscanf( fp, "%d
oat n2 = 3.141592654f; FILE *fp; fp = fopen( data, %o %x, &i[0], &i[1], &i[2] ); printf( " %d\t%d\t%d\n,
w ); fprintf( fp, " %d %f, n1, n2 ); fclose( fp ); } i[0], i[1], i[2] ); fscanf( fp, "%f %f %f %f, &f[0],
-- stores the following ASCII data: &f[1], &f[2], &f[3] ); printf( " %f\t%f\t%f\t%f\n, f[0],
f[1], f[2], f[3] ); fclose( fp ); }
16 3.14159
The program generates the output:
The formatting codes are exactly the same as for
printf()": 16 16 16 256 256 256 3.141593 3.141593 3.141593
3.141600
%d decimal integer %ld long decimal integer %c charac-
ter %s string %e oating-point number in exponential no- The fwrite()" and fread()" functions are used for binary
tation %f oating-point number in decimal notation %g le I/O. The syntax of fwrite()" is as follows:
use %e and %f, whichever is shorter %u unsigned deci- fwrite( <array_pointer>, <element_size>, <count>,
mal integer %o unsigned octal integer %x unsigned hex <le_pointer> );
integer
The array pointer is of type void, and so the array can
Field-width speciers can be used as well. The fprintf()" be of any type. The element size and count, which give
function returns the number of characters it dumps to the the number of bytes in each array element and the number
le, or a negative number if it terminates with an error. of elements in the array, are of type size_t, which are
The fscanf()" function is to fprintf()" what scanf()" is equivalent to unsigned int.
to printf()": it reads ASCII-formatted data into a list of The fread()" function similarly has the syntax:
variables. It has the syntax:
fread( <array_pointer>, <element_size>, <count>,
fscanf( <le pointer>, <string>, <variable list> ); <le_pointer> );
However, the string contains only format codes, no text, The fread()" function returns the number of items it ac-
and the variable list contains the addresses of the vari- tually read.
ables, not the variables themselves. For example, the pro-
gram below reads back the two numbers that were stored The following program stores an array of data to a le and
with fprintf()" in the last example: then reads it back using fwrite()" and fread()":
/* frdata.c */ #include <stdio.h> void main() { int n1; oat /* fwrrd.c */ #include <stdio.h> #include <math.h> #de-
n2; FILE *fp; fp = fopen( data, r ); fscanf( fp, "%d ne SIZE 20 void main() { int n; oat d[SIZE]; FILE *fp;
%f, &n1, &n2 ); printf( "%d %f, n1, n2 ); fclose( fp ); for( n = 0; n < SIZE; ++n ) /* Fill array with roots. */ {
} d[n] = (oat)sqrt( (double)n ); } fp = fopen( data, w+"
); /* Open le. */ fwrite( d, sizeof( oat ), SIZE, fp ); /*
The fscanf()" function uses the same format codes as Write it to le. */ rewind( fp ); /* Rewind le. */ fread(
fprintf()", with the familiar exceptions: d, sizeof( oat ), SIZE, fp ); /* Read back data. */ for(
n = 0; n < SIZE; ++n ) /* Print array. */ { printf( "%d:
There is no "%g format code. %7.3f\n, n, d[n] ); } fclose( fp ); /* Close le. */ }
3.3. C FILE-IO THROUGH SYSTEM CALLS 19
instead wipe the contents of the le and return a le de- write( <le descriptor>, <buer>, <buer length> );
scriptor for it. The le descriptor is returned by a creat()" or open()"
For example, to create a le named data with read and system call. The buer is a pointer to a variable or an
write permission for everyone on the system would re- array that contains the data; and the buer length gives
quire the following statements: the number of bytes to be written into the le.
#dene RD_WR 0666 ... int fd; /Dene le descriptor. While dierent data types may have dierent byte lengths
*/ fd = creat( data, RD_WR ); on dierent systems, the sizeof()" statement can be used
The open()" system call opens an existing le for reading to provide the proper buer length in bytes. A write()"
or writing. It has the syntax: call could be specied as follows:
<le descriptor variable> = open( <lename>, <access oat array[10]; ... write( fd, array, sizeof( array ) );
mode> ); The write()" function returns the number of bytes it ac-
tually writes. It will return 1 on an error.
The open()" call is similar to the creat()" call in that it
returns a le descriptor for the given le, and returns a The read()" system call reads data from a open le. Its
le descriptor of 1 if it encounters an error. However, syntax is exactly the same as that of the write()" call:
the second parameter is an access mode, not a permis- read( <le descriptor>, <buer>, <buer length> );
sion code. There are three modes (dened in the fcntl.h
header le): The read()" function returns the number of bytes it actu-
ally returns. At the end of le it returns 0, or returns 1
O_RDONLY Open for reading only. O_WRONLY on error.
Open for writing only. O_RDWR Open for reading and
writing.
For example, to open data for writing, assuming that the
le had been created by another program, the following
statements would be used:
int fd; fd = open( data, O_WRONLY );
A few additional comments before proceeding:
21
22 CHAPTER 4. C LIBRARY FUNCTIONS & OTHER COMMENTS
main() { time_t *t; time( t ); puts( ctime( t )); } strcmp() Compare two strings. strchr() Find character
This program prints a string of the form: in string. strstr() Find string in string. strlwr() Convert
string to lowercase. strupr() Convert string to uppercase.
Tue Dec 27 15:18:16 1994
strlen()
4.2.1 further reading
The strlen()" function gives the length of a string, not
C Programming/Standard libraries including the NUL character at the end:
C Programming/C Reference /* strlen.c */ #include <stdio.h> #include <string.h> int
main() { char *t = XXX"; printf( Length of <%s> is
C++ Programming/Code/Standard C Library %d.\n, t, strlen( t )); }
This prints:
4.3 The C sprintf Function
Length of <XXX> is 3.
strcpy()
The sprintf function creates strings with formatted data.
Technically speaking, this is part of the standard-I/O li- The strcpy function copies one string from another. For
brary, and requires the declaration: example:
#include <stdio.h> /* strcpy.c */ #include <stdio.h> #include <string.h> int
main() { char s1[100], s2[100]; strcpy( s1, xxxxxx 1
However, it is really a string function and needs to be dis- ); strcpy( s2, zzzzzz 2 ); puts( Original strings: " );
cussed along with the other string functions. The syntax puts( "" ); puts( s1 ); puts( s2 ); puts( "" ); strcpy( s2, s1
of sprintf()" is exactly the same as it is for printf()", ex- ); puts( New strings: " ); puts( "" ); puts( s1 ); puts( s2 ); }
cept there is an extra parameter at the beginning, which
is a pointer to a string. Instead of outputting to standard This will print:
output, sprintf outputs to the string. For example:
Original strings: xxxxxx 1 zzzzzz 2 New strings: xxxxxx
/* csprntf.c */ #include <stdio.h> int main() { char 1 xxxxxx 1
b[100]; int i = 42; oat f = 1.1234f; sprintf( b, Format-
ted data: %d / %f, i, f ); puts( b ); } Please be aware of two features of this program:
moved to A Little C Primer/C String Function Library These comments are applicable to most of the other string
functions.
New strings: xxxxxx 1 xxxxxz 2 A stricmp()" function that ignores case in compar-
Notice that the parameter n is declared size_t, which isons.
is dened in string.h. Because strncpy does not add '\0'
after coping 5 charecters strnicmp()
output le.\n ); exit( 0 ); } break; default: /* Too many In more sophisticated programs, this leads to trouble.
parameters. */ puts( Wrong parameters.\n ); exit( 0 ); There may be no way of knowing how big an array needs
} /* Copy one le to the next. */ while( ( fgets( b, MAX, to be for the specic task the program is performing, and
src ) ) != NULL ) { fputs( b, dst ); } /* All done, close up so allocating an array in a xed size will either result in
shop. */ fclose( src ); fclose( dst ); } wasted memory or in not having enough to do the job.
The answer to this problem is to have the program allocate
the memory at runtime, and thats what the malloc()"
4.7 Pointers to C Functions library function does. For example, lets use malloc()"
to allocate an array of char":
/*malloc.c */ #include <stdio.h> #include <stdlib.h>
/*For malloc, exit functions. */ int main() { char *p;
This document has explained how to declare pointers to /*Pointer to array. */ unsigned count; /*Size of array. */
variables, arrays, and structures in C. It is also possible to puts( Size of array?" ); scanf( "%d, &count ); /*Get
dene pointers to functions. This feature allows functions size in bytes. */ p = malloc( (size_t)count ); /*Allocate
to be passed as arguments to other functions. This is use- array. */ if( p == NULL ) /*Check for failure. */ { puts(
ful for, say, building a function that determines solutions Can't allocate memory!" ); exit( 0 ); } puts( Allocated
to a range of math functions. array!" ); free( p ); /*Release memory. */ return 0; }
The syntax for declaring pointers to functions is obscure, The header le stdlib.h must be included, and a pointer
and so lets start with an idiot example: declaring a pointer to the memory block to be allocated must be declared.
to the standard library function printf()": The malloc()" function sets the pointer to the allocated
memory block with:
/* ptrprt.c */ #include <stdio.h> void main() { int
(*func_ptr) (); /* Declare the pointer. */ func_ptr = p = malloc( (size_t)count );
printf; /* Assign it a function. */ (*func_ptr) ( Printf is The count is in bytes and it is cast to the type of size_t,
here!\n ); /* Execute the function. */ } which is dened in stdlib.h. The pointer returned by
malloc()" is assigned to the variable p, which is of type
The function pointer has to be declared as the same type char *". In ANSI C, malloc()" returns a pointer of type
(int in this case) as the function it represents. void *", which can be assigned to any other pointer type
without a cast.
Next, lets pass function pointers to another function.
This function will assume the functions passed to it are If the malloc()" fails because it can't allocate the mem-
math functions that accept double and return double val- ory, it returns the value NULL (as dened in stdio.h).
ues: It is simple to allocate other data types:
/* ptrroot.c */ #include <stdio.h> #include <math.h> int *buf; ... buf = malloc( (size_t)sizeof( int ) );
void testfunc ( char *name, double (*func_ptr) () ); void
main() { testfunc( square root, sqrt ); } void testfunc The sizeof()" function is used to determine the number
( char *name, double (*func_ptr) () ) { double x, xinc; of bytes in the int data type.
int c; printf( Testing function %s:\n\n, name ); for( When the programs nished using the memory block, it
c=0; c < 20; ++c ) { printf( "%d: %f\n, c,(*func_ptr)( get rids of it using the free function:
(double)c )); } }
free( p );
C also contains two other memory-allocation functions
It is obvious that not all functions can be passed to test-
closely related to malloc()": the calloc()" function,
func()". The function being passed must agree with the
which performs the same function as malloc()" but al-
expected number and type of parameters, as well as with
lows the block allocated to be specied in terms of num-
the value returned.
ber of elements:
void *calloc( size_t <number_elements>, size_t
<sizeof_element_type> );
4.8 C Dynamic Memory Allocation
-- and the realloc()" function, which reallocates the size
& Deallocation of an array thats already been allocated:
void *realloc( void *<block_pointer>, size_t
<size_in_bytes> );
4.9 Common Programming Prob- This means that a string will always be one character big-
ger than the text it stores. It can also cause trouble if a
lems in C string is being created on a character-by-character basis,
and the program doesn't tack the null character onto the
end of it.
There are a number of common programming pitfalls in 9: Failing to allocate enough memory for a string -- or, if
C that even trap experienced programmers: pointers are declared, to allocate any memory for it at all.
1: Confusing "=" (assignment operator) with "==" 10: Failing to check return values from library functions.
(equality operator). For example: Most library functions return an error code; while it may
not be desirable to check every invocation of printf()",
if ( x = 1 ){ /* wrong! */ } ... if ( x == 1 ){ /* good. */ } be careful not to ignore error codes in critical operations.
and Of course, forgetting to store the value returned by a func-
for ( x == 1; ... /* wrong! */ ... for ( x = 1; ... /* good. */ tion when thats the only way to get the value out of it is a
bonehead move, but people do things like that every now
2: Confusing precedence of operations in expressions.
and then.
When in doubt, use parentheses to enforce precedence.
It never hurts to use a few extra parenthesis. 11: Having duplicate library-function names. The com-
piler will not always catch such bugs.
3: Confusing structure-member operators. If struct_val
is a structure and struct_ptr is a pointer to a structure, 12: Forgetting to specify header les for library functions.
then: 13: Specifying variables as parameters to functions when
struct_val->myname pointers are supposed to be specied, and the reverse.
If the function returns a value through a parameter, that
-- is wrong and so is:
means it must be specied as a pointer:
struct_ptr.myname
myfunc( &myvar );
4: Using incorrect formatting codes for printf()" and
The following will not do the job:
scanf()". Using a "%f to print an int, for example,
can lead to bizarre output. myfunc( myvar );
5: Remember that the actual base index of an array is 0, Remember that a function may require a pointer as a pa-
and the nal index is 1 less than the declared size. For rameter even if it doesn't return a value, though as a rule
example: this is not a good programming practice.
int data[20]; ... for ( x = 1; x <= 20; ++x ) { printf( 14: Getting mixed up when using nested if and else
"%d\n, data[x] ); } statements. The best way to avoid problems with this is
to always use brackets. Avoiding complicated if con-
-- will give invalid results when x is 20. Since C does
structs is also a good idea; use switch if theres any
not do bounds checking, this one might be hard to catch.
choice in the matter. Using switch is also useful even
6: Muddling syntax for multidimensional arrays. If: for simple if statements, since it makes it easier to ex-
data[10][10] pand the construct if that is necessary.
-- is a two-dimensional array, then: 15: Forgetting semicolons -- though the compiler usually
catches this -- or adding one where it isn't supposed to be
data[2][7] -- which it usually doesn't. For example:
-- will select an element in that array. However: for( x = 1; x < 10; ++x ); { printf( "%d\n, x ) }
data[ 2, 7 ] -- never prints anything.
-- will give invalid results but not be agged as an error 16: Forgetting break statements in switch constructs.
by C. As commented earlier, doing so will simply cause execu-
7: Confusing strings and character constants. The follow- tion to ow from one clause of the switch to the next.
ing is a string: 17: Careless mixing and misuse of signed and unsigned
Y values, or of dierent data types. This can lead to some
insanely subtle bugs. One particular problem to watch
-- as opposed to the character constant: out for is declaring single character variables as unsigned
'Y' char. Many I/O functions will expect values of un-
signed int and fail to properly ag EOF. It is recom-
This can cause troubles in comparisons of strings against
mended to cast function arguments to the proper type
character constants.
even if it appears that type conversion will take care of
8: Forgetting that strings end in a null character ('\0').
4.9. COMMON PROGRAMMING PROBLEMS IN C 27
it on its own.
18: Confusion of variable names. It is recommended that
such identiers be unique in the rst 6 characters to ensure
portability of code.
19: In general, excessively tricky and clever code. Pro-
grams are nasty beasts and even if it works, it will have
to be modied and even ported to dierent languages.
Maintain a clean structure and do the simple straightfor-
ward thing, unless it imposes an unacceptable penalty.
Chapter 5
C Quick Reference
28
5.1. C QUICK REFERENCE 29
30
Chapter 7
7.1 Resources
7.2 Licensing
7.2.1 Licensing
The original text of this book was released into the public
domain by its author, Greg Goebel. The text of the book
is available at http://www.vectorsite.net/tscpp.html, and
that original version is still available in the public domain.
The version here at Wikibooks is released under the fol-
lowing license:
31
Chapter 8
8.1 Text
Wikibooks:Collections Preface Source: https://en.wikibooks.org/wiki/Wikibooks%3ACollections_Preface?oldid=2842060 Contribu-
tors: RobinH, Whiteknight, Jomegat, Mike.lifeguard, Martin Kraus, Adrignola, Magesha and MadKaw
A Little C Primer/An Introductory C Program Source: https://en.wikibooks.org/wiki/A_Little_C_Primer/An_Introductory_C_
Program?oldid=3021468 Contributors: Whiteknight, Sv1xv, Kd7nyq~enwikibooks, Makoryu, Fishpi, ReubenGarrett, Jfmantis, Zhaofeng
Li, AllenZh, GiovanniES and Anonymous: 8
A Little C Primer/C Functions in Detail Source: https://en.wikibooks.org/wiki/A_Little_C_Primer/C_Functions_in_Detail?oldid=
2551325 Contributors: Whiteknight, Makoryu, Carlos RH Ruiz, Ebaychatter0, S Rifqi and Anonymous: 1
A Little C Primer/C Control Constructs Source: https://en.wikibooks.org/wiki/A_Little_C_Primer/C_Control_Constructs?oldid=
3021484 Contributors: Whiteknight, Makoryu, VeganaiZe and Anonymous: 2
A Little C Primer/C Variables, Declarations and Constants Source: https://en.wikibooks.org/wiki/A_Little_C_Primer/C_Variables%
2C_Declarations_and_Constants?oldid=2511735 Contributors: Whiteknight, Thenub314, Rbonvall, Fauxmight, 4crickj, RomanSaveljev
and Anonymous: 4
A Little C Primer/C Operators Source: https://en.wikibooks.org/wiki/A_Little_C_Primer/C_Operators?oldid=2438195 Contributors:
Whiteknight, Fauxmight, HethrirBot and Fender0107401
A Little C Primer/C Preprocessor Directives Source: https://en.wikibooks.org/wiki/A_Little_C_Primer/C_Preprocessor_Directives?
oldid=2616802 Contributors: Whiteknight, Fishpi, RayOctopus and Anonymous: 1
A Little C Primer/C Console IO Source: https://en.wikibooks.org/wiki/A_Little_C_Primer/C_Console_IO?oldid=2239015 Contribu-
tors: Whiteknight, Fauxmight, Jfmantis and Anonymous: 1
A Little C Primer/C File-IO Through Library Functions Source: https://en.wikibooks.org/wiki/A_Little_C_Primer/C_File-IO_
Through_Library_Functions?oldid=1548907 Contributors: Whiteknight and Fauxmight
A Little C Primer/C File-IO Through System Calls Source: https://en.wikibooks.org/wiki/A_Little_C_Primer/C_File-IO_Through_
System_Calls?oldid=2840170 Contributors: Whiteknight, QuiteUnusual and Anonymous: 4
A Little C Primer/C Math Library Source: https://en.wikibooks.org/wiki/A_Little_C_Primer/C_Math_Library?oldid=2239018 Con-
tributors: Whiteknight and Jfmantis
A Little C Primer/C Standard Utility Library & Time Library Source: https://en.wikibooks.org/wiki/A_Little_C_Primer/C_
Standard_Utility_Library_%26_Time_Library?oldid=2321402 Contributors: DavidCary, Whiteknight and Fauxmight
A Little C Primer/The C sprintf Function Source: https://en.wikibooks.org/wiki/A_Little_C_Primer/The_C_sprintf_Function?oldid=
2410276 Contributors: DavidCary, Whiteknight, Sigma 7, Ebaychatter0 and Anonymous: 2
A Little C Primer/C String Function Library Source: https://en.wikibooks.org/wiki/A_Little_C_Primer/C_String_Function_Library?
oldid=2459794 Contributors: DavidCary, Whiteknight, Denispir, Sigma 7, Mabdul, Fauxmight, Ebaychatter0 and Anonymous: 5
A Little C Primer/C Character Class Test Library Source: https://en.wikibooks.org/wiki/A_Little_C_Primer/C_Character_Class_
Test_Library?oldid=1274974 Contributors: Whiteknight
A Little C Primer/C Command Line Arguments Source: https://en.wikibooks.org/wiki/A_Little_C_Primer/C_Command_Line_
Arguments?oldid=1551853 Contributors: Whiteknight, Fauxmight and Anonymous: 2
A Little C Primer/Pointers to C Functions Source: https://en.wikibooks.org/wiki/A_Little_C_Primer/Pointers_to_C_Functions?oldid=
2301673 Contributors: Whiteknight, , Jfmantis and Anonymous: 2
A Little C Primer/C Dynamic Memory Allocation & Deallocation Source: https://en.wikibooks.org/wiki/A_Little_C_Primer/C_
Dynamic_Memory_Allocation_%26_Deallocation?oldid=2421314 Contributors: Whiteknight, Sergioller and Anonymous: 2
A Little C Primer/Common Programming Problems in C Source: https://en.wikibooks.org/wiki/A_Little_C_Primer/Common_
Programming_Problems_in_C?oldid=2364113 Contributors: DavidCary, Whiteknight, Avicennasis, Reyk and Anonymous: 1
32
8.2. IMAGES 33
8.2 Images
File:Heckert_GNU_white.svg Source: https://upload.wikimedia.org/wikipedia/commons/2/22/Heckert_GNU_white.svg License: CC
BY-SA 2.0 Contributors: gnu.org Original artist: Aurelio A. Heckert <aurium@gmail.com>
File:Wikibooks-logo-en-noslogan.svg Source: https://upload.wikimedia.org/wikipedia/commons/d/df/Wikibooks-logo-en-noslogan.
svg License: CC BY-SA 3.0 Contributors: Own work Original artist: User:Bastique, User:Ramac et al.