You are on page 1of 12

C Programming :: Introduction

• C is a simple programming language with few keywords and a relatively simple to


understand syntax.
• C is also useless. C itself has no input/output commands, doesn't have support f
or strings as a fundamental (atomic) data type. No useful math functions built i
n.
• Because C is useless by itself, it requires the use of libraries. This increases
the complexity of C. The issue of standard libraries is resolved through the us
e of ANSI libraries and other methods.
C Programming :: Hello, World
Let's give a go at a very simple program that prints out "Hello World" to standa
rd out (usually your monitor). We'll call our little program hello.c.
#include <stdio.h>
main() {
printf("Hello, world!\n");
return 0;
}
What's all this junk just to print out Hello, World? Let's see what's happening:
• #include <stdio.h> - Tells the compiler to include this header file for compilat
ion.
o What is a header file? They contain prototypes and other compiler/pre-pr
ocessor directives. Prototypes are basic abstract function definitions. More on
these later...
o Some common header files are stdio.h, stdlib.h, unistd.h, math.h.
• main() - This is a function, in particular the main block.
• { } - These curly braces are equivalent to stating "block begin" and "block end"
. These can be used at many places, such as if and switch.
• printf() - Ah... the actual print statement. Thankfully we have the header file
stdio.h! But what does it do? How is it defined?
• return 0 - What's this? Who knows!
Seems like trying to figure all this out is just way too confusing. Let's break
things up one at at time:
• The return 0 statement. Seems like we are trying to give something back, and it
is an integer. Maybe if we modified our main function definition: int main() Ok,
now we are saying that our main function will be returning an integer! So remem
ber, you should always explicitly declare the return type on the function!
• Something is still a little fishy... I remember that 0 implied false... so isn't
it returning that an int signifying a bad result? Thankfully there is a simple
solution to this. Let's add #include <stdlib.h> to our includes. Let's change ou
r return statement to return EXIT_SUCCESS;. Now it makes sense!
• Let's take a look at printf. Hmm... I wonder what the prototype for printf is. U
tilizing the man pages we see that printf is: int printf(const char *format, ...
); printf returns an int. The man pages say that printf returns the number of ch
aracters printed. Now you wonder, who cares? Why should you care about this? It
is good programming practice to ALWAYS check for return values. It will not only
make your program more readable, but in the end it will make your programs less
error prone. But in this particular case, we don't really need it. So we cast t
he function's return to (void). fprintf, fflush, and exit are the only functions
where you should do this. More on this later when we get to I/O. For now, let's
just void the return value.
• What about documentation? We should probably doc some of our code so that other
people can understand what we are doing. Comments in the C89 standard are noted
by: /* */. The comment begins with /* and ends with */.
Let's see our new improved code!
#include <stdio.h>
#include <stdlib.h>
/* Main Function
* Purpose: Controls program, prints Hello, World!
* Input: None
* Output: Returns Exit Status
*/
int main(int argc, char **argv) {
printf("Hello, world!\n");
return EXIT_SUCCESS;
}
Much better! The KEY POINT of this whole introduction is to show you the fundame
ntal difference between correctness and understandability. Both sample codes pro
duce the exact same output in "Hello, world!" However, only the latter example s
hows better readability in the code leading to code that is understandable. All
codes will have bugs. If you sacrifice code readability with reduced (or no) com
ments and cryptic lines, the burden is shifted and magnified when your code need
s to be maintained.
Document what you can. Complex data types, function calls that may not be obviou
s, etc. Good documentation goes a long way!
C Programming :: A Glimpse at Compilation
You will most likely be using the GNU C compiler, gcc in a Linux or *nix-like en
vironment. Most environments will have the cc variable set which is much more su
itable when using Makefiles.
A superficial look at compiling would entail: gcc file.c But is this what you re
ally want? We get a file called a.out in our directory. This is because you aren
't specifying the correct compiler options. Taking the theory of "that which is
learned first is learned best", compile your programs using the "anal retentive"
flags. This will not only catch more errors at compile time, but will help you
create a program with less errors at run time. The recommended options to includ
e while compiling are:
• -ansi: Use ANSI C
• -pedantic: Issue all warnings according to strict ANSI standard C
• -Wall: Turn on all Warnings. ALWAYS use this. You will benefit from it.
• -O2: Optimization Level 2, nearly all supported optimizations that do not involv
e a space-speed tradeoff are performed. Increases compilation time, but produces
performance-enhanced code.
• -o: Specify output file to be created
So here is an example command line:
gcc -ansi -pedantic -Wall -O2 -o hello hello.c
This produces an executable called hello in the directory where hello.c resides.

