C IN HAND by KAPOOR
C IN HAND by KAPOOR
History of C
Characteristics of C
C Program Structure
Variables
o Defining Global Variables
o Printing Out and Inputting Variables
Constants
Arithmetic Operations
Comparison Operators
Logical Operators
Order of Precedence
Exercises
CHAPTER-2
The if statement
The ? operator
The switch statement
Exercises
CHAPTER-3
The for statement
The while statement
The do-while statement
break and continue
Exercises
CHAPTER-4
Single and Multi-dimensional Arrays
Strings
Exercises
CHAPTER-5
void functions
Functions and Arrays
Function Prototyping
Exercises
CHAPTER-6
Structures
o Defining New Data Types
Unions
Coercion or Type-Casting
Enumerated Types
Static Variables
Exercises
CHAPTER-7
What is a Pointer?
Pointer and Functions
Pointers and Arrays
Arrays of Pointers
Multidimensional arrays and pointers
Static Initialisation of Pointer Arrays
Pointers and Structures
Common Pointer Pitfalls
o Not assigning a pointer to memory address before
using it
o Illegal indirection
Exercise
CHAPTER-8
Malloc, Sizeof, and Free
Calloc and Realloc
Linked Lists
Full Program: queue.c
Exercises
CHAPTER-9
Bitwise Operators
Bit Fields
o Bit Fields: Practical Example
o A note of caution: Portability
Exercises
CHAPTER-10
#define
#undef
#include
#if -- Conditional inclusion
Preprocessor Compiler Control
Other Preprocessor Commands
Exercises
Introduction
C is sometimes referred to as a ``high-level assembly language.'' Some people think that's
an insult, but it's actually a deliberate and significant aspect of the language. If you have
programmed in assembly language, you'll probably find C very natural and comfortable
(although if you continue to focus too heavily on machine-level details, you'll probably
end up with unnecessarily non-portable programs). If you haven't programmed in
assembly language, you may be frustrated by C's lack of certain higher-level features. In
either case, you should understand why C was designed this way: so that seemingly-
simple constructions expressed in C would not expand to arbitrarily expensive (in time or
space) machine language constructions when compiled. If you write a C program simply
and succinctly, it is likely to result in a succinct, efficient machine language executable.
If you find that the executable program resulting from a C program is not efficient, it's
probably because of something silly you did, not because of something the compiler did
behind your back which you have no control over. In any case, there's no point in
complaining about C's low-level flavor: C is what it is.
A programming language is a tool, and no tool can perform every task unaided. If you're
building a house, and I'm teaching you how to use a hammer, and you ask how to
assemble rafters and trusses into gables, that's a legitimate question, but the answer has
fallen out of the realm of ``How do I use a hammer?'' and into ``How do I build a
house?''. In the same way, we'll see that C does not have built-in features to perform
every function that we might ever need to do while programming.
As mentioned above, C imposes relatively few built-in ways of doing things on the
programmer. Some common tasks, such as manipulating strings, allocating memory, and
doing input/output (I/O), are performed by calling on library functions. Other tasks which
you might want to do, such as creating or listing directories, or interacting with a mouse,
or displaying windows or other user-interface elements, or doing color graphics, are not
defined by the C language at all. You can do these things from a C program, of course,
but you will be calling on services which are peculiar to your programming environment
(compiler, processor, and operating system) and which are not defined by the C standard.
Since this course is about portable C programming, it will also be steering clear of
facilities not provided in all C environments.
Another aspect of C that's worth mentioning here is that it is, to put it bluntly, a bit
dangerous. C does not, in general, try hard to protect a programmer from mistakes. If you
write a piece of code which will (through some oversight of yours) do something wildly
different from what you intended it to do, up to and including deleting your data or
trashing your disk, and if it is possible for the compiler to compile it, it generally will.
You won't get warnings of the form ``Do you really mean to...?'' or ``Are you sure you
really want to...?''. C is often compared to a sharp knife: it can do a surgically precise job
on some exacting task you have in mind, but it can also do a surgically precise job of
cutting off your finger. It's up to you to use it carefully.
Characteristics of C
We briefly list some of C's characteristics that define the language and also have lead to
its popularity as a programming language. Naturally we will be studying many of these
aspects throughout the course.
Small size
Extensive use of function calls
Loose typing -- unlike PASCAL
Structured language
Low level (BitWise) programming readily available
Pointer implementation - extensive use of pointers for memory, array,
structures and functions.
C has now become a widely used professional language for various reasons.
Its main drawback is that it has poor error detection which can make it off putting to the
beginner. However diligence in this matter can pay off handsomely since having learned
the rules of C we can break them. Not many languages allow this. This if done properly
and carefully leads to the power of C programming.
#include <stdio.h>
main()
{
printf(“Hello”);
return 0;
}
C Program Structure
A C program basically has the following form:
Preprocessor Commands
Type definitions
Function prototypes -- declare function types and variables passed to
function.
Variables
Functions
Variables
C has the following simple data types:
a variable (also called an object) is a place you can store a value. So that you can refer to
it unambiguously, a variable needs a name. You can think of the variables in your
program as a set of boxes or cubbyholes, each with a label giving its name; you might
imagine that storing a value ``in'' a variable consists of writing the value on a slip of
paper and placing it in the cubbyhole.
A declaration tells the compiler the name and type of a variable you'll be using in your
program. In its simplest form, a declaration consists of the type, the name of the variable,
and a terminating semicolon:
char c;
int i;
float f;
You can also declare several variables of the same type in one declaration, separating
them with commas:
The placement of declarations is significant. You can't place them just anywhere (i.e. they
cannot be interspersed with the other statements in your program). They must either be
placed at the beginning of a function, or at the beginning of a brace-enclosed block of
statements (which we'll learn about in the next chapter), or outside of any function.
Furthermore, the placement of a declaration, as well as its storage class, controls several
things about its visibility and lifetime, as we'll see later.
You may wonder why variables must be declared before use. There are two reasons:
1. It makes things somewhat easier on the compiler; it knows right away what kind
of storage to allocate and what code to emit to store and manipulate each variable;
it doesn't have to try to intuit the programmer's intentions.
2. It forces a bit of useful discipline on the programmer: you cannot introduce
variables willy-nilly; you must think about them enough to pick appropriate types
for them. (The compiler's error messages to you, telling you that you apparently
forgot to declare a variable, are as often helpful as they are a nuisance: they're
helpful when they tell you that you misspelled a variable, or forgot to think about
exactly how you were going to use it.)
Although there are a few places where declarations can be omitted (in which case the
compiler will assume an implicit declaration), making use of these removes the
advantages of reason 2 above, so I recommend always declaring everything explicitly.
short number,sum;
int bignumber,bigsum;
char letter;
main()
{
}
For example:-
float sum=0.0;
int bigsum=0;
char letter=`A';
main()
{
}
float sum;
int bigsum;
char letter;
main()
{
sum=0.0;
bigsum=0;
letter=`A';
}
a=b=c=d=3;
a=3;
b=3;
c=3;
d=3;
You can define your own types use typedef. This will have greater
relevance later in the course when we learn how to create more complex
data structures.
As an example of a simple use let us consider how we may define two new
types real and letter. These new types can then be used in the same way
as the pre-defined C types:
C uses formatted output. The printf function has a special formatting character (%) -- a
character following this defines a certain format for a variable:
%c -- characters
%d -- integers
%f -- floats
NOTE: Format statement enclosed in ``...'', variables follow after. Make sure order of
format and variable data types match up.
scanf() is the function for inputting values to a data structure: Its format is similar to
printf:
Constants
A constant is just an immediate, absolute value found in an expression. The simplest
constants are decimal integers, e.g. 0, 1, 2, 123 . Occasionally it is useful to specify
constants in base 8 or base 16 (octal or hexadecimal); this is done by prefixing an extra 0
(zero) for octal, or 0x for hexadecimal: the constants 100, 0144, and 0x64 all represent
the same number. (If you're not using these non-decimal constants, just remember not to
use any leading zeroes. If you accidentally write 0123 intending to get one hundred and
twenty three, you'll get 83 instead, which is 123 base 8.)
We write constants in decimal, octal, or hexadecimal for our convenience, not the
compiler's. The compiler doesn't care; it always converts everything into binary
internally, anyway. (There is, however, no good way to specify constants in source code
in binary.)
ANSI C allows you to declare constants. When you declare a constant it is a bit like a
variable declaration except the value cannot be changed.
int const a = 1;
const int a =2;
Note:
You can declare the const before or after the type. Choose one an stick to
it.
It is usual to initialise a const with a value as it cannot get a value any
other way.
The preprocessor #define is another more flexible (see Preprocessor Chapters) method
to define constants in a program.
You frequently see const declaration in function parameters. This says simply that the
function is not going to change the value of the parameter.
The following function definition used concepts we have not met (see chapters on
functions, strings, pointers, and standard libraries) but for completenes of this section it is
is included here:
The second argument string is a C string that will not be altered by the string copying
standard library function.
Arithmetic Operations
Arithmetic Operators
The basic operators for performing arithmetic are the same in many computer languages:
+ addition
- subtraction
* multiplication
/ division
% modulus (remainder)
The - operator can be used in two ways: to subtract two numbers (as in a - b), or to
negate one number (as in -a + b or a + -b).
The modulus operator % gives you the remainder when two integers are divided: 1 % 2 is
1; 7 % 4 is 3. (The modulus operator can only be applied to integers.)
Multiplication, division, and modulus all have higher precedence than addition and
subtraction. The term ``precedence'' refers to how ``tightly'' operators bind to their
operands (that is, to the things they operate on). In mathematics, multiplication has higher
precedence than addition, so 1 + 2 * 3 is 7, not 9. In other words, 1 + 2 * 3 is
equivalent to 1 + (2 * 3). C is the same way.
All of these operators ``group'' from left to right, which means that when two or more of
them have the same precedence and participate next to each other in an expression, the
evaluation conceptually proceeds from left to right. For example, 1 - 2 - 3 is
equivalent to (1 - 2) - 3 and gives -4, not +2. (``Grouping'' is sometimes called
associativity, although the term is used somewhat differently in programming than it is in
mathematics. Not all C operators group from left to right; a few group from right to left.)
Whenever the default precedence or associativity doesn't give you the grouping you want,
you can always use explicit parentheses. For example, if you wanted to add 1 to 2 and
then multiply the result by 3, you could write (1 + 2) * 3.
Assignment Operators
x = 1
sets x to 1, and
a = b
sets a to whatever b's value is. The expression
i = i + 1
is, as we've mentioned elsewhere, the standard programming idiom for increasing a
variable's value by 1: this expression takes i's old value, adds 1 to it, and stores it back
into i. (C provides several ``shortcut'' operators for modifying variables in this and
similar ways, which we'll meet later.)
We've called the = sign the ``assignment operator'' and referred to ``assignment
expressions'' because, in fact, = is an operator just like + or -. C does not have
``assignment statements''; instead, an assignment like a = b is an expression and can be
used wherever any expression can appear. Since it's an expression, the assignment a = b
has a value, namely, the same value that's assigned to a. This value can then be used in a
larger expression; for example, we might write
c = a = b
which is equivalent to
c = (a = b)
and assigns b's value to both a and c. (The assignment operator, therefore, groups from
right to left.) Later we'll see other circumstances in which it can be useful to use the value
of an assignment expression.
It's usually a matter of style whether you initialize a variable with an initializer in its
declaration or with an assignment expression near where you first use it. That is, there's
no particular difference between
int a = 10;
and
int a;
/* later... */
a = 10;
Comparison Operators
To test for equality is ==
if ( i = j ) .....
This is a perfectly LEGAL C statement (syntactically speaking) which copies the value
in "j" into "i", and delivers this value, which will then be interpreted as TRUE if j is non-
zero. This is called assignment by value -- a key feature of C.
Not equals is: !=
Other operators < (less than) , > (grater than), <= (less than or equals), >= (greater than or
equals) are as usual.
Logical Operators
Logical operators are usually used with conditional statements which we shall meet in the
next Chapter.
Beware & and | have a different meaning for bitwise AND and OR ( more on this later
in Chapter 12).
Order of Precedence
It is necessary to be careful of the meaning of such expressions as a + b * c
(a + b) * c
or
a + (b * c)
All operators have a priority, and high priority operators are evaluated before lower
priority ones. Operators of the same priority are evaluated from left to right, so that
a - b - c
is evaluated as
( a - b ) - c
From high priority to low priority the order for all C operators (we have not met all of
them yet) is:
( ) [ ] -> .
! - * & sizeof cast ++ -
(these are right->left)
* / %
+ -
< <= >= >
== !=
&
|
&&
||
?: (right->left)
= += -= (right->left)
, (comma)
Thus
is interpreted as
( a < 10 ) && ( ( 2 * b ) < c )
and
a =
b =
spokes / spokes_per_wheel
+ spares;
as
a =
( b =
( spokes / spokes_per_wheel )
+ spares
);
Exercises
Write C programs to perform the following tasks.
Exercise 12270
Input two numbers and work out their sum, average and sum of the squares of the
numbers.
Exercise 12271
Input and output your name, address and age to an appropriate structure.
Exercise 12272
Write a program that works out the largest and smallest values from a set of 10 inputted
numbers.
Exercise 12273
Write a program to read a "float" representing a number of degrees Celsius, and print as a
"float" the equivalent temperature in degrees Fahrenheit. Print your results in a form such
as
Exercise 12274
Write a program to print several lines (such as your name and address). You may use
either several printf instructions, each with a newline character in it, or one printf with
several newlines in the string.
Exercise 12275
Write a program to read a positive integer at least equal to 3, and print out all possible
permutations of three positive integers less or equal to than this value.
Exercise 12276
Write a program to read a number of units of length (a float) and print out the area of a
circle of that radius. Assume that the value of pi is 3.14159 (an appropriate declaration
will be given you by ceilidh - select setup).
Your output should take the form: The area of a circle of radius ... units is .... units.
If you want to be clever, and have looked ahead in the notes, print the message Error:
Negative values not permitted. if the input value is negative.
Exercise 12277
Given as input a floating (real) number of centimeters, print out the equivalent number of
feet (integer) and inches (floating, 1 decimal), with the inches given to an accuracy of one
decimal place.
Exercise 12278
Given as input an integer number of seconds, print as output the equivalent time in hours,
minutes and seconds. Recommended output format is something like
Exercise 12279
The first integer value represents a time of day on a 24 hour clock, so that 1245
represents quarter to one mid-day, for example.
The second integer represents a time duration in a similar way, so that 345 represents
three hours and 45 minutes.
This duration is to be added to the first time, and the result printed out in the same
notation, in this case 1630 which is the time 3 hours and 45 minutes after 12.45.
Typical output might be Start time is 1415. Duration is 50. End time is 1505.
CHAPTER-2
Expression Statements
Expression statements do all of the real work in a C program. Whenever you need to
compute new values for variables, you'll typically use expression statements (and they'll
typically contain assignment operators). Whenever you want your program to do
something visible, in the real world, you'll typically call a function (as part of an
expression statement).
The if statement
The if statement has the same function as other languages. It has three basic forms:
if (expression)
statement
...or:
if (expression)
statement1
else
statement2
...or:
if (expression)
statement1
else if (expression)
statement2
else
statement3
For example:-
int x,y,w;
main()
{
if (x>0)
{
z=w;
........
}
else
{
z=y;
........
}
}
The simplest way to modify the control flow of a program is with an if statement, which
in its simplest form looks like this:
An if statement may also optionally contain a second statement, the ``else clause,''
which is to be executed if the condition is not met. Here is an example:
if(n > 0)
average = sum / n;
else {
printf("can't compute average\n");
average = 0;
}
Boolean Expressions
An if statement like
if(a = 0)
(and you probably will at some point; everybody makes this mistake), it will not test
whether a is zero, as you probably intended. Instead, it will assign 0 to a, and then
perform the ``true'' branch of the if statement if a is nonzero. But a will have just been
assigned the value 0, so the ``true'' branch will never be taken! (This could drive you
crazy while debugging--you wanted to do something if a was 0, and after the test, a is 0,
whether it was supposed to be or not, but the ``true'' branch is nevertheless not taken.)
The relational operators work with arbitrary numbers and generate true/false values. You
can also combine true/false values by using the Boolean operators, which take true/false
values as operands and compute new true/false values. The three Boolean operators are:
&& and
|| or
! not (takes one operand; ``unary'')
The && (``and'') operator takes two true/false values and produces a true (1) result if both
operands are true (that is, if the left-hand side is true and the right-hand side is true). The
|| (``or'') operator takes two true/false values and produces a true (1) result if either
operand is true. The ! (``not'') operator takes a single true/false value and negates it,
turning false to true and true to false (0 to 1 and nonzero to 0).
For example, to test whether the variable i lies between 1 and 10, you might use
Relational and Boolean expressions are usually used in contexts such as an if statement,
where something is to be done or not done depending on some condition. In these cases
what's actually checked is whether the expression representing the condition has a zero or
nonzero value. As long as the expression is a relational or Boolean expression, the
interpretation is just what we want. For example, when we wrote
if(x)
statement
and the statement will be executed if x is nonzero (since nonzero means ``true'').
This possibility (that the controlling expression of an if statement doesn't have to ``look
like'' a Boolean expression) is both useful and potentially confusing. It's useful when you
have a variable or a function that is ``conceptually Boolean,'' that is, one that you
consider to hold a true or false (actually nonzero or zero) value. For example, if you have
a variable verbose which contains a nonzero value when your program should run in
verbose mode and zero when it should be quiet, you can write things like
if(verbose)
printf("Starting first pass\n");
and this code is both legal and readable, besides which it does what you want. The
standard library contains a function isupper() which tests whether a character is an
upper-case letter, so if c is a character, you might write
if(isupper(c))
...
Both of these examples (verbose and isupper()) are useful and readable.
if(n)
average = sum / n;
where n is just a number. Here, the programmer wants to compute the average only if n is
nonzero (otherwise, of course, the code would divide by 0), and the code works, because,
in the context of the if statement, the trivial expression n is (as always) interpreted as
``true'' if it is nonzero, and ``false'' if it is zero.
``Coding shortcuts'' like these can seem cryptic, but they're also quite common, so you'll
need to be able to recognize them even if you don't choose to write them in your own
code. Whenever you see code like
if(x)
or
if(f())
where x or f() do not have obvious ``Boolean'' names, you can read them as ``if x is
nonzero'' or ``if f() returns nonzero.''
The ? operator
The ? (ternary condition) operator is a more efficient form for expressing simple if
statements. It has the following form:
It simply states:
z = (a>b) ? a : b;
if (a>b)
z = a;
else
z=b;
Switch – Case
The control statement which allows us to make a decision from
the number of choices is called a switch or says switch-case-default. These
three keywords are together to make the control statement. Its objective is to
check various possible constant values for an expression.
Syntax
switch( int variable)
{
case constant1:
statement 1;
break;
case constant2:
statement 2;
break;
case constant3:
statement 3;
break;
case constant4:
statement 4;
break;
default:
statement;
}
If we did not include a break statement after the first group for case one, the
program will not automatically jump to the end of the switch selective block
and it would continue executing the rest of statements until it reaches either
a break instruction or the end of the switch selective block. This makes
unnecessary to include braces { } surrounding the statements for each of the
cases. Notice that switch can only be used to compare an expression against
constants.
Exercise
1) Write a program to read two characters, and print their value when
interpreted as a 2-digit hexadecimal number. Accept upper case letters for
values from 10 to 15.
2) Read an integer value. Assume it is the number of a month of the year;
print out the name of that month.
3) Given as input three integers representing a date as day, month, year, print
out the number day, month and year for the following day's date.
Loops generally consist of two parts: one or more control expressions which control the
execution of the loop, and the body, which is the statement or set of statements which is
executed over and over.