C Programming :: Introduction Summary


1. Program with understandability in mind. Speed in writing the program may
pay off now, but writing a program with readability and understandability will
pay off in the future.
2. Take advantage of the compiler's smarts! Use the "anal retentive" option
s!
3. Did I mention that you should make your programs well documented and und
erstandable? :)
4. Note: From here on out, major topics will be highlighted in blue and sub
topics will be highlighted in black.
Basic C: Operators, Types, Storage Classes
In the introduction, we discussed very simple C, now it is time for us to move a
head and explore the basics of C programming. If you do not understand the conce
pts explained in the Introduction, do not proceed. Make sure you understand comp
letely the topics covered in the introduction before you dive into C.
Operations :: Relational Operators
• You probably are familiar with < and the > relational operators from mathematics
. The same principles apply in C when you are comparing two objects.
• There are six possibilities in C: <, <=, >, >=, !=, and ==. The first four a sel
f-explanatory, the != stands for "not equals to" and == is "equivalent to".
• Here we can point out the difference between syntax and semantics. a = b is diff
erent from a == b. Most C compilers will allow both statements to be used in con
ditionals like if, but they have two completely different meanings. Make sure yo
ur assignment operators are where you want them to be and your relationals where
you want relational comparisons!
Operations :: Logical Operators
• Logical operators simulate boolean algebra in C.
• A sampling of Logical/Boolean Operators: &&, ||, &, |, and ^.
• For example, && is used to compare two objects with AND: x != 0 && y != 0
• Expressions involving logical operators undergo Short-Circuit Evaluation. Take t
he above example into consideration. If x != 0 evaluates to false, the whole sta
tement is false regardless of the outcome of y != 0. This can be a good thing or
a bad thing depending on the context. (See Weiss pg. 51-52).
Operations :: Arithmetic Operators
• There are other operators available. The two arithemetic operators that are used
frequently are ++ and --. You can place these in front or on the back of variab
les. ++ specifies increment, the -- specifies decrement. If the operator is plac
ed in front, it is prefix if it is placed behind, it is postfix. Prefix means, i
ncrement before any operations are performed, postfix is increment afterwards. T
hese are important considerations when using these operators.
• C allows *= += /= -= operators. For example:
• int i = 5;

• i *= 5;
The int i would have the value 25 after the operation.
• For a full listing of operators, reference Weiss pg. 51.
Basic C :: Conditionals
• if used with the above relational and logical operators allows for conditional s
tatements. You can start blocks of code using { and }. if's can be coupled with
else keyword to handle alternative outcomes.
• The ? : operator can be a shorthand method for signifying (if expression) ? (eva
luate if true) : (else evaluate this). For example, you can use this in a return
statement or a printf statement for conciseness. Beware! This reduces the reada
bility of the program... see Introduction. This does not in any way speed up exe
cution time.
• The switch statement allows for quick if-else checking. For example, if you want
ed to determine what the char x was and have different outcomes for certain valu
es of x, you could simply switch x and run cases. Some sample code:
• switch ( x ) {
• case 'a': /* Do stuff when x is 'a' */
• break;
• case 'b':
• case 'c':
• case 'd': /* Fallthrough technique... cases b,c,d all use this code */
• break;
• default: /* Handle cases when x is not a,b,c or d. ALWAYS have a default */
• /* case!!! */
• break;
• }
Basic C :: Looping
• You can loop (jumping for those assembly junkies) through your code by using spe
cial loop keywords.
• These include while, for, and do while.
• The while loops until the expression specified is false. For example while (x <
4) will loop while x is less than 4.
• The syntax for for is different. Here's an example: for (i = 0; i < n; i++, z++)
. This code will loop until i is equal to n. The first argument specifies initia
lizing conditions, the second argument is like the while expression: continue th
e for loop until this expression no longer evaulates to true. The third argument
allows for adjustment of loop control variables or other variables. These state
ments can be null, e.g. for (; i < n; i++) does not specify initializing code.
• do while is like a "repeat-until" in Pascal. This is useful for loops that must
be executed at least once. Some sample code would be:
• do {
• /* do stuff */
• } while (statement);
Basic C :: Types, Type Qualifiers, Storage Classes
• int, char, float, double are the fundamental data types in C.
• Type modifiers include: short, long, unsigned, signed. Not all combinations of t
ypes and modifiers are availble.
• Type qualifiers include the keywords: const and volatile. The const qualifier pl
aces the assigned variable in the constant data area of memory which makes the p
articular variable unmodifiable (technically it still is though). volatile is us
ed less frequently and tells the compiler that this value can be modified outsid
e the control of the program.
• Storage classes include: auto, extern, register, static.
• The auto keyword places the specified variable into the stack area of memory. Th
is is usually implicit in most variable declarations, e.g. int i;
• The extern keyword makes the specified variable access the variable of the same
name from some other file. This is very useful for sharing variables in modular
programs.
• The register keyword suggests to the compiler to place the particular variable i
n the fast register memory located directly on the CPU. Most compilers these day
s (like gcc) are so smart that suggesting registers could actually make your pro
gram slower.
• The static keyword is useful for extending the lifetime of a particular variable
. If you declare a static variable inside a function, the variable remains even
after the function call is long gone (the variable is placed in the alterable ar
ea of memory). The static keyword is overloaded. It is also used to declare vari
ables to be private to a certain file only when declared with global variables.
static can also be used with functions, making those functions visible only to t
he file itself.
• A string is NOT a type directly supported by C. You, therefore, cannot "assign"
stuff into strings. A string is defined by ANSI as an array (or collection) of c
haracters. We will go more in-depth with strings later...
Basic C, Operations, Types Review
• Relational and Logical operators are used to compare expressions.
• Conditonal statements allow for conditional execution of expressions using if, e
lse, switch.
• Loops allow you to repeatedly do things until a stopping point is reached.
• Types, Storage Classes, and Type Qualifiers are used to modify the particular ty
pe's scope and lifetime.
Functions and the C Preprocessor
Now that we have a understanding of the very basics of C, it is time now to turn
our focus over to making our programs not only run correctly but more efficient
ly and are more understandable.
Functions :: The Basics
• Why should we make functions in our programs when we can just do it all under ma
in? Weiss (pg. 77) has a very good analogy that I'll borrow :) Think for a minut
e about high-end stereo systems. These stereo systems do not come in an all-in-o
ne package, but rather come in separate components: pre-amplifier, amplifier, eq
ualizer, receiver, cd player, tape deck, and speakers. The same concept applies
to programming. Your programs become modularized and much more readable if they
are broken down into components.
• This type of programming is known as top-down programming, because we first anal
yze what needs to be broken down into components. Functions allow us to create t
op-down modular programs.
• Each function consists of a name, a return type, and a possible parameter list.
This abstract definition of a function is known as it's interface. Here are some
sample function interfaces:
• char *strdup(char *s)
• int add_two_ints(int x, int y)
• void useless(void)
The first function header takes in a pointer to a string and outputs a char poin
ter. The second header takes in two integers and returns an int. The last header
doesn't return anything nor take in parameters.
• Some programmers like to separate returns from their function names to facilitat
e easier readability and searchability. This is just a matter of taste. For exam
ple:
• int
• add_two_ints(int x, int y)
• A function can return a single value to its caller in a statement using the keyw
ord return. The return value must be the same type as the return type specified
in the function's interface.
Functions :: Prototypes
• In the introduction, we touched on function prototypes. To recap, what are funct
ion prototypes? Function prototypes are abstract function interfaces. These func
tion declarations have no bodies; they just have their interfaces.
• Function prototypes are usually declared at the top of a C source file, or in a
separate header file (see Appendix: Creating Libraries).
• For example, if you wanted to grab command line parameters for your program, you
would most likely use the function getopt. But since this function is not part
of ANSI C, you must declare the function prototype, or you will get implicit dec
laration warnings when compiling with our flags. So you can simply prototype get
opt(3) from the man pages:
• /* This section of our program is for Function Prototypes */
• int getopt(int argc, char * const argv[], const char *optstring);
• extern char *optarg;
• extern int optind, opterr, optopt;
So if we declared this function prototype in our program, we would be telling th
e compiler explicitly what getopt returns and it's parameter list. What are thos
e extern variables? Recall that extern creates a reference to variables across f
iles, or in other words, it creates file global scope for those variables in tha
t particular C source file. That way we can access these variables that getopt m
odifies directly. More on getopt on the next section about Input/Output.
Functions :: Functions as Parameters
• This is a little more advanced section on functions, but is very useful. Take th
is for example:
• int applyeqn(int F(int), int max, int min) {
• int itmp;

• itmp = F(int) + min;
• itmp = itmp - max;
• return itmp;
• }
What does this function do if we call it with applyeqn(square(x), y, z);? What h
appens is that the int F(int) is a reference to the function that is passed in a
s a parameter. Thus inside applyeqn where there is a call to F, it actually is a
call to square! This is very useful if we have one set function, but wish to va
ry the input according to a particular function. So if we had a different functi
on called cube we could change how we call applyeqn by calling the function by a
pplyeqn(cube(x), y, z);.
Functions :: The Problem
• So now you must be thinking... Wow! Functions are great! I can do anything with
functions! WRONG. There are four major ways that parameters are passed into func
tions. The two that we should be concerned with are Pass by Value and Pass by Re
ference. In C, all parameters are passed by value.
• So you're saying so what? It makes a big difference. In simplistic terms, functi
ons in C create copies of the passed in variables. These variables remain on the
stack for the lifetime of the function and then are discarded, so they do not a
ffect the inputs! This is important. Let's repeat it again. Passed in arguments
will remain unchanged. Let's use this swapping function as an example:
• void swap(int x, int y) {
• int tmp = 0;

• tmp = x;
• x = y;
• y = tmp;
• }
If you were to simply pass in parameters to this swapping function that swaps tw
o integers, this would fail horribly. You'll just get the same values back.
• But thankfully, you can circumvent this pass by value limitation in C by simulat
ing pass by reference. Pass by reference changes the values that are passed in w
hen the function exits. This isn't how C works technically but can be thought of
in the same fashion. So how do you avoid pass by value side effects? By using p
ointers and in some cases using macros. We will discuss pointers in detail later
.
The C Preprocessor :: Overview
• The C Preprocessor is not part of the compiler, but is a separate step in the co
mpilation process. In simplistic terms, a C Preprocessor is just a text substitu
tion tool. We'll refer to the C Preprocessor as the CPP.
• All preprocessor lines begin with #. This listing is from Weiss pg. 104. The unc
onditional directives are:
o #include - Inserts a particular header from another file
o #define - Defines a preprocessor macro
o #undef - Undefines a preprocessor macro
The conditional directives are:
o #ifdef - If this macro is defined
o #ifndef - If this macro is not defined
o #if - Test if a compile time condition is true
o #else - The alternative for #if
o #elif - #else an #if in one statement
o #endif - End preprocessor conditional
Other directives include:
o # - Stringization, replaces a macro parameter with a string constant
o ## - Token merge, creates a single token from two adjacent ones
• Some examples of the above:
• #define MAX_ARRAY_LENGTH 20
Tells the CPP to replace instances of MAX_ARRAY_LENGTH with 20. Use #define for
constants to increase readability. Notice the absence of the ;.
#include <stdio.h>
#include "mystring.h"
Tells the CPP to get stdio.h from System Libraries and add the text to this file
. The next line tells CPP to get mystring.h from the local directory and add the
text to the file. This is a difference you must take note of.
#undef MEANING_OF_LIFE
#define MEANING_OF_LIFE 42
Tells the CPP to undefine MEANING_OF_LIFE and define it for 42.
#ifndef IROCK
#define IROCK "You wish!"
#endif
Tells the CPP to define IROCK only if IROCK isn't defined already.
#ifdef DEBUG
/* Your debugging statements here */
#endif
Tells the CPP to do the following statements if DEBUG is defined. This is useful
if you pass the -DDEBUG flag to gcc. This will define DEBUG, so you can turn de
bugging on and off on the fly!
The C Preprocessor :: Parameterized Macros
• One of the powerful functions of the CPP is the ability to simulate functions us
ing parameterized macros. For example, we might have some code to square a numbe
r:
• int square(int x) {
• return x * x;
• }
We can instead rewrite this using a macro:
#define square(x) ((x) * (x))
A few things you should notice. First square(x) The left parentheses must "cuddl
e" with the macro identifier. The next thing that should catch your eye are the
parenthesis surrounding the x's. These are necessary... what if we used this mac
ro as square(1 + 1)? Imagine if the macro didn't have those parentheses? It woul
d become ( 1 + 1 * 1 + 1 ). Instead of our desired result of 4, we would get 3.
Thus the added parentheses will make the expression ( (1 + 1) * (1 + 1) ). This
is a fundamental difference between macros and functions. You don't have to worr
y about this with functions, but you must consider this when using macros.
• Remeber that pass by value vs. pass by reference issue earlier? I said that you
could go around this by using a macro. Here is swap in action when using a macro
:
• #define swap(x, y) { int tmp = x; x = y; y = tmp }
Now we have swapping code that works. Why does this work? It's because the CPP j
ust simply replaces text. Wherever swap is called, the CPP will replace the macr
o call with the defined text. We'll go into how we can do this with pointers lat
er.
Functions and C Preprocessor Review
• Functions allow for modular programming. You must remember that all parameters p
assed into function in C are passed by value!
• The C Preprocessor allows for macro definitions and other pre-compilation direct
ives. It is just a text substitution tool before the actual compilation begins.
Input/Output and File I/O
With most of the basics of C under our belts, lets focus now on grabbing Input a
nd directing Output. This is essential for many programs that might require comm
and line parameters, or standard input.
I/O :: printf(3)
• printf(3) is one of the most frequently used functions in C for output.
• The prototype for printf(3) is:
• int printf(const char *format, ...);
printf takes in a formatting string and the actual variables to print. An exampl
e of printf is:
int x = 5;
char str[] = "abc";
char c = 'z';
float pi = 3.14;
printf("\t%d %s %f %s %c\n", x, str, pi, "WOW", c);
The output of the above would be:
5 abc 3.140000 WOW z
Let's see what's happening. The \t line signifies an escape sequence, specifical
ly, a tab. Then the %d specifies a conversion specification as given by the vari
able x. The %s matches with the string and the %f matches with the float. The de
fault precision for %f is 6 places after the decimal point. %f works for both fl
oats and doubles. For long doubles, use %Lf. The %s matches with the "WOW", and
the %c tells printf to output the char c. The \n signifies a newline.
• For a listing of escape sequences, see Weiss pg. 183.
• You can format the output through the formatting line.. By modifying the convers
ion specification, you can change how the particular variable is placed in outpu
t. For example:
• printf("%10.4d", x);
Would print this:
0005
The . allows for precision. This can be applied to floats as well. The number 10
puts 0005 over 10 spaces so that the number 5 is on the tenth spacing. You can
also add + and - right after % to make the number explicitly output as +0005. No
te that this does not actually change the value of x. In other words, using %-10
.4d will not output -0005.
• %e is useful for outputting floats using scientific notation. %le for doubles an
d %Le for long doubles.
I/O :: scanf(3)
• scanf(3) is useful for grabbing things from input. Beware though, scanf isn't th
e greatest function that C has to offer. Some people brush off scanf as a broken
function that shouldn't be used often.
• The prototype for scanf is:
• int scanf( const char *format, ...);
Looks similar to printf, but doesn't completely behave like printf does. Take fo
r example:
scanf("%d", x);
You'd expect scanf to read in an int into x. But scanf requires that you specify
the address to where the int should be stored. Thus you specify the address-of
operator (more on this when we get to pointers). Therefore,
scanf("%d", &x);
will put an int into x correctly.
• Simple enough, eh? Think again. scanf's major "flaw" is it's inability to digest
incorrect input. If scanf is expecting an int and your standard in keeps giving
it a string, scanf will keep trying at the same location. If you looped scanf,
this would create an infinite loop. Take this example code:
• int x, args;

• for ( ; ; ) {
• printf("Enter an integer bub: ");
• if (( args = scanf("%d", &x)) == 0) {
• printf("Error: not an integer\n");
• continue;
• } else {
• if (args == 1)
• printf("Read in %d\n", x);
• else
• break;
• }
• }
The code above will fail. Why? It's because scanf isn't discarding bad input. So
instead of using just continue;, we have to add a line before it to digest inpu
t. We can use a function called digestline().
void digestline(void) {
scanf("%*[^\n]"); /* Skip to the End of the Line */
scanf("%*1[\n]"); /* Skip One Newline */
}
This function is taken from Weiss pg. 341. Using assignment suppression, we can
use * to suppress anything contained in the set [^\n]. This skips all characters
until the newline. The next scanf allows one newline character read. Thus we ca
n digest bad input!
• The section on scanf by Weiss is excellent. Section 12.2, pgs. 336-342.
File I/O :: fgets(3)
• One of the alternatives to scanf/fscanf is fgets. The prototype is:
• char *fgets(char *s, int size, FILE *stream);
fgets reads in size - 1 characters from the stream and stores it into *s pointer
. The string is automatically null-terminated.
• fgets stops reading in characters if it reaches an EOF or newline.
• Now that you've read characters of interest from a stream, what do you do with t
he string? Simple! Use sscanf, see below.
File I/O :: sscanf(3)
• To scan a string for a format, the sscanf library call is handy. It's prototype:
• int sscanf(const char *str, const char *format, ...);
sscanf works much like fscanf except it takes a character pointer instead of a f
ile pointer.
• Using the combination of fgets/sscanf instead of scanf/fscanf you can avoid the
"digestion" problem (or bug, depending on who you talk to :)
File I/O :: fprintf(3)
• It is sometimes useful also to output to different streams. fprintf(3) allows us
to do exactly that.
• The prototype for fprintf is:
• int fprintf(FILE *stream, const char *format, ...);
fprintf takes in a special pointer called a file pointer, signified by FILE *. I
t then accepts a formatting string and arguments. The only difference between fp
rintf and printf is that fprintf can redirect output to a particular stream. The
se streams can be stdout, stderr, or a file pointer. More on file pointers when
we get to fopen.
• An example:
• fprintf(stderr, "ERROR: Cannot malloc enough memory.\n");
This outputs the error message to standard error.
File I/O :: fscanf(3)
• fscanf(3) is basically a streams version of fscanf. The prototype for fscanf is:
• int fscanf( FILE *stream, const char *format, ...);
File I/O :: fflush(3)
• Sometimes it is necessary to forcefully flush a buffer to its stream. If a progr
am crashes, sometimes the stream isn't written. You can do this by using the ffl
ush(3) function. The prototype for fflush is:
• int fflush(FILE *stream);
Not very difficult to use, specify the stream to fflush.
File I/O :: fopen(3), fclose(3), and File Pointers
• fopen(3) is used to open streams. This is most often used with opening files for
input. fopen's prototype is:
• FILE *fopen (const char *path, const char *mode);
fopen returns a file pointer and takes in the path to the file as well as the mo
de to open the file with. Take for example:
FILE *Fp;
Fp = fopen("/home/johndoe/input.dat", "r");
This will open the file in /home/johndoe/input.dat for reading. Now you can use
fscanf commands with Fp. For example:
fscanf(Fp, "%d", &x);
This would read in an integer from the input.dat file. If we opened input.dat wi
th the mode "w", then we could write to it using fprintf:
fprintf(Fp, "%s\n", "File Streams are cool!");
• To close the stream, you would use fclose(3). The prototype for fclose is:
• int fclose( FILE *stream );
You would just give fclose the stream to close the stream. Remember to do this f
or all file streams, especially when writing to files!
I/O and File I/O :: Return Values
• We have been looking at I/O functions without any regard to their return values.
This is bad. Very, very bad. So to make our lives easier and to make our progra
ms behave well, let's write some macros!
• Let's write some wrapper macros for these functions. First let's create a meta-w
rapper function for all of our printf type functions:
• #define ERR_MSG( fn ) { (void)fflush(stderr); \
• (void)fprintf(stderr, __FILE__ ":%d:" #fn ": %s\n", \
• __LINE__, strerror(errno)); }
• #define METAPRINTF( fn, args, exp ) if( fn args exp ) ERR_MSG( fn )
This will create an ERR_MSG macro to handle error messages. The METAPRINTF is th
e meta-wrapper for our printf type functions. So let's define our printf type ma
cros:
#define PRINTF(args) METAPRINTF(printf, args, < 0)
#define FPRINTF(args) METAPRINTF(fprintf, args, < 0)
#define SCANF(args) METAPRINTF(scanf, args, < 0)
#define FSCANF(args) METAPRINTF(fscanf, args, < 0)
#define FFLUSH(args) METAPRINTF(fflush, args, < 0)
Now we have our wrapper functions. Because args is sent to METAPRINTF, we need t
wo sets of parentheses when we use the PRINTF macro. Examples on using the wrapp
er function:
PRINTF(("This is so cool!"));
FPRINTF((stderr, "Error bub!"));
Now you can this code into a common header file and be able to use these conveni
ent macros and still be able to check for return values! (Make sure you have inc
luded the string.h library)
• Note: We did not write macros for fopen and fclose. You must manually check for
return values on those functions.
Other I/O Functions
• There are many other Input/Output functions, such as fputs, getchar, putchar, un
getc. Refer to the man pages on these functions or in the Weiss text.
Command Line Arguments and Parameters :: getopt(3)
• I'm sure you've run the ls -l command before. ls -l *.c would display all c file
s with extended information. These parameters and arguments can be handled by yo
ur c program through getopt(3).
• We have already seen getopt, but now lets actually make some code that makes thi
s function useful. Let's see the prototype again:
• int getopt(int argc, char * const argv[], const char *optstring);
• extern char *optarg;
• extern int optind, opterr, optopt;
In order for us to utilize argc and argv, we must allow these as parameters on o
ur main() function:
int main(int argc, char **argv)
Now that we have everything set up, lets get this show on the road. Here's an ex
ample of using getopt:
int ich;
while ((ich = getopt (argc, argv, "ab:c")) != EOF) {
switch (ich) {
case 'a': /* Flags/Code when -a is specified */
break;
case 'b': /* Flags/Code when -b is specified */
/* The argument passed in with b is specified */
/* by optarg */
break;
case 'c': /* Flags/Code when -c is specified */
break;
default: /* Code when there are no parameters */
break;
}
}
if (optind < argc) {
printf ("non-option ARGV-elements: ");
while (optind < argc)
printf ("%s ", argv[optind++]);
printf ("\n");
}
This code might be a bit confusing if taken in all at once.
o The first step is to get getopt to pass an int into ich. The options all
owed are specified by the "ab:c". The colon following b allows b to have argumen
ts, e.g. -b gradient. Thus, optarg will contain the string "gradient".
o ich is then switched to check for the parameters.
o The ending conditional if (optind < argc) { checks for aguments passed i
n without an accompanying parameter. optind is the current index in the list of
arguments passed in in the argv-list. argc is the total number of arguments pass
ed in.
o So if we had a program called "junk" and we called it from the command p
rompt as ./junk -b gradient yeehaw the variables would look like:
o Variable Contains
o ------------------ ----------
o argc 4
o argv[0] "./junk"
o argv[1] "-b"
o argv[2] "gradient"
o argv[3] "yeehaw"
o optarg at case 'b' "gradient"
o optind after while 3
o getopt loop
Input/Output and File I/O Review
• printf and scanf can be used for Input and Output, while the "f versions" of the
se can be used to modify streams. Make sure you check the return values!
• You can grab command line arguments and parameters through getopt.

Variables, Types and Declarations


Storing data. Descriminating types. Declaring data.
A variable is a seqeuence of program code with a name (also called its identifie
r). A name or identifier in C can be anything from a single letter to a word. Th
e name of a variable must begin with an alphabetic letter or the underscore _ ch
aracter but the other characters in the name can be chosen from the following gr
oups:
a .. z
(any letter from a to z)
A .. Z
(any letter from A to Z)
0 .. 9
(any digit from 0 to 9)
_
(the underscore character)
Some examples of valid variable names are:
a total Out_of_Memory VAR integer etc...
In C variables do not only have names: they also have types. The type of a varia
ble conveys to the the compiler what sort of data will be stored in it. In BASIC
and in some older, largely obsolete languages, like PL/1, a special naming conv
ention is used to determine the sort of data which can be held in particular var
iables. e.g. the dollar symbol $ is commonly used in BASIC to mean that a variab
le is a string and the percentage % symbol is used to indicate an integer. No su
ch convention exists in C. Instead we specify the types of variables in their de
clarations. This serves two purposes:
• It gives a compiler precise information about the amount of memory that will hav
e to be given over to a variable when a program is finally run and what sort of
arithmetic will have to be used on it (e.g. integer only or floating point or no
ne).
• It provides the compiler with a list of the variables in a convenient place so t
hat it can cross check names and types for any errors.
There is a lot of different possible types in C. In fact it is possible for us t
o define our own, but there is no need to do this right away: there are some bas
ic types which are provided by C ready for use. The names of these types are all
reserved words in C and they are summarized as follows:
char
A single ASCII character
short
A short integer (usually 16-bits)
short int
A short integer
int
A standard integer (usually 32-bits)
long
A long integer
long int
A long integer (usually 32-bits, but increasingly 64 bits)
float
A floating point or real number (short)
long float
a long floating point number
double
A long floating point number
void
Discussed in a later chapter.
enum
Discussed in a later chapter.
volatile
Discussed in a later chapter.
There is some repetition in these words. In addition to the above, the word unsi
gned can also be placed in front of any of these types. Unsigned means that only
positive or zero values can be used. (i.e. there is no minus sign). The advanta
ge of using this kind of variable is that storing a minus sign takes up some mem
ory, so that if no minus sign is present, larger numbers can be stored in the sa
me kind of variable. The ANSI standard also allows the word signed to be placed
in front of any of these types, so indicate the opposite of unsigned. On some sy
stems variables are signed by default, whereas on others they are not.

You might also like