c4micros

130
1 C4Micros: Contents Introduction -------------------------------------------------------2 Concept 1 - Micro controllers ----------------------------------3 Concept 2 – Languages------------------------------------------4 Concept 3 – Micro controller Hardware ----------------------5 Concept 4 - ROM and RAM -----------------------------------6 Concept 5 - CPU / pc /reset ------------------------------------7 Concept 6 - Function names -----------------------------------9 Concept 7 - The “main” function -----------------------------10 Concept 8 - Variable Types ------------------------------------12 Concept 9 - Variable Names / Assignment ------------------13 Concept 10 - Operators, Comments --------------------------15 Concept 11 - Let's do it in Style! ----------------------------17 Concept 12 - More Functions ------------------------------- 18 Concept 13 – And More Functions! ------------------------21 Concept 14 – Include files ----------------------------------23 Concept 15 – Local Variables -------------------------------25 Concept 16 – Auto and Static Variables -------------------31 Concept 17 – Global variables ------------------------------35 Concept 18 – Extern variables ------------------------------40 Concept 19 – Constant variables ---------------------------43 Concept 20 - #define constants ----------------------------46 Concept 21 – Enumerations ---------------------------------48 Concept 22 – Volatile variables ----------------------------49 Concept 23 – Macros ----------------------------------------51 Concept 24 – Integer Storage -------------------------------52 Concept 25 – Float Storage ---------------------------------55 Concept 26 – Operators / Precedence----------------------56 Concept 27 –Integral Promotions---------------------------58 Concept 28 - Type Conversion by Assignment-----------61 Concept 29 – Usual Arithmetic Conversions--------------63 Concept 30 – Explicit Type Conversion-------------------66 Concept 31 – Pointers cracked! ----------------------------68 Concept 32 – Arrays -----------------------------------------77 Concept 33 – Sizeof operator -------------------------------81 Concept 34 – Structures -------------------------------------82 Concept 35 – Preprocessor commands --------------------90 Concept 36 – Header files -----------------------------------93 Concept 37 – The last arithmetic operator, % ------------98 Concept 38 – Comparison operators -----------------------100 Concept 39 - Bit operators ----------------------------------102 Concept 40 - Logic operators -------------------------------108 Concept 41 – If Statements ---------------------------------110 Concept 42 – goto statements ------------------------------126 Concept 43 – The switch ------------------------------------128

Upload: praveensssp

Post on 12-Dec-2015

11 views

Category:

Documents


1 download

DESCRIPTION

Learn embedded C for micro controllers.

TRANSCRIPT

Page 1: C4micros

1

C4Micros:

Contents Introduction -------------------------------------------------------2

Concept 1 - Micro controllers ----------------------------------3

Concept 2 – Languages------------------------------------------4

Concept 3 – Micro controller Hardware ----------------------5

Concept 4 - ROM and RAM -----------------------------------6

Concept 5 - CPU / pc /reset ------------------------------------7

Concept 6 - Function names -----------------------------------9

Concept 7 - The “main” function -----------------------------10

Concept 8 - Variable Types ------------------------------------12

Concept 9 - Variable Names / Assignment ------------------13

Concept 10 - Operators, Comments --------------------------15

Concept 11 - Let's do it in Style! ----------------------------17

Concept 12 - More Functions ------------------------------- 18

Concept 13 – And More Functions! ------------------------21

Concept 14 – Include files ----------------------------------23

Concept 15 – Local Variables -------------------------------25

Concept 16 – Auto and Static Variables -------------------31

Concept 17 – Global variables ------------------------------35

Concept 18 – Extern variables ------------------------------40

Concept 19 – Constant variables ---------------------------43

Concept 20 - #define constants ----------------------------46

Concept 21 – Enumerations ---------------------------------48

Concept 22 – Volatile variables ----------------------------49

Concept 23 – Macros ----------------------------------------51

Concept 24 – Integer Storage -------------------------------52

Concept 25 – Float Storage ---------------------------------55

Concept 26 – Operators / Precedence----------------------56

Concept 27 –Integral Promotions---------------------------58

Concept 28 - Type Conversion by Assignment-----------61

Concept 29 – Usual Arithmetic Conversions--------------63

Concept 30 – Explicit Type Conversion-------------------66

Concept 31 – Pointers cracked! ----------------------------68

Concept 32 – Arrays -----------------------------------------77

Concept 33 – Sizeof operator -------------------------------81

Concept 34 – Structures -------------------------------------82

Concept 35 – Preprocessor commands --------------------90

Concept 36 – Header files -----------------------------------93

Concept 37 – The last arithmetic operator, % ------------98

Concept 38 – Comparison operators -----------------------100

Concept 39 - Bit operators ----------------------------------102

Concept 40 - Logic operators -------------------------------108

Concept 41 – If Statements ---------------------------------110

Concept 42 – goto statements ------------------------------126

Concept 43 – The switch ------------------------------------128

Page 2: C4micros

2

Introduction

This is a simple, direct, highly interactive course that will teach you to program micro

controllers using the C language. The emphasis is on practice and getting things to work

now!

Assumptions:

1. You have a vague idea what a micro controller is.

2. You do not know C.

3. You have a C compiler. (Down load a demo version from the web).

So what are we waiting for? Let's start!

Page 3: C4micros

3

Concept 1 - Micro controllers

Let's see what micro controllers are and what we should know about them to get along.

micro is small and a controller manipulates or controls things or events.

Thus let's just think of a micro controller as a mini computer that can do or control events

like blinking a LED or doing multiplication or indicating the temperature on a display.

So how do micros (for short) do these things?

We make them do these activities by instructing them to do the same, that is

programming them.

So how we do instruct them? Surely they do not understand English?

No, the only language they understand is something called machine language!

and you should physically dump those instructions in machine language into them.

So there are two things I should learn?

1. Machine language?

2. How to dump my instructions into them?

Page 4: C4micros

4

Concept 2 - Languages

How long will it take to learn this machine language's vocabulary? --- 2 Seconds!

Really? How big is its vocabulary? --- Only a 0 and a 1!

Try telling the micro to blink Leds using only 0s and 1s!

Impossible?

That's why some one invented Assembly.

Now what's that?

Its vocabulary consists of words like mov, clr, setb etc!

That’s for the 8051 Core. For the Microchip the words are movwf, bcf, clrf etc.

For Atmel ……? , For Hitachi …… ?

No! No?

That's why some one invented C. (who?)

It's closest to regular English. And common to all micro controller platforms.

But, but the micro doesn't understand C?

Yes, that's why we have a compiler to convert the C language instructions to machine

language and a programmer to dump the machine language instructions into the micro!

Ha! So now I need only to learn C?

and understand how to use a compiler and a programmer?

Always remember C is for Compilers and Machine Language is for Micro controllers.

Page 5: C4micros

5

Concept 3 – Micro controller Hardware

Micro controllers range from 8 pin to 80 pin devices.

The pins generally present are:

2 pins for the power supply, generally 5V. Can it work without a power supply?

2 pins for the crystal. It is a clock telling the micro to carry out an instruction or part of an

instruction with every pulse it generates.

1 pin for RESET. Tells the micro to start carrying out the instructions from the start.

A reset always occurs when a micro is switched ON.

2 pins for communication. Generally called Rx and Tx, one for Receiving and another for

transmitting.

1 or more pins for input. Generally switches.

1 or more pins for output. LCD displays, LED etc.

1 or more pins for interrupts or event counters, for immediate action or for counting

number of pulses.

Page 6: C4micros

6

Concept 4 - ROM and RAM

Programming is also called coding and the code written is put into a space called ROM in

the micro. ROM is Read Only Memory and cannot be changed after the code has been

put into the micro, or code has been burned in.

Let's say we want the micro to add 4 and 5 and display the answer on a LCD display

connected to the output pins.

We write the code say (4+5) compile it, get the machine code and dump it into the micro.

The code goes into the ROM of the micro, and when we switch on the micro, it resets,

does (4+5) and displays the answer 9.

A smart guy would realise 4 + 5 will always be 9 and rewrite the code to just display 9!

So having a ROM, where we cannot change values is not such a good idea after all!

Would we not want to do 6+7 or 3+2 also?

That's why we have the input pins! And where do the inputs get stored?

In the RAM, or Random Access Memory. RAM contents can be changed any number of

times and will remain in the memory of the micro as long as the micro is ON.

So are the answers in ROM or RAM? RAM, because they change with the inputs!

One of the important specifications, to look for in a micro is the size of the ROM and

RAM. ROM generally varies from 4 Kbytes to 64 Kbytes, and RAM from 128 bytes to 2

Kbytes.

Just to refresh our memories, a byte has 8 bits and a Kbyte has 1000 or 1024 bytes?

Page 7: C4micros

7

Concept 5 - CPU / pc /reset

The heart of every micro is a Central Processing Unit (CPU for short).

As the name suggests, the job of the CPU is to process and execute all instructions (code)

stored in the micro.

Let's say we have a micro with a ROM of 4 Kbytes and a RAM of 128 bytes.

{By the way why do we have so much ROM and so little RAM?

One of the reasons is that RAM is very expensive compared to ROM.

So why is RAM more expensive?

Because the code written in ROM is a one time affair (or may be a couple of times affair)

whereas the values stored in RAM are written and erased millions of times.}

The ROM has 4000 locations (4096 to be precise) numbered from 0 to 4095 and all

instructions (code) are stored sequentially in these locations from 0 to 4095.

The RAM has 128 locations numbered from 0 to 127 and may be randomly accessed and

used as required by the program.

There is one more piece of hardware we require to understand inside the micro, the

Program Counter (PC for short).

The PC keeps a count of the instructions (code) executed and points the next instruction

(code) to be executed to the CPU.

When power is first switched on, the micro always resets and the PC always points to the

instruction (code) located at location 0 of the ROM.

When you want the code to be executed from the start, RESET the micro. This is done

by connecting the RESET pin of the micro to Vdd (+5V) or Vss (0V) momentarily,

depending on the make of the micro chosen.

Yes, the other way is to switch OFF the power to the micro and then powering it ON!

So to summarise, we should write code and be certain our first instruction starts at

location 0 in ROM, since at power on, the PC would present the instruction at location 0

of ROM to the CPU to execute, and proceed from there onwards in sequence.

Page 8: C4micros

8

Thus our program for addition of two numbers and the display of the answer (C = A + B)

would be done as follows:

ROM Instruction RAM NAME VALUE

0 Get the number A. (store at RAM loc. 3). 0 C 9

1 Get the number B. (store at RAM loc. 1). 1 B 5

2 Add them and call it C. (store at RAM loc. 0). 2

3 Display C 3 A 4

4 - 4

A, B and C may be stored randomly and is determined by the compiler.

A, B and C are called VARIABLES as they take on different values every time we reset

the micro and the PC starts execution at ROM location 0.

The instructions may be considered as 3 distinct jobs:

1. Get the numbers.

2. Process them.

3. Display the answer.

Page 9: C4micros

9

Concept 6 - Function names

In C, the instructions are written in "functions" and the "functions" are executed or

"called" in a certain sequence.

A "function" may have a single instruction or "statement" or multiple “statements”.

Can it have no instructions at all?

To learn the language C we shall use

1) C terminology - for example "function" to indicate some job to be done, or a

"statement" to indicate an instruction. (The words enclosed in

inverted commas).

2) C words - We haven't seen them yet (They will be in bold italics), and are also

called “Reserved words” or “key words”. Examples are int, char etc.

3) C operators – Examples +, -, /, * etc

4) User names or “identifiers” - The names we give to “variables” or “functions”.

5) C syntax - the correct arrangement of C words, operators and identifiers, to

make sense to the compiler, enabling it to produce correct machine

code. (They will be in bold).

Since a C program may have many “functions”, they are given names so that they may be

distinguished easily.

Since a “function” is a set of instructions, it is sensible to split the instructions into

modules to do certain jobs.

Thus the previously listed jobs may be converted to “functions” and given names.

1. Get the numbers can be named as get_numbers.

2. Process them can be given the name, add_numbers.

3. Display the answer can be given the name, display.

The rules we shall adhere to while naming “functions” are:

1. Start with an alphabet. (Not an under score or numeral).

2. Only use lower case alphabets.

3. Seperate words with underscores. (Spaces are not allowed).

4. Use numerals only if they impart some meaning to the name.

5.Don't worry about the length of the names. (Check your compiler for the maximum

length).

6.The name should adequately represent the “function” or job being performed.

Page 10: C4micros

10

Concept 7 - The “main” function

Now any job or “function” or routine or subroutine generally produces an output.

The output of a “function” in a micro would naturally be a number and is also called its

"return value".

If the value returned is a byte long, it is called char, short for character.

So char is the first C word we have learnt. Since it a byte and has 8 bits, it can represent

256 values.

An integer, int for short represents two bytes and can represent 2^16 or 65536 values.

If there is no output it is represented by void. These are called "types".

The C word return will return a number or value.

The value to be returned is put in brackets after the word return as follows: return ( 1 ) ;

This would “return” the number 1 or, we can say the output of this particular function is

the number 1.

A semicolon is added to indicate to the compiler that it is a complete instruction or

"statement".

This is our first "statement" we have learnt.

The statement return ( 1 ); is an example of C syntax with an output 1.

Will not a function require some input too? Normally yes, for example, a function adding

two numbers would require the two numbers as input.

What will the input "type" be? Yes! char or int or nothing, void.

The inputs may be plain numbers or “names” of “variables”, with their “types”.

The plain numbers or “type” and variable “names” are enclosed in brackets.

Examples: (2, 5), or ( char a, char b ) ;

The inputs are also called “arguments”.

Finally a “function” will have a series of “statements” or instructions and these are

enclosed in curly brackets.

So now that we have seen the various components of a “function”, we are ready for some

more syntax!

How do we represent the “function” for the compiler to understand us?

A B ( C )

{

statements

return ( ) ;

}

Page 11: C4micros

11

where A is the “return type”, B is the “function name” and C is the input/inputs enclosed

in brackets.

The above is also called a "definition" of a function.

So let's write our first function:

1.We want it to start at RAM 0.

2.Since it is the first function, it has no inputs.

3.The function does nothing.

4. Thus we have no outputs, and hence nothing to return.

So our first function is

main ( )

{

}

main is the name of the “function” which the compiler looks for, and the first instruction

in main is put at ROM location 0.

Thus main is a "reserved" “function name” and has to be used in every C program to

indicate start of user code.

The compiler might put some house keeping code at RAM 0 and the user code will begin

immediately after that.

So go ahead and compile the “function” main.

A “return value” could be used to check if all the statements have been carried out and a

good and specific way to write the main function would be:

int main ( void )

{

return ( 1 ) ;

}

Go ahead and check where the number 1 has been returned on executing the above

program.

So to summarise, every C program should have a function called main ( )

The first function to be executed in every C program is the main ( ) function.

The first instruction to be executed in every C program is the first instruction (statement)

in the main ( ) function.

Page 12: C4micros

12

Concept 8 - Variable Types

Now that we know how to do nothing, let's learn how to do something!

Consider our problem:

C = A + B.

A, B and C are the “names” we have given to the “Variables”. They are called

“variables” because they take on different values for different problems.

We saw they are stored in RAM. What are the possible values, these variables could

take?

If they take integer values like 0, 1, 2, 3,..... 255, they are called “characters” and are

designated the type char.

Since they take only positive values, they are designated as unsigned char.

So what if they take negative values like -1, -2, -3,..... 128?

We then designate them-signed char.

As we saw earlier, a char has 8 bits and can store 256 values.

The values a signed char can represent are the integers from 0 to 127 with the MSB

(most significant bit 0), or the integers -1 to -128 with the MSB set to 1.

Similarly we have unsigned int having a range of positive integers from 0 to 65535,

or signed int having a range of positive integers from 0 to 32,767 or negative integers

from -1 to -32768.

An integer generally occupies 16 bits.

A long is 4 bytes long.

An unsigned long would represent positive integers 0 to 4294967295.

A signed long would represent positive integers from 0 to 268435455 or negative

integers from -1 to -268435456.

Why do we have so many types?

The compiler allots the RAM as per our directions.

As RAM is precious and very little of it is available, we have to choose the smallest

variable type required for our purpose.

Thus ideally we would prefer char if that would suffice for our purposes.

What if we have to represent still larger numbers, or we have to add 4.5 + 5.2?

We have float 4 bytes long, representing floating-point numbers, the ones having decimal

points.

The range of a float is approximately +/- 1 exp 38 to +/- 1 exp -38.Check your compiler.

Page 13: C4micros

13

Concept 9 - Variable Names / Assignment

Consider the following problem:

If the cost of a book is 10 bucks and the cost of a pen is 1 buck, what is the total cost?

We can write the problem as C = A + B, where A is the cost of the book, B is the cost of

the pen and C is the total cost.

Here the variables are book, pen and total cost. We have named them A, B and C

respectively.

Wouldn't it be more clearer if we named them book, pen and total or better still

cost_of_book, cost_of_pen and total_cost?

Sure! So here are the rules we shall follow for naming variables:

1.Start with an alphabet. (Not an under score or numeral).

2.Only use lower case alphabets.

3.Seperate words with underscores. (Spaces are not allowed).

4.Use numerals only if they impart some meaning to the name.

5.Don't worry about the length of the names. (Check your compiler for the maximum

length).

6.The name should adequately represent the variable.

Let's now "assign" values to our variables: 10 for the book and 1 for the pen.

This is done as cost_of_book = 10 and cost_of_pen = 1.

Now we have enough data to "declare" our “variables” to the compiler “define” or

“allocate” storage for them and finally "assign" values to them.

Variables are "declared / defined" to the compiler as A B ; where A is the "type", B is the

name.

Values are "assigned" to the “variables” as B = C ; where B is the name, and C is the

value “assigned” to the “variable”.

The ; as usual tells the compiler that the "declaration / definition" or "assignment" is

complete.

"Declaration / definition" and "assignment" can be combined as A B = C ;

This is preferable, as we would not forget to initialise values.

The difference between “declaration” and “ definition” is that “declaration” does not

“allocate” storage whereas a “definition” does.

Storage or “definition” may be done only once; “declaration” may be required more than

once. They are use generally used quite interchangeably.

Why is it required to declare variables?

The compiler needs to know the amount of RAM to be allocated, hence the "type". It has

to distinguish between variables (RAM locations), hence the name. Every name or RAM

location has a number in it. If a specific number is not assigned, the name (RAM

location) can have some junk number in it. Hence variables are generally initialised.

Page 14: C4micros

14

A number of “variables” of the same “type” may be “declared” on a single line separated

by commas, for example:

char a, b, c ; or char a = 4, b = 5, c ;

Once the “variables” are “declared”, they are used by referring to their names only.

for example:

char a, b, c ; // declaration

a = 4, b = 5 ; // use

c = a + b ; // use

or

char a = 4, b = 5, c ; // declaration cum assignment

c = a + b ; // use

Page 15: C4micros

15

Concept 10 - Operators, Comments

Back to our problem, c = a + b. (We have made it lower case, ha ha!)

Consider the following "declaration" cum "assignment":

char a = 4, b = 5, c ;

The compiler allocates 1 byte each for a, b and c (char).

It assigns 4 to a and 5 to b. c has not been initialised and can have any value in it.

Generally a 0 the first time you run the compiler / debugger.

Now does the compiler understand the addition symbol (+)?

Can we write the instruction as c = a + b?

Luckily, yes! The compiler understands the "operators" + for addition, - for subtraction,

* for multiplication and / for division!

So our "statement" is written as c = a + b ; (Remember it is complete only with a

semicolon).

So let's build our first program, which does something!

Start of code:

The compiler always starts with the "main" function, so we write

int main ( void )

Next we have to enclose all instructions in curly brackets, so we open the curly bracket

{

Next we allocate spaces, names in RAM by "declaration" and "assignment"

char a = 4, b = 5, c ;

Next we write the first instruction,

c = a + b ;

Next we give the "return" statement,

return ( 1 ) ;

Finally we close the main function with

}

End of code.

Page 16: C4micros

16

Copy and paste the above program from start of code to end of code in your compiler.

Compile and run the above "code"! My! What a lot of errors!

Why? Because the compiler doesn't understand English and couldn't distinguish between

"code" and our explanation statements.

The compiler ignores explanation statements or "comments" if they are enclosed with a

/* and */.

Thus our code would now be written as:

int main ( void ) /* Next we have to enclose all instructions in curly brackets, so we open the curly bracket

*/

{

/* Next we allocate spaces, names in RAM by "declaration" and "assignment" */

char a = 4, b = 5, c ;

/* Next we write the first instruction, */

c = a + b ;

/* Next we give the "return" statement, */

return ( 1 ) ;

/*Finally we close the main function with */

}

Single line comments may start with // and nothing is required to end them. (Check your

compiler).

Go ahead and compile the above again.

Page 17: C4micros

17

Concept 11 - Let's do it in Style!

A neat way to write the previous program would be:

int main ( void ) // The program always starts at main.

{ // We have to enclose all instructions in curly

brackets, so we open the curly bracket.

char a = 4, b = 5, c ; // we allocate spaces, names in RAM by

“declaration" and "assignment".

c = a + b ; // the first instruction.

return ( 1 ) ; // 1 is returned on successful completion.

} // Finally we close the main function with a curly

bracket.

Code should be written in such a way that:

1.It is presentable.

2.Clearly understood.

3.Easily modifiable.

The rules we shall follow for presentation:

1. The "return type" for main will be int.

2. A space between function names and brackets.

3. A space at start and end of brackets.

4. A space before a semi colon.

5. A space before and after operators.

6. Inputs will be typed, "void" if no inputs are required for the function.

7. The curly bracket will be placed below the function type.

8. Declarations after 1 blank line below the curly bracket.

9. Declarations 1 tab or 4 spaces from the curly bracket.

10. Statements after 1 blank line below the declarations.

11. Statements in line with declarations.

12. 1 blank line before start of a new function.

13. 1 blank line between the last statement and the return statement.

14. 1 blank line between the return statement and curly bracket.

Page 18: C4micros

18

Concept 12 - More Functions

We saw how to start a C program: use the “main” function.

Now let's see how other functions are written.

There are 3 requisites in writing a function:

1.The “Function declaration or prototype”:

This is written as A B ( C, D…) ;

where A is the “return type”, B is the “function name” and C, D … are the input “types”.

The “prototype or function declaration” is made outside and before the “main” function.

This gives the compiler information to check syntax of the function wherever used.

2.The “Function definition”:

The code is derived here, and the function definition is written as

A B ( C, D, …) {

declarations

statements

return ( E ) ;

}

where A is the “return type”, B is the “function name” and C, D … are the

input “arguments”.

“arguments” are the input declarations and are written as A B where A is the “input type”

and B is the “name” of the input variable.

The Function “definition” is generally written after the closing brackets of the “main”

function.

3. “Calling” the function:

As we now know, code starts executing with the opening bracket in “main” and ends

with the closing bracket in “main”.

So how does the code in the functions get executed?

“Code” in “functions” are executed by “calling” them in the “main” function.

This is done by writing the statement B ( x, y, …) ; at the point in the “main” program,

the code is required to execute. Here B is the “function name” and x, y, … are the input

“arguments” on which the “function” is to be performed.

Page 19: C4micros

19

Now let's write a function for “addition”.

We shall add two integer numbers whose sum is less than 255. C = A+B, where A = 10,

and B = 20.

The steps:

I. “Function declaration” or “prototype”:

1. Name the function: add_numbers

2. Add the return and input “types”:

char add_numbers ( void ) ; // Inputs A and B are fixed and can be made

part of the function itself.

II. "Function definition":

char add_numbers ( void ) // function name, its return type and input

arguments

{ //start of function

char a = 10, b = 20, c ; // declarations

c = a + b ; // statements

return ( c ) ; // return value

} // close of function

The function definitions are generally put below the "main" function.

III. “Calling” or using the function:

int main ( void ) {

add_numbers ( ) ; //The function is "called" by invoking the "name” of

the function and its "arguments", no “void”here!

return ( void ) ;

}

So a complete program would be:

Page 20: C4micros

20

char add_numbers ( void ) ; // function declaration.

int main ( void ) // execution starts here.

{

add_numbers ( ) ; // The function is called.

return ( 1 ) ; }

char add_numbers ( void ) // function definition

{

char a = 10, b = 20, c ; // input, output declarations.

c = a + b ; // statements

return ( c ) ;

}

Let’s compile and see.

Page 21: C4micros

21

Concept 13 – And More Functions!

We saw one way to use a function, “Call” it.

It does its work, no inputs required, and we didn’t use its output (return value).

Let’s see how to “pass” inputs to our “function”.

We rewrite the above program with the following changes:

1. In the Function declaration, the input “types” are added.

2. In the Function definition, the input “arguments” are added.

3. In the “main” Function, the inputs are “passed by value” on to the “function”.

char add_numbers ( char, char ) ; // function declaration with input “types”.

int main ( void ) // execution starts here.

{

char x = 4, y = 5 ; // values “assigned” to variables of interest

add_numbers ( x, y ) ; /* The function is “called” “passing”

“variables” into it.*/

// add_numbers ( 1, 2 ) ; // The function is called “passing” “values”.

return ( 1 ) ; }

char add_numbers ( char a, char b ) /* function definition with input

“declarations” */

{

char c ; /* a and b already declared in function

definition above, hence not required. */

c = a + b ; // statements

return ( c ) ;

}

Time to compile. Recompile deleting commented “statement”.

Now let’s see how to use the return values.

The return values have to be “assigned” to any variable in “main” to be used.

So here goes:

Page 22: C4micros

22

char add_numbers ( char, char ) ; // function declaration with input “types”.

int main ( void ) // execution starts here.

{

char x = 4, y = 5, z ; // values “assigned” to variables of interest

z = add_numbers ( x, y ) ; /* The function is “called” “passing”

“variables” into it, and z is “assigned”

the return value*/

return ( 1 ) ; }

char add_numbers ( char a, char b ) /* function definition with input

“declarations” */

{

char c ; /* a and b already declared in function

definition above, hence not required. */

c = a + b ; // statements

return ( c ) ;

}

Ready for compilation and debug!

Page 23: C4micros

23

Concept 14 – Include files

We now know how to write code! And compile it! And debug it! Great!

Now imagine we have a hundred jobs “functions” to be executed, or imagine Tom, Dick

and Harry have written separate functions and we have to combine them.

The way it should be done is follow the structure below:

function 1 prototype ;

function 2 prototype ;

function 3 prototype ;

main ( )

{

function 1 ( ) ;

function 2 ( ) ;

function 3 ( ) ;

}

function 1( )

{

}

function 2 ( )

{

}

function 3 ( )

{

}

Gosh! Wouldn’t it become unmanageable if we have plenty of functions?

It would be much nicer if we could write the functions separately, test them separately,

and ask some one else to combine them into a single program. Who is going to do it?

The compiler, with what is called “preprocessor directives”.

The “preprocessor directives or commands” instruct the compiler to do some cutting and

pasting and some such work before beginning the serious work of “assembling” code.

The first “preprocessor directive we are going to learn is

#include “A.c”

where A is the name of the file we are going to include, it is a C file and it is in the same

folder or project folder.

Page 24: C4micros

24

So let’s create the file Add.c, and put the function add_numbers in it.

The function definition is:

char add_numbers ( char a, char b) {

char c ;

c = a + b ;

return ( c ) ; }

Now let’s write the “main” program and save it as main.c:

int main ( void ) {

return ( 1 ) ;

}

Now let’s combine them in main.c:

#include “Add.c” // Includes the file in the main.c

char add_numbers ( char, char ) ; // function prototype for compiler check

int main ( void ) {

char x = 2, y = 3 ;

add_numbers ( x, y ) ;

return ( 1 ) ; // Try return ( 0 ) ;

}

So the rules are:

1.Put all required files in a single project folder.

2.Include them (using #include “file.c”) in your main program, generally called main.c.

3.Insert required function prototypes.

4.Write the main program.

5.Compile main.c

Page 25: C4micros

25

Concept 15 – Local Variables

Let’s observe the structure of the C “functions” we have learned so far.

char add_numbers ( void ) // function name, its return type and input

arguments

{ //start of function

char a, b = 20, c ; // declarations, declaration cum initializations

c = a + b ; // statements or executable code

return ( c ) ; // return value

} // close of function

The variables are declared at the start of a “block” (after the opening of the curly

brackets). They are within the “block”. (The opening and closing of the curly brackets).

Thus they may be said to be “local” to the “block” as they are localized within the block.

Let’s try to get a better insight of these “local” variables and their “localization”.

If you have come so far, you must be pretty confident in writing a complex program like:

int main ( void ) {

char a = 1, b = 2, c = 3, d = 4, e, f, g ; // define separate variables a - g

e = (a+b) ;

f = (c*d) ; g = e + f ; // perform a complex calculation!

return ( 1) ;

}

You have taken care to allocate separate names and spaces for the variables, not to

confuse your compiler or yourself.

You get the answer 0F, perfect!

Now try being smart, try reducing the number of variables, try something like:

int main ( void ) {

char a = 1, b = 2, e; // define a, b and e

Page 26: C4micros

26

e = (a+b) ; // e = 3

char a = 3, b = 4, f; // redefine a and b

f = (a*b) ; // f = 12

char g ; // define g

g = e + f; // g = 15

return ( 1) ;

}

Gosh! What a lot of errors!

Too many statements to handle, so let’s try a reduced compile of:

int main ( void ) {

char a = 1, b = 2, e; // define a, b and e

e = (a+b) ; // e = 3

char a = 3, b = 4, f; // redefine a and b

return ( 1) ;

}

Still errors!

Let’s try putting all variable declarations at one place.

int main ( void ) {

char a = 1, b = 2, e; // define a, b and e

char a = 3, b = 4, f; // redefine a and b

e = (a+b) ; // e = 3

return ( 1) ;

}

Page 27: C4micros

27

The compiler does not allow redefinition.

So rule no.1:

All variables in a block should have different names!

Now try this:

int main ( void ) {

char a = 1, b = 2, e; // define a, b and e

e = (a+b) ; // e = 3

char f ;

return ( 1) ;

}

Errors!

Finally this:

int main ( void ) {

char a = 1, b = 2, e; // define a, b and e

char f;

e = (a+b) ; // e = 3

return ( 1) ;

}

Thank God! Only warnings!

Rule no. 2:

All variables have to be declared immediately after opening the curly brackets!

Executable code should start after declarations or declaration cum initializations.

Will this work?

Page 28: C4micros

28

int main ( void ) {

char a, // declare a

a = 1 ; // make a = 1

b = 2, e; // define b and e

e = ( a + b ) ; // e = 3

return ( 1) ;

}

No! a = 1 ; is executable code and should come after declarations and declaration cum

assignments!

Now let’s imagine you did not have the time to write such a complex program, hence you

hire Tom and Dick to do the job. Just to refresh your mind the program was:

int main ( void ) {

char a = 1, b = 2, c = 3, d = 4, e, f, g ; // define separate variables a - g

e = ( a + b ) ;

f = ( c * d ) ;

g = e + f ; // perform a complex calculation!

return ( 1 ) ;

}

Tom writes out the code for “addition” as

{

char a = 1, b = 2, c ;

c = a + b ;

}

Dick writes the code for “multiplication” as

{

char a = 3, b = 4, c ;

c = a * b ;

Page 29: C4micros

29

}

With our previous experience of using variables with the same name we should expect a

mess! But any way we try compiling as:

int main ( void ) {

char e, f, g ; // Our required output variables.

{ // Tom’s code

char a = 1, b = 2, c ;

c = a + b ;

e = c ; // We rename c as per our requirement.

}

{ // Dick’s code

char a = 3, b = 4, c ;

c = a * b ;

f = c ; // We rename c as per our requirement.

}

g = e + f ;

return ( 1 ) ;

}

We did it! It compiled and ran and we got the right answer!

So here are the points we learn:

1. Variables are declared, declared cum assigned values immediately after opening a

block.

2. Variable names in a block have to be unique.

3. Variables in one block are “invisible” to variables in another block and are said to

be “local” to that block, and may have the same names.

4. Blocks can have blocks in them, and the variables declared in an outer block are

visible inside an inner block, but not vice versa.

5. The “visibility” of a variable is also called its “scope” and a variable can be used

only if it is “visible”.

Page 30: C4micros

30

6. Communication between blocks is achieved by declaring variables in an outer

block, enclosing the various blocks.

The program rewritten indicating the “scope” of the variables:

int main ( void ) {

char e, f, g ; // e, f, g are visible till end of “main” and can be

// used anywhere.

{ // Tom’s code

char a = 1, b = 2, c ; // a, b, c are visible only in Tom’s code

c = a + b ; e = c ; // e is visible

}

{ // Dick’s code

char a = 3, b = 4, c ; // A new set of a, b, c are visible only in

// Dick’s code

c = a * b ; f = c ; // f is visible.

}

g = e + f ; // only e, f, g are visible

return ( 1 ) ;

}

Page 31: C4micros

31

Concept 16 – Auto and Static Variables

Scope and visibility of the variables was something for the compiler to relate to, so that

programs written by various authors could be merged without ambiguity.

But what about their actual storage? Where are they stored in the micro controller?

Variables are stored in RAM. RAM we saw was expensive and very little is generally

available. Consider we have a RAM of 4 bytes and we want to do the following:

x = a + b + c + d

We want to display the value of x.

If we allocate 1 byte for each variable, we require 5 bytes and we do not have them.

Let’s allocate the 4 bytes to a, b, c and d and carry out the addition. Once the addition is

carried out we do not require a, b, c or d as we only have to display x. Hence we require

only to store x and a, b, c and d may be “destroyed”.

Let’s “destroy” a and store x in its place.

Now we can do

y = b + c + d, destroy b and store y in its place.

Then you can do

z = c + d, destroy c and store z in its place.

And we can still do

f = d * d !, destroy d and store f in its place!

Thus we can do with 4 bytes, where normally you would have done with 8 bytes!

So now we know why RAM is also called a “scratch pad”! Scratch, scratch, and scratch!

So we should learn to use the RAM for necessary calculations, store required quantities

only and make available the remaining RAM for other calculations or work. We have to

clearly make a distinction between variables that are required to be stored permanently

and those that can be destroyed.

Variables that are stored permanently are called “static” variables.

Variables that are destroyed are called “ auto” variables”

So are “local” variables “auto” or “static”?

Page 32: C4micros

32

They are “auto”! The compiler treats all local variables as “auto”. The variables are

created on entering a block and may be destroyed on exit from the block.

Variables declared inside the curly brackets of a function are “local”, and may get

destroyed on exiting the “function”. If the “function” is entered again, care should be

taken to initialize the “variables” again or they may have some junk value in them.

So let’s CCD (Code, Compile and Debug!).

int main ( void ) {

char a, b, c = 2, d = 3 ; // local variables declared inside block

a = c + d ;

b = c * d ;

return ( 1 ) ;

}

We have declared our variables within “blocks” and hence they are considered “local”

and hence “auto” by the compiler and may be destroyed by the end of the program.

Which ones have been destroyed? When have they been destroyed?

How do we stop them from being destroyed? What if we require the value to be

maintained forever? Allot permanent storage for the variable?

We declare the variable “static”. We prefix the “type” with the word static.

The above example would now become:

int main ( void ) {

static char a, b, c = 2, d = 3 ; // local variables declared inside block

a = c + d ;

b = c * d ;

return ( 1 ) ;

}

Now try this:

char f1 (void ) ; // function prototype

Page 33: C4micros

33

int main ( ) {

char a ;

a = f1 ( ) ; // call f1 ( )

a = 0 ; // change a

a = f1 ( ) ; // call f1 ( ) again

return ( 1 ) ; // success

}

// function definition

char f1( void ) {

char a = 2 ;

a = a + 1 ;

return ( a ) ; }

What do we observe?

Variable “a” in the “main” block takes the value 3 after every return from the function f1(

). The variable “a” in the function f1( ) is initialized each time upon entry to 2.

Or we can say the variable is created anew upon entry into the block and destroyed upon

exit from the block.

And now with the variable in the function declared as static:

char f1 (void ) ; // function prototype

int main ( ) {

char a ;

a = f1 ( ) ; // call f1 ( )

a = 0 ; // change a

a = f1 ( ) ; // call f1 ( ) again

return ( 1 ) ; // success

}

// function definition

Page 34: C4micros

34

char f1( void ) {

static char a = 2 ;

a = a + 1 ;

return ( a ) ; }

Variable “a” in the “main” block takes the value 3 after the first return from the function

f1( ) as expected. The value becomes 4 on the second return from the function f1( )!

The variable “a” in the function f1( ) is not initialized each time upon entry to 2.

As we have allotted it permanent storage it retains its previous value!

So auto variables have to be initialized every time before use.

static variables need to be initialized only once before the first use, afterwards they retain

their previous value permanently.

static variables are local and “visible” in the “block” they are declared.

In the program below a and b have permanent storage. a is visible throughout, while b is

visible only in the inner block.

int main ( void ) {

static char a = 3 ;

a = a + 2 ; // a = 5

{

static char b = 4 ;

b = b + 3 ; // b = 7

a = a + b ; // a = 12

}

return ( 1 ) ;

}

Page 35: C4micros

35

Concept 17 – Global variables

The variables we have seen so far were “local” and were “visible” only in the block they

were used. What do we do if we require variables to be visible to all blocks?

We use “global variables”. “global variables” are variables “declared” outside and above

a “block”. “Statements” enclosed in curly brackets form a block.

Now let’s write a program with the variables declared outside the block:

char a, b, c = 2, d = 3 ; // global variables declared outside and above block

int main ( void ) {

a = c + d ;

b = c * d ;

return ( 1 ) ;

}

Are the variables a, b, c, d visible inside the block?

Are all of them preserved?

Yes! “global variables” are visible everywhere and are also static.

If a “local variable” has the same name as a “global variable”, the local variable takes

precedence.

Now try this:

int main ( void ) {

a = c + d ;

b = c * d ;

return ( 1 ) ;

}

char a, b, c = 2, d = 3 ; // global variables declared outside and below block

Global variables should be declared above the block.

And this: A program with functions:

char f1 ( char, char ) ; // function prototype ( compiler check)

int main ( )

Page 36: C4micros

36

{

char a = 2, b = 3, d ; // local variables

d = f1 ( a, b ) ;

return ( 1 ) ;

}

char f1 ( char a, char b ) // function definition, a and b declared “local”

{

char c ; // local

c = a + b ;

return ( c ) ;

}

“arguments” of a function are always “local”.

Did d get destroyed? Is it visible in the main block?

And this: Make ‘d’, a variable in main a “global”

char d ;

char f1 ( char, char ) ;

int main ( ) {

char a = 2, b = 3 ;

d = f1 ( a, b ) ;

return ( 1 ) ;

}

char f1 ( char a, char b ) {

char c ;

Page 37: C4micros

37

c = a + b ;

return ( c ) ;

}

And this: Make ‘c’, a variable of a function a global

char d ;

char f1 ( char, char ) ;

int main ( ) {

char a = 2, b = 3 ;

d = f1 ( a, b ) ;

return ( 1 ) ;

}

char c ;

char f1 ( char a, char b ) {

c = a + b ;

return ( c ) ;

}

Did c get preserved?

Is c truly global? Is it visible in the main block? Try this to find out:

char d ;

char f1 ( char, char ) ;

int main ( ) {

char a = 2, b = 3 ;

Page 38: C4micros

38

d = f1 ( a, b ) ; c = 7 ; // c has been declared below

return ( 1 ) ;

}

char c ;

char f1 ( char a, char b ) {

c = a + b ;

return ( c ) ;

}

No! It is “global” or “visible” only to functions or blocks below the declaration.

It can be used only where it is visible.

And this: Declare all “globals” above “main”

char c ;

char d ;

char f1 ( char, char ) ;

int main ( ) {

char a = 2, b = 3 ;

d = f1 ( a, b ) ;

c = 7 ;

return ( 1 ) ;

}

char f1 ( char a, char b ) {

c = a + b ;

return ( c ) ;

It works! A Variable should be “declared” before use.

Page 39: C4micros

39

And this: What do we do with separate files?

// main.c

#include “f1.c”

char d ;

char f1 ( char, char ) ;

int main ( ) {

char a = 2, b = 3 ;

d = f1 ( a, b ) ;

return ( 1 ) ;

}

// f1.c

char c ;

char f1 ( char a, char b ) {

c = a + b ;

return ( c ) ;

}

In main.c we are including file f1.c at the beginning and hence c is declared before main

and is fully visible everywhere!

Page 40: C4micros

40

Concept 18 – Extern variables

We have seen one way of combining files written at various times or by various people,

by using the #include preprocessor directive.

There is another way. Let’s try it out. Here’s the program copied from concept #include

files with the #include line commented out:

file main.c:

// #include “Add.c” // Includes the file in the main.c

char add_numbers ( char, char ) ; // function prototype for compiler check

int main ( void ) {

char x = 2, y = 3 ;

add_numbers ( x, y ) ;

return ( 1 ) ; }

The file Add.c:

char add_numbers ( char a, char b) {

char c ;

c = a + b ;

return ( c ) ; }

Put the files in the same project folder and add them both to the same project.

Compile and check!

Now let’s make c a “global” and use it in “main.c”:

file main.c:

char add_numbers ( char, char ) ; // function prototype for compiler check

int main ( void ) {

Page 41: C4micros

41

char x = 2, y = 3 ;

add_numbers ( x, y ) ;

c = 7 ;

return ( 1 ) ; }

The file Add.c:

char c ;

char add_numbers ( char a, char b) {

c = a + b ;

return ( c ) ; }

It doesn’t work. The compiler does not recognize c as it is external to the file.

In the #include case the added file became part of the final file and it worked.

c has been “defined” in the file Add.c and storage allocated there.

Hence only a “declaration” as to where c is to found is required in “main.c”.

The C “reserved word” extern does this.

The program with c declared outside the main block now becomes:

file main.c:

extern char c ; // c is defined in another file.

char add_numbers ( char, char ) ; // function prototype for compiler check

int main ( void ) {

char x = 2, y = 3 ;

add_numbers ( x, y ) ;

c = 7 ;

return ( 1 ) ; }

Page 42: C4micros

42

The file Add.c:

char c ;

char add_numbers ( char a, char b) {

c = a + b ;

return ( c ) ; }

Or with c declared within the main block, where it is used:

file main.c:

char add_numbers ( char, char ) ; // function prototype for compiler check

int main ( void ) {

extern char c ; // c is defined in another file.

char x = 2, y = 3 ;

add_numbers ( x, y ) ;

c = 7 ;

return ( 1 ) ; }

The file Add.c:

char c ;

char add_numbers ( char a, char b) {

c = a + b ;

return ( c ) ; }

Global variables may be used as such in the files where they are “defined”. Storage in

RAM is allocated here.

If they are required to be used in any other file, they have to be “declared” in that file as

extern. Storage is not allocated here, the compiler looks for its definition outside.

Page 43: C4micros

43

Concept 19 – Constant variables

Let’s say we have a program where we require finding the perimeter of a circle of radius

2 cm.

We could code as:

int main ( void ) {

char radius = 2; // variables which can take on various values

float perimeter ;

perimeter = 2 * 3.14 * radius ; // 2 and 3.14 never change

return ( 1 ) ;

}

2 and 3.14 are constants and have been written as such in the formula.

We did not declare any constants. It worked fine, so why should we ever declare

constants?

Let’s say we require finding the area too.

Our code would now become:

int main ( void ) {

char radius = 2; // variables which can take on various values

float perimeter, area ;

perimeter = 2 * 3.14 * radius ; // 2 and 3.14 never change

area = 3.14 * radius * radius ;

return ( 1 ) ;

}

Now let’s say we require finding (3.14 * radius * radius * radius) and

(3.14 * radius * radius * radius * radius). Why? I don’t know!

But we can see 3.14 is used quite a lot and may be we can replace it with PI, which is

what 3.14 is. We would probably then never make a mistake like writing 3.41 in place of

3.14.PI is also more easily visualized than the number 3.14.

So our code would become:

Page 44: C4micros

44

int main ( void ) {

char radius = 2; // variables which can take on various values

float PI = 3.14 ; // a “constant” declared as a “variable”

float perimeter, area ;

perimeter = 2 * PI * radius ; // PI used as a variable

area = PI * radius * radius ;

return ( 1 ) ;

}

We now find our customer is fussy about the accuracy of our results and would like to

have PI replaced with 3.14159 or whatever!

Thank God we considered 3.14 as a variable with a name PI and didn’t leave it as a

number. We need to change the value only once as required by the customer!

Now try this:

int main ( void ) {

char radius = 2; // variables which can take on various values

float PI = 3.14 ; // constant PI given a value 3.14

float perimeter, area ;

perimeter = 2 * PI * radius ; // PI used as a variable

PI = 3.14159 ; // constant PI given a value 3.14159

area = PI * radius * radius ;

return ( 1 ) ;

}

The constant PI took on values of 3.14 once and 3.14159 the next time. So it is not a

constant after all.

To avoid different values for a variable, which we would like to keep constant and

prevent inadvertent use of a constant as a normal variable we declare the variable as

constant by prefixing the declaration of the variable with const.

The C reserved word const instructs the compiler to check your code and see that there is

only one value for the constant in the block it has been used and that it cannot be changed

Page 45: C4micros

45

by any statement or assignment in that block. It can only be used as is. It is a “read only”

variable.

Since a constant is constant and the compiler would refuse to allow you to assign it a

value, the only way to assign a value to a constant is the declaration cum assignment

statement at the beginning of a block.

Our code then becomes:

int main ( void ) {

char radius = 2; const float PI = 3.14 ; // constant PI given a value 3.14

float perimeter, area ;

perimeter = 2 * PI * radius ; // PI used as a variable

// PI = 3.14159 ; // constant PI given a value 3.14159, error!

area = PI * radius * radius ;

return ( 1 ) ;

}

Where is a variable declared as const stored? In ROM or RAM?

Check your compiler.

If it is stored in RAM, can its value ever change?

Yes! The code cannot change the value and it has to be used with the value in it.

If there is access to that variable externally, for example if the variable represents an

input port, the value can definitely change! And we thought it was constant!

Beware! It can also be changed by the use of “pointers”!

Now what in Heaven’s name is that?

Only constants stored in ROM are ABSLOUTELY CONSTANT and cannot change!

There can be constant chars, ints or floats.

It may be preferable to locate constants, for example a table of values in ROM. Check

your compiler on how this may be done.

Page 46: C4micros

46

Concept 20 - #define constants

There is another way to define constants, using the #define preprocessor command.

Any command with a # prefix is a preprocessor command indicating some processing to

be done before actual compilation of code.

The syntax is #define A B where A is the name of the constant and B is the value

assigned to it. As it is not an instruction or statement, it does not end with a semicolon.

B can be a char, an integer, a long or a floating-point number.

They are generally declared before the “main” function.

Before compilation, all instances of A are replaced with B.

The constants are hard coded or located in ROM.

Our earlier program would now become:

#define PI 3.14

int main ( void ) {

char radius = 2;

float perimeter, area ;

perimeter = 2 * PI * radius ; // PI is replaced by 3.14 before code

// compilation

area = PI * radius * radius ;

return ( 1 ) ;

}

Our rules for naming constants:

1. Use capital letters and underscores only.

2. Use representative names.

3. Expand constants into expressions to clearly indicate what is being done.

For example: #define SECONDS_PER_HOUR ( 60 * 60 ) is better than

#define SECONDS_PER_HOUR ( 3600 )

Caution: Beware of “implicit type conversions” when you expand a number into an

expression.

(60 * 60) may turn out to be 16 instead of 3600!

Now what the heck is “implicit type conversions”?

We shall learn about it later!

Page 47: C4micros

47

Let’s understand in more detail the way constants, coded by the #define statement are

stored and used.

Try this:

#define A 255 // integer,1 byte

#define B 65535 // integer, 2 bytes

#define C 4294967295 // integer, 4 bytes

#define D 4294967296 // integer, >4 bytes

# define E 4294967296. // a float, 4 bytes

#define F 4.294967296e9 // a float, 4 bytes

int main ( void ) {

char g = A ; // FF

int h = B ; // FFFF

long i = C ; // FFFFFFFF

float j = D ; // 0!

float k = E ; // 4.294967e9

float l = F ; // 4.294967e9

return ( 1 ) ;

}

So what we learn is that the maximum integer number that can be defined is 4294967295.

For defining larger numbers use floats, i.e., a number with a decimal point or a number

with an exponential. (e.g. 4e9).

Try this on a 16 bit compiler:

#define A 255 // Compiler allots 2 bytes

#define B 255L // L for long, Compiler allots 4 bytes

#define C 32768 // ? #define D 0x8000 // Compiler allots 4 bytes

int main ( void ) {

return (1 ) ;

}

Generally, the compiler allots 2 bytes to a number by default, i.e., a number is considered

as an integer any where it occurs in the program, not just in the #define statement.

You can force the compiler to treat a number as a long by the SUFFIX L or UL. (Long or

unsigned long.). Now why would you do that? “Implicit type conversions!”

Page 48: C4micros

48

Concept 21 – Enumerations

There is one more way to “declare” “integer” constants. Use the “reserved word” enum.

The syntax would be enum { a, b, c, d } ;

This would hard code in ROM the values of a, b, c and d.

The compiler would not allow change of these variables.

They have a “scope” and are visible only in the blocks they are declared.

They may be declared as local or global variables.

Avoid enumerations with the same names for the variables.

What values do these variables take?

If used as above, by default, the first variable a is assigned 0 and the others are

incremented by one over the previous variable. So b = 1, c = 2, and d = 3.

The variables can be assigned independent values as:

enum { a = 3, b, c = 8, d = 9 } ; What would b equal? One more than the previous value 3 i.e., 4.

Enumerations are generally useful when you require a series of consecutive numbers, for

example, if the days of a week or month names are to be represented by consecutive

numbers.

They can take only integer values like 0, 5 or – 6, - 9 etc. No floats!

More on Enumerations will be covered after we understand “structures”!

Now for some practice!

int main ( void ) {

enum { a = 3, b, c = -5, d }; // don’t forget the semicolon, it is a “declaration”!

char f ;

f = b + 4 ; // f = 8

f = f + d ; // f = 4

return ( 1 ) ;

}

Page 49: C4micros

49

Concept 22 – Volatile variables

Gosh what a lot of stuff to learn about variables! Couldn’t just char, int and float do?

Unfortunately no!

Let’s write a program:

int main ( void ) {

char a = 2, b = 3, c = 4 ; // assign values to a, b and c

c = a + b ; // c is expected to become 5

c = a * b ; // c is expected to become 6

return ( 1 ) ;

}

Check your compiler options and compile with and without any code optimization.

With a high level of code optimization selected, you would be surprised that our code

doesn’t run at all! The compiler thinks we are wasting our time and does not do anything

we expect!

Now let’s rewrite the program as follows, and compile with the highest level of

optimization possible:

int main ( void ) {

volatile char a = 2, b = 3, c = 4 ; // assign values to a, b and c

c = a + b ; // c is expected to become 5

c = a * b ; // c is expected to become 6

return ( 1 ) ;

}

The program runs on expected lines!

The C “reserved word” volatile tells the compiler NOT to touch any code related to the

variables declared volatile and run it without any optimization.

As a general rule, run your code without any optimization and after you are satisfied with

the output, go for optimization if required. While going in for optimization, declare

variables like time from RTC or port inputs, etc as volatile or your code may go haywire!

Page 50: C4micros

50

Try this with various levels of optimization:

int main ( void ) {

char a = 2, b = 3, c = 4 ; // assign values to a, b and c

c = a + b ; // c is expected to become 5

// we have a delay of 5 minutes. a is port 1 and b is port 2 and they change to 3 & 4

// respectively

c = a + b ; // c is expected to become 7

return ( 1 ) ;

}

Any luck? Does c become 7?

Now try this with the highest level of optimization:

int main ( void ) {

volatile char a = 2, b = 3, c = 4 ; // assign values to a, b and c

c = a + b ; // c becomes 5

// Change a to 3 & b to 4 respectively in single step mode

c = a + b ; // c becomes 7

return ( 1 ) ;

}

Hope you got it!

What does a declaration like const volatile a ; mean? It means the variable a cannot be

modified by code. It is “read only”. It also means the value of a may be different at

different times and hence the compiler should not try to optimize code but load the values

as stated in code.

Page 51: C4micros

51

Concept 23 – Macros

Now that we have seen enough of “functions” and “variables”, let’s see what “macros”

are. Macros are similar to functions in that they may be required to do some “jobs” quite

a few times. The main difference is that “macros” produce “inline” code and the code is

repeated every time the “macro” is used. In a “function”, the code is written at one place

in ROM and “called” as many times as required.

The syntax for a “macro” is #define A( b, c ) ( C )

where A is the “macro” name in capital letters, b, c are the parameters enclosed in

brackets, and C is the required operation or operations enclosed in brackets. Generally,

every parameter in C would again be enclosed in brackets. It is imperative there be no

“space” between A and the opening bracket of the parameter list.

#define is a preprocessor command and the compiler just replaces all instances of the

“macro” A( x, y ), or A( a, b ) etc by C. C may be an expression like ( ( a ) + ( b ) ) etc.

x, y may be char, int or float as it is just text replacement and no declarations are done.

Time for some examples:

#define SQ( a ) ( ( a ) * ( a ) ) // square of a

int main ( void ) {

char a = 2, c ;

float d;

c = SQ ( a ) ; // 4

d = SQ ( 2.5 ) ; // 6.25

c = SQ ( 2+3 ) ; // 25

return ( 1 ) ; }

The importance of so many brackets:

#define SQ( a ) ( a * a ) // square of a, no brackets!

int main ( void ) {

char c;

c = SQ ( 2+3 ) ; // 11! See the preprocessor list file to find out why!

return ( 1 ) ; }

Page 52: C4micros

52

Concept 24 – Integer Storage

Let’s understand where and how the different types of integers are stored in a micro

controller.

An unsigned character is a 8 bit integer and can take values from 0 to 255.

8 bits or 1 byte is allocated to the character.

00000000 would represent 0, and 11111111 would represent 255. Simple!

Try this:

int main ( void ) {

unsigned char a = 30, b = 300, c = -30, d = - 300 ;

return ( 1 ) ;

}

What values are stored at locations a to d?

As only binary (hex) values can reside in the micro controller, the values put in the

locations by the compiler would be:

a = 0x1E, b = 0x2C, c = 0x E2, d = 0xD4.

How did we get these values? Use the scientific calculator, type in the decimal values

with the sign and convert to hex with byte selected.

Reconverting to decimal using the calculator, the values are:

a = 30 We put in 30

b = 44 We put in 300

c = 226 We put in -30

d = 212 We put in –300

And the compiler too has these values at these locations!

A signed character is a 8 bit integer and can take values from -128 to +127.

8 bits or 1 byte is allocated to the character.

The MSB represents the sign:

0 indicating a positive number and 1 indicating a negative number.

So the maximum positive number would be 01111111 or + 127.

Imagine a binary odometer with 8 bits.

00000000 would indicate 0.

00000001 would indicate 1.

Page 53: C4micros

53

11111111 would indicate the next number would be 00000000

or can be thought of as – 1.

What would 10000000 represent? You guessed it! – 128!

So how do we figure this out?

Negative numbers are represented in 2’s complement form.

If we have a signed char, and if the MSB is 1 put the minus sign -.

Now invert all 8 bits or take the complement of the byte (1’s complement).

Add 1 to the LSB. This gives the magnitude of the negative number.

So Let’s check if we have really understood this.

A character will be allocated 8 bits or 1 byte by the compiler.

If we declare it as signed char, the number in the byte would be treated as signed by the

compiler and can only represent the number in the range –128 to +127.

Try this and see what your compiler thinks or treats these variables as:

int main ( void ) {

signed char a = 100, b = -100 ; // Within the correct range

signed char c = 150, d = -150 ; // Out of signed range, but within byte range

signed char e = 257, f = -257 ; // Fully out of range

return ( 1 ) ;

}

As only binary (hex) values can reside in the micro controller, the values put in the

locations by the compiler would be:

a = 0x64, b = 0x9C, c = 0x 96, d = 0x6A, e = 0x01, f = 0xFF.

Converted to binary, the numbers are:

a = 0110 0100

b = 1001 1100

c = 1001 0110

d = 0110 1010

e = 0000 0001

f = 1111 1111

The MSB of a is 0 and hence the compiler treats a as decimal 100. a = 100

The MSB of b is 1 and hence the compiler treats b as decimal -100. b = -100

The MSB of c is 1 and hence the compiler treats c as decimal -106. c = 150

The MSB of d is 0 and hence the compiler treats d as decimal 106. d = -150

Page 54: C4micros

54

The MSB of e is 0 and hence the compiler treats e as decimal 1. e = 257

The MSB of f is 1 and hence the compiler treats f as decimal -1. f = –257

So be careful when you allot storage.

Be sure that your numbers fall within specified range. Or the compiler might play some

strange tricks! Be specific, stating signed or unsigned, or be sure to check what your

compiler takes as default.

Integers are stored similarly in two bytes and longs in 4 bytes.

To summarize, the ranges of the various integer types are:

signed char -128 to +128 unsigned char 0 to 255

signed int -32768 to 32767 unsigned int 0 to 65535

signed long -268435456 to 2684354455 unsigned long 0 to 4294967295

Page 55: C4micros

55

Concept 25 – Float Storage

How are floating-point numbers stored? How many bytes do they require? What about

their range? Can they store both positive and negative numbers? What is their precision?

A lot of questions! A floating point number is one with a decimal point. Since the

decimal point moves or floats for representing different values, they are called floating

point numbers. Examples: 12.577, 1.12, 0.005 etc.

Floating point numbers are represented as (+ / - number x 2 +-exp

).

Thus it can represent very large positive or negative numbers( + exp) as well as very

small positive or negative numbers (-ve exp ).

Floating point numbers occupy 4 bytes, MSB (byte 0), byte 1, byte 2 and LSB (byte 3).

The MSB bit 7 represents sign information, 0 for a positive number and 1 for a negative

number.

The remaining 7 bits of the MSB and bit 7 of byte 1 represents the value of the exponent.

Subtracting 127 from the actual value obtains the exponent. As an 8 bit value can range

from 0 to 255, the exponent can take values from –127 to +128 .

Thus a float number can represent values of approx. 2 127

as against a long which can

only represent a value of 2 32

.

The remaining 23 bits are thought of as 1.b23 b22 …b0

The decimal point is then shifted left or right by the value of the exponent.

Left for a negative exponent, Right for a positive exponent.

Gosh! Imagine shifting by 128!

The number would now look something like:

1 b23 b22. b21 … b0, ……for an exp value of +2 or

0. 0 0 1 b23 b22 b21 … b0, ……for an exp value of -2

The value now represents a binary floating point number and can be found as:

( 1 x 22

) + ( b23 x 21

) + ( b22 x 20

) . ( b21 x 2-1

) + ( b20 x 2-2

) …. or

0. ( 0 x 2-1

) + ( 0 x 2-2

) + ( 1 x 2-3

) + ( b23 x 2-4

) + ( b22 x 2-5

) ….

There are three special values:

ZERO: Represented by sign bit being 0 or 1, all the other bits 0, (+0, -0).

INFINITY: Represented by sign bit being 0 or 1, exponent bits all 1,the remaining

bits 0,(+ ∞, -∞ )

NOT A NUMBER: Exponent bits 1, the remaining bits not all zero. Can you have an unsigned float? NO!

Page 56: C4micros

56

Concept 26 – Operators / Precedence

Operators “operate” on “operands” and the process is called an “operation”.

Easy eh?

Types of operators:

Unary: The operator has only one operand.

Binary: The operator has two operands.

Ternary: The operator has three operands.

Examples of binary operators are: +, -, *, /.

They require two operands:

c = a + b ; // = is an assignment operator, + is an arithmetic operator.

// a and b are the operands.

// c = a + b ; is an operation or expression.

The operators may also be classified as:

Assignment, Mathematical, Logical, etc.

If there is a long expression like:

a = b + c - d * e ;

What does the compiler do?!

To take care of such situations we have an “order of precedence” and the “associative”

rules .

Operators are “assigned” a priority. An operator with a higher priority gets executed first.

If two operators have the same priority the compiler goes from left to right and executes

the operations in sequence. This is “associativity”

The priorities for the arithmetic operators are:

* / // Equal priority, higher than + or -

+ - // Equal priority, lower than * or /

A parenthesis takes on the highest priority and hence it is wise to use parentheses

liberally to avoid confusions.

For b = 5, c = 4, d =3, e = 2,

The above statement would be executed as

a = ( b + c ) – ( d * e ) ; // Answer 3

Did you want?

a = b + (c – d ) * e ? // Answer 7

Page 57: C4micros

57

CCD and see what your compiler does for all the above expressions.

int main ( void ) {

char a, b = 5, c = 4, d = 3, e = 2 ;

a = b + c – d * e ; // Answer 3

a = b + ( c – d ) * e ; // Answer 7

a = ( b + c ) – ( d * e ) ; // Answer 3

return ( 1 ) ;

}

Table of operator precedence and associativity:

Operators Associativity () [] Left to right

-> . ++ (postfix) -- (postfix) Left to right

++ (prefix) -- (prefix)

! ~ sizeof (type)

+ (unary) - (unary) & (address) * (indirection)

Right to left

* / % Left to right

+ - Left to right

<< >> Left to right

< <= > >= Left to right

== != Left to right

& Left to right

^ Left to right

| Left to right

&& Left to right

|| Left to right

?: Right to left

= += -= *= /= %=

&= ^= |= <<= >>=

Right to left

, Left to right

Does it make sense?

Page 58: C4micros

58

Concept 27 –Integral Promotions

Consider:

unsigned char a, b, c ; // apples, apples and apples!

c = a + b ;

Do we really understand this statement?

Of course! It says a, b and c are unsigned characters, occupy a byte each and c = a+b!

Just a minute, if a and b are 255, the answer would be 510 and c should be an integer! So

let’s be wise and change the above to:

unsigned char b, c ; // apples and apples

unsigned int a ; // basket of apples!

c = a + b ;

So now we think we should get the right answer.

Go ahead, compile and check your answer. Make sure your compiler does not apply any

conversion rules.

int main ( void ) {

unsigned char a = 255, b = 255 ;

unsigned int c ;

c = a + b ; // c = 254 or 0x00FE!

return ( 1 ) ;

}

Wrong! Why?

If you thought we should get the right answer, we haven’t understood the “assignment”

operator.

Page 59: C4micros

59

Let’s understand the declarations and statement below more conceptually.

unsigned char a, b, c ;

c = a + b ;

There are actually three jobs done by the above two lines and not two as we would

intuitively guess!

Job 1- the declaration:

The declaration tells the compiler:

1. To name three locations (addresses) in RAM as a, b and c.

2. To allot 1 byte each for a, b and c.

3. Treat a, b and c as unsigned, i.e., consider the number stored in the bytes as positive

only. Hence a 0x00 in the byte would represent 0 and 0x FF would represent 255.

Job 2 – the addition:

a + b ; Surprise of Surprise! Involves two steps:

1. To add a and b.

2. To store the result in some temporary register!

Job 3 – the assignment:

c =

1.The contents of the temporary register are copied into c.

If the temporary register has 0xFE in it c will have 0xFE.

Now back to the problem. Since we have declared c as an unsigned int and it has 0x00FE

instead of the expected 0x 01FE, obviously the temporary register must have the wrong

value 0xFE in it! Why?

We have always been told apples + apples are apples and so has the compiler been told!

So a char + char would be a char and 1FE is truncated to FE!

c is not “equal” to ( a + b ), it is just “assigned” the value of ( a + b ).

Whatever is in ( a + b ) is put in c. If FE is in ( a + b ), c has FE, if 1FE is in ( a + b ), c

has 1FE. If c has been declared as an unsigned char it would have FE(254), if it has been

declared as a signed char it would still have FE, but be interpreted as ( -2 ).

Page 60: C4micros

60

Now ANSI C dictates that characters (signed or unsigned) be converted to integers

(signed) before any mathematical operations are performed. The result would also be

integers (signed).

This is “implicit type casting”

In other words apples = apples + apples.

As characters are promoted to integers, it is called “integral promotions”.

Or is it better termed “integer promotions”?

Try out the previous program with ANSI C “promotions” allowed.

Got it!

As characters are promoted to integers before any operation is performed, more code /

ram is required, and it is generally not wise to enable this feature for embedded use.

So how do we get the right answers? “Explicit type casting”!

Page 61: C4micros

61

Concept 28 - Type Conversion by Assignment

The assignment operator converts the type of the operand on its right to the type of the

operand on its left. This is also “implicit type casting”.

Slightly confused?

We are always working with the operator on the left. We first declare it to be of some

type. We then assign to it some expression or variable. Thus that expression or variable

would be converted to the type of the variable on the left.

int main (void ) {

float a, c = 2.5 ;

unsigned long b = 70000 ;

a = b ; // widening, ok

b = c ; // narrowing, decimal portion truncated

return ( 1 ) ;

}

int main (void ) {

unsigned long a, c = 70000 ;

unsigned int b = 700;

a = b ; // widening, ok

b = c ; // truncation of extra bytes

return ( 1 ) ; }

int main (void ) {

unsigned long a, c = 70000 ;

signed char b, d = -70;

a = d ; // wow! –70 = FFFFFFBA (check on cal as Dword with –70)

b = c ; // wow! 70000 = 11170 hex = 70 as a byte

return ( 1 ) ;

}

Page 62: C4micros

62

My! Isn’t it dangerous to mix signed and unsigned numbers!

A judicious use of assigning to a float or a signed larger number would solve the

problem.

int main (void ) {

unsigned long a, c = 70000 ;

signed char b, d = -70;

signed int e ;

float f, g ;

a = d ; // wow! –70 = FFFFFFBA (check on cal as Dword with –70)

b = c ; // wow! 70000 = 11170 hex = 70 as a byte

e = d ; // signed int can hold a signed char

f = d ; // a float can hold a signed number

g = c ; // a float can hold very large numbers.

return ( 1 ) ;

}

Page 63: C4micros

63

Concept 29 – Usual Arithmetic Conversions

The compiler can only work with operands of the same type and produce a result of the

same type.

Hence expressions like: 3 * 4.4 should get the compiler confused!

3 is an integer, 4.4 is a float, what should the answer be? Integer or Float? Float!

Consider: 3 + 2 / 4 . Is the answer 3 or 3.5? 3! Who says 2/4 is 0.5? It is 0!

So what’s going on? ANSI C specifies the compiler should follow certain rules when an

expression has mixed types like a char and an int, or an int and a float, or a signed int

and an unsigned int etc.

Operands of an operator (one for unary, two for binary and three for ternary) are

converted to a single type before any operations (mathematical, logical etc) are

performed.

In the first expression, 3 is “converted” to a float 3.0. 3 is “implicitly cast” as a “float”.

In the second expression, operands 2 and 4 are integers, hence integer division is

performed, 2/4 is converted to an integer, 0.

The “types” of the operands in a mixed expression are generally “promoted” to the

highest “type” in that expression. This avoids loss of information.

The rules simplified are:

1. If any operand is of type float, the result is of type float.

Otherwise “integral promotions” are carried out as follows:

1. If any operand is of type unsigned long, the result is of type unsigned long.

2. If any operand is of type long, the result is of type long.

3. If any operand is of type unsigned int, the result is of type unsigned int.

Otherwise the result is of type int.

Here long and int are signed by default.

Let’s understand the above rules for an expression having only one binary operator with

two operands and later move on to a bigger expression where we have to also deal with

“associativity”.

Page 64: C4micros

64

1. If any operand is of type float, the result is of type float.

int main ( void ) {

float a, b ; // Let’s assign the answers to a float

a = 4.4 * 2 ; // 2 will be promoted to a float 2.0

b = 4.1 * 2.2 ; // both are float

return (1 ) ;

}

We get the right answers for both expressions.

2. If any operand is of type unsigned long, the result is of type unsigned long.

int main (void ) {

float a,b,c ; // a float can hold any thing right!

unsigned long d;

signed long e ;

a = 70000 * 70000 ; // unsigned long * unsigned long = unsigned

// long = 605032704!

b = 70000 * 7 ; // 7 promoted to long and multiplied = 490,000

c = 70000 * (-7) ; // -7 promoted to long and multiplied = - 490,000

d = 70000 * (-7) ; // -7 promoted to long and multiplied =

// 0xFFF88F50 considered as 4294477296

e = 70000 * (-7) ; //-7 promoted to long and multiplied =

// 0xFFF88F50 considered as – 490, 000

return ( 1 ) ;

}

Page 65: C4micros

65

A signed type is promoted to a larger unsigned type by:

If the number is in HEX:

1. Padding the number with zeros to the left for a positive number.

2. Padding the number with Fs to the left for a negative number.

If the number is in Decimal:

1. Convert the number to HEX and pad the number with zeros to the left for a

positive number.

2. Convert the number to HEX and pad the number with Fs to the left for a negative

number.(Type the number with sign and convert to hex using the calculator on

your PC) OR convert magnitude to HEX, take 2’s complement and pad with Fs).

I guess we got it now! So let’s skip the rest of the rules and proceed to expressions with

more than one arithmetic operator.

The compiler follows an order of precedence and associativity as indicated in the table in

the concept on operators’ precedence. The type casting or promotion is done operation by

operation in sequence.

Consider:

int main( void ) {

float a, b, c, d ; // Easy to represent the right answers

a = 2 / 3 * 4.5 ; // 0!

b = ( 2 / 3 ) * 4.5 ; // 0 again!

c = 2.0 / 3 * 4.5 ; // Right at last!

d = 4.5 * 2 / 3 ; // Right again!

return ( 1 ) ;

}

The compiler starts from left and proceeds to the right operation by operation as / and *

have the same priority.

a) Both operands of / are integers and hence the integer result is 0, and 0 * anything is 0!

b) Same again.

c) 2.0 is float, float / float is float .float * float is float and the answer is correct.

d) float *float is float. float / integer is float and the answer is still correct.

Page 66: C4micros

66

Concept 30 – Explicit Type Conversion

In Embedded systems, especially if we are dealing with 8 bit micro controllers, we should

be dealing a lot with chars and avoid integers and longs to save on RAM and lengthy

code. We should avoid “integral promotions”. How?

Consider:

int main ( void ) {

unsigned char a = 255, b = 255 ;

unsigned int c ;

c = a + b ; // c = 254 or 0x00FE!

return ( 1 ) ;

}

Compile and debug without integral promotions.

Now try this:

int main ( void ) {

unsigned char a = 255, b = 255 ;

unsigned int c ;

c = ( int ) a + b ; // c = 510 or 0x01FE!

return ( 1 ) ;

}

By prefixing a with ( int ) , we get the right answer. We have requested the compiler to

consider a as an integer while evaluating this expression. This is “explicit type

conversion”.

The syntax is ( A ) B where A is the “new type” required while evaluating a particular

expression. It is enclosed in brackets and prefixed to B, the variable name which has been

already declared as a different type.

( A ) or a type name enclosed in brackets is also called a “cast operator”

Page 67: C4micros

67

Another example where an explicit type cast would be required:

int main ( void ) {

int a =3, b = 4 ;

float c ;

c = a / b ;

return ( 1 ) ;

}

The above would give wrong results and can be corrected by explicit type casting as:

int main ( void ) {

int a =3, b = 4 ;

float c ;

c = ( float ) a / b ;

return ( 1 ) ;

}

Ha! You got it! Remember we did some explicit type conversions when we wrote 2 / 3 as

2.0 / 3 sometime back?

Page 68: C4micros

68

Concept 31 – Pointers cracked!

We have covered a lot of basics and it is time to take on what most people are said to

fear, pointers!

The “types” of variables we have seen so far are chars, ints and floats. They contain small

integers, big integers and floating-point numbers, which can represent almost anything!

So what more do we want?

We introduce a new “type” of variable called “pointers”. Now what do they hold?

They hold addresses of, any of the other type of variables discussed, i.e., chars, ints or

floats.

Time for some pictures again. A representation of the memory in a micro controller

initially would be something like this:

Byte

address: 00 01 02 03 04 05 06

Value

Where XX is some junk value, or some random value, or uninitialised value.

After a declaration (definition) like:

char a ;

int b ;

the representation would be:

Byte

address: 00 01 02 03 04 05 06

Value:

Name: a b

Type: char int

Where XX is some junk value, or some random value, or uninitialised value.

Finally and only after initialization like:

a = 0xFF ;

b = 0xFEFE ;

the representation would become:

XX XX XX XX XX XX XX

XX XX XX XX XX XX XX

Page 69: C4micros

69

Byte

address: 00 01 02 03 04 05 06

Value:

Name: a b

Type: char int

We can see a variable has 4 attributes to it!

1. A name.

2. An address.

3. A type, or length, or the number of bytes required to represent it.

4. A value.

We give the variable, it’s name.

The compiler allots it’s address.

We declare the type and the compiler allots the required number of bytes.

We initialize the values of the variables.

The micro controller and the compiler work only with addresses and not names.

So let’s be a little curious and see what addresses the compiler allots to our variables.

We know the addresses have to be positive integers only.

The symbol & in front of a variable name gives away the address of the variable.

So let’ code and see:

int main ( void ) {

char a = 5 ; unsigned long b ; // let’s be safe!

b = &a ; // put the address of a in b.

return ( 1 ) ;

}

Ho! So there is an address for each and every variable we create! And we can get it by

just prefixing the variable name with &!

Now let’s see how we can change the value of the variable using it’s address!

FF FE FE XX XX XX XX

Page 70: C4micros

70

To do this we require to use pointers. As already mentioned, pointers are just another

“type” of variables. So there must be a way of declaring them.

A * is used to indicate a pointer, and is suffixed to the type of variable it points to.

Thus our new type is char* and

char* add_of_a ;

declares a pointer with the name add_of_a and points to a char.

Those of us who have worked with the 8051 assembly language find it easy to symbolize

or think of * as @ (at) or better still (at the address in).

Thus the above declaration could be read as: char at add_of_a (character at address of a)!

Now let’s conveniently name a character as a by the declaration:

char a ;

So we now have two declarations and both have random values in them.

So what should we put in them?

You guessed it! The address of a in add_of_a! And what is the address of a?

&a!

So we initialize the pointer variable as:

add_of_a = &a ; // apples matched to apples!

We know addresses are integer numbers and we can put the address 9 in add_of_a as:

add_of_a = 9 ;

Can we assign it a floating point number or a variable without the &?

The syntax to get at the value stored at the address in the pointer is:

*A where A is the name of the pointer.

We can read it as at A!

Thus :

*add_of_a = 5 ; // assigns 5 to a

If we had another char b, we could assign a to b by:

Page 71: C4micros

71

b = a ; // the normal way or

b = *add_of_a ; // the pointer way!

Let’s consolidate:

int main ( void ) {

char a, b, c ; char* add_of_a = &a ; // safer to initialize at once!

*add_of_a = 5 ; // assigns 5 to a

b = a ; // b = 5, the normal way or

c = *add_of_a ; // c = 5, the pointer way!

return ( 1 ) ; }

The rules we shall follow for pointers:

1. Prefix the name of the variable for which we require a pointer with add_of_

2. Initialize as: add_of_A = &A, by replacing add_of_ with &

The picture now is:

Byte

address: 00 01 02 03 04 05 06

Value:

Name: a b add_of_a

Type: char int char*

*add_of_a is nothing but a! At the address of a ( 00) we have a naturally! At the

address of Tom, can we have Dick?

Two or more pointers are declared as: char* ptr1, *ptr2 ; A * has to be prefixed to each

pointer name. In a declaration like: char* ptr1, ptr2 ; ptr2 will be considered as a char.

An int a would be cast as a pointer if written as ( int* ) a. Read int* as integer pointer.

FF FE FE XX 00 XX XX

Page 72: C4micros

72

Replace add_of_a with add_of_var if you would like the pointer pointing to different

variables at different times.

Does a pointer have an address? Yes. So we can have a pointer to a pointer!

Now things can get out of hand. We would have to name the new pointer as add_of_

add_of a! and how do we declare it?

Well! We have understood the concepts, so let’s change strategies and make things

simple!

We have always declared a variable as A B ;

where A is the type and B is the name of the variable.

A table of various types would be in order:

Types Read as

char character

int integer

float float

* pointer to a

char* pointer to a character

int* pointer to a integer

float* pointer to a float

char** (char*)* : pointer to a ( pointer to a char )

int** (int*)* : pointer to a ( pointer to a int )

float** (float*)* : pointer to a ( pointer to a float )

Notice we are reading from RIGHT to LEFT!

We shall now change our rule of naming pointers to:

PB where P stands for pointer and B is the name of the variable to which we are

attaching a pointer. It will be initialized with the address of B using PB = &B ;

( P replaced with &).

A small table on naming the variables would help:

Variable Name Read as

b b

Pb pointer to b

PPb pointer to a pointer to b

Page 73: C4micros

73

Combining the two we have a declaration table:

Declaration Read as

char b b is a character

char* Pb Pb is a pointer to a character

char** PPb PPb is a pointer to a pointer to a character

Notice the number of *s are equal to the number of Ps, and we are reading from RIGHT

to LEFT!

We will now require an initialization table for our pointers:

Pointer Name Initialisation

Pb &b

PPb &Pb

Notice the first P is replaced with &.

And finally a table to understand the various assignments possible:

Pointer Name Assignments possible Variable Affected / Read as

Pb *Pb b

PPb *PPb Pb

**PPb b

Notice the number of assignments possible are equal to the number of Ps in the pointer

name.

Notice the variable affected is obtained by deleting the stars and an equal number of Ps!

Now Let’s write a small program illustrating what we have learnt!

int main ( void ) {

char a, b, c, d ; // declarations / definitions

char* Pa ;

char** PPa ;

a = 5 ; // initialisations

Pa = &a ;

PPa = &Pa ;

b = *Pa ; // assignments

c = *PPa;

d = **PPa;

Page 74: C4micros

74

return ( 1 ) ; }

So what can we do with all this information?

We can change a variable either directly by assignment or indirectly through pointers!

Let’s try to assign 5 directly and 6 indirectly to a variable a.

int main ( void ) {

char a = 5 ;

char* Pa = &a ;

*Pa = 6 ; // Read as a = 6 ;

return ( 1 ) ;

}

So why should we ever want to do something like that?

We saw, with functions only value could be returned to main.

All other variables including the parameters passed were created on entry and

destroyed on exit. The input parameters created are copies of the input variables

passed. This is termed “pass by value”.

A way to circumvent copies of input parameters being made and change them directly

is called “pass by reference”. Reference means address reference of the variables

passed. Now that strikes a note! Address = pointers!

So a lot of code and time can be saved in embedded systems by using this feature of

“pass by reference”. So now we know why pointers are so important!

So how do we do it?

We pass the references (addresses) of the variables instead of the variables

themselves.

Let’s illustrate with the famous “swap” example.

swap ( char*, char* ) ; // function prototype

int main ( void ) {

char a = 5, b = 6 ;

swap ( &a, &b ) ; // call by reference. Give an address only.

Page 75: C4micros

75

return ( 1 ) ; }

swap ( char* Pa, char* Pb ) // function definition

{

char temp ;

temp = *Pa ; // read as temp = a ;

*Pa = *Pb ; // read as a = b ;

*Pb = temp ; // read as b = temp ;

return ; // just return!

}

Time for some pictures.

Byte

address: 30 31 32 33 34 35 36

Value:

Name: Pa Pb

Type: char* int*

Name two pointers Pa, a pointer to a char and Pb a pointer to an int .

Locate them at addresses 30 and 35 respectively.

Put values 4 and 5 in them respectively.

Try out some pointer arithmetic:

int main ( void ) {

char* Pa = 30 ; // Name address 30 as Pa (char)

int* Pb = 35 ; // Name address 35,36 as Pb (int )

*Pa = 4 ; // at Pa (add. 30) fill with 04 (char) – 1 byte

*Pb = 5 ; // at Pb (add. 35,36) fill with 00 05 (int) – 2 bytes

*( Pa + 1 ) = 4 ; // at (30+1 char or 1 byte = 31) fill with 04

*( Pa + 2 ) = 4 ; // at (30+2 = 32) fill with 04

*( Pb - 1 ) = 5 ; // at (35-1 int or 2 bytes = 33) fill with 00 05

04 04 04 00 05 00 05

Page 76: C4micros

76

return ( 1 ) ; }

Character pointers + 1 would increment the address by 1 byte.

Integer pointers + 1 would increment the address by 2 bytes.

Long or float pointers + 1 would increment the address by 4 bytes.

The difference between two character pointers would indicate the number of characters

between them.

The difference between two integer pointers would indicate the number of integers

between them.

Thus pointers may be compared to fill in or read a series of values.

Note:

Numbers (int or float) and addresses can be assigned to pointers.

char or ints, or floats cannot be assigned to pointers. The compiler would catch them.

pointers (addresses) can be assigned to chars, ints or floats. After all addresses are just

numbers.

Try:

int main ( void ) {

char a ; // variable a

char* Pa ; // a pointer to a character, name - Pa

a = 5 ; Pa = 12 ; // Pa is assigned the address 12

*Pa = 6 ; // at 12 = 6

Pa = &a ; // Pa is assigned the address of a

Pa = a ; // get’s caught!

a =Pa ; // a is assigned the address of a

return ( 1 ) ;

}

Page 77: C4micros

77

Concept 32 – Arrays

So far, we dealt with single variables, example char a, b ;

The compiler is free to locate them at random locations. In the figure below, they have

been located at addresses 31 and 35.

Byte

address: 30 31 32 33 34 35 36

Value:

Name: a b

Type: char char

At times we may require loading values in a sequence for fast action and retrieval. Would

you like to name twelve variables for the months in a year? It would be much more easier

to say month 1, month 2 etc., and name it month [X], where X could vary from 1 to 12.

Yes! The inventors thought of it and so we have a new type called “arrays”.

The syntax is A B [X] ; where A is the type of the variables to be kept in the array, a

char, int or float. B is the name of the array and X is the number of elements in the array.

X is enclosed in square brackets.

Read the declaration as, B is an array of X As.

for e.g., char months [12] ; will be read as months is an array of ( [ ] )12 characters.

Can you notice the reading is still from right to left?

[ ] is read as “an array of ”,

months is read as “months is”

months [ ] is read as “months is” “an array of ”,

char months [ ] is read as “months is ” “an array of ” “chars” and finally

char months [12] ; is read as “months is” “an array of ” “12” “chars”!

First locate the name, and then identify what it is by looking right or left, then move left!

Now let’s see how the compiler deals with such a declaration.

It creates a new variable, calls it months (the name of the array) and puts the address of

the first element months [0] in it. Yes, it is months [0] and not months [1], who said 1

comes first? 0 comes first!

As the new variable named months (the name of the array) has an address in it, It is a

pointer!

The compiler next allots 12 [X] “consecutive spaces”(may be bytes, words or double

words depending on the array type being declared as char, int or float,) from the starting

address located in months (the name of the array). The spaces are named months [0],

months [1]… up to months [11].

XX XX XX XX XX XX XX

Page 78: C4micros

78

Notice the first element is always [0] and the last is [X-1].

An array months [6] would look something like:

Byte

address: 30 31 32 33 34 35 36

Value:

Name: months [0] months [1] months [2] months [3] months [4] months [5]

Type: char

Element 1 2 3 4 5 6

Notice months = &months [0] = 30.

Notice the index or subscript of the array varies from 0 to 5.

Notice the elements have junk value in them.

So let’s see how to initialize an array.

If we require to initialize all values to 0, we write char months [6] = {0} ;

If we require initializing the first two values to 1, 2 and the rest to 0, we write:

char months [6] = {1,2} ; If we require to initialize all 6 values we write: char months [6] = {1,2,3,4,5,6} ;

We can also write it as: char months [ ] = {1,2,3,4,5,6} ;

The compiler counts the number of elements and allocates the size of the array.

Notice initialization should be combined with declaration, and cannot be done separately.

Notice initialization is by using curly braces.

Access to the various elements is by just using their names and assigning values to them.

Consider:

int main ( void ) {

char months [12] ; // months is an array of 12 characters

months [0] = 1 ; // The first month is 1

months [1] = 2 ; // the next month is 2

months [11] = 12 ; // and the last is 12

return ( 1 ) ;

}

XX XX XX XX XX XX XX

Page 79: C4micros

79

A declaration of an array has the number of elements in that array as the index [X].

An index elsewhere represents the X+1 th

element.

If you require accessing an element A, use A-1 as the index.

Ok, let’s find out where our array is stored.

int main ( void ) {

char months [12] ; // months is an array of 12 characters

char a ; // a is a character. Let’s store the start address in it.

a = months ; // months has the start address in it and is put into a.

return ( 1 ) ;

}

You will find &months [0] giving the same result.

What happens when we pass an array into a function? Will a copy of the array be made or

will the original array be modified? Is it call by value or by reference? To understand this

let’s try out:

char triple ( char [ ] ) ; // Read as “triple is” “a function taking” ( ( ) ) “an array of

// chars”( [ ] ) as input and returning a char

int main ( void ) {

char months [1] = {5} ; // months is an array of 1 char.

triple ( months ) ; // input the name of the array

return ( 1 ) ;

}

char triple ( char b [] ) {

b[0] = 3* b[0] ; // remember the 1st element is 1-1 = 0!

return ( 1 ) ;

}

Page 80: C4micros

80

So it is “call” by reference! We saw previously, the name of the array is nothing but the

address of the first element of the array, thus we are passing an address (pointer) into the

function. So what should we expect? Naturally a call by reference!

A string is an array of alphabets and may be stored as:

char alphabets [ 3 ] = {‘a’, ‘b’, ‘c’} ; or

char alphabets [ 3 ] = {“abc”} ;

It is good practice to use a variable for the index instead of a number as in:

#define A 3

char months [A] ;

If the value of A has to be modified, it needs to be done only once.

Page 81: C4micros

81

Concept 33 – Sizeof operator

There is a very useful operator called sizeof in C. It is a keyword and its use gives us the

number of bytes a variable, expression or type occupies.

Try this:

int main ( void ) {

char a, e, f, g, h ;

int b ;

float c ;

char d[5] ;

a = sizeof ( a ) ; // a is a character of 1 byte

b = sizeof ( b ) ; // b is an integer of 2 bytes

c = sizeof ( c ) ; // c is a float of 4 bytes

e = sizeof ( d ) ; // d is a character array of 5 bytes

f = sizeof ( char ) ; // a char in this machine is 1 byte

g = sizeof ( int ) ; // an int in this machine is 2 bytes

h = sizeof ( float ) ; // a float in this machine is 4 bytes

return ( 1 ) ;

}

Page 82: C4micros

82

Concept 34 – Structures

Quite often, it would be required to group items, which may or may not be of the same

type. Consider we have to display the date (dd: mm: yy) at any given time. We can

declare the required variables as:

char day ; // days are less than 255

char month ; // months are less than 255

int year ; // greater than 255

Now imagine, we require storing 3 dates. A declaration could be:

char day1, day2, day3 ;

month1, month2, month3 ;

int year1, year2, year3 ;

or

char day[3] ;

char month[3] ;

int year[3] ;

Isn’t it going to be tedious to get the individual dates? Wouldn’t be nicer to group day1,

month1 and year1 as a single item and say call it date1? i.e., (group dd: mm: yy) as a

single item? Yes, it can be done using structures. So that’s the new “type” we are going

to learn about now.

To define a structure we have to use the “key word” struct.

A definition of a “new type” would be:

struct // keyword

{ // open a curly brace

char day ; // fill in required basic types

char month ;

int year ; } // close the curly brace

Our declarations so far have always had a format:

A B ; where A is the “type” and B the name of the variable.

Well, it is the same here, so we have:

struct

{

char day ;

char month ;

int year ;

Page 83: C4micros

83

} date1 ;

Is it easier to write as struct {char day ; char month ; int year ;} date1 ; ?

This creates a “struct variable” with name date1 and is allotted a byte each for variables

named day and month and a word for a variable named year. Go check it out.

For our 3 dates, we can declare as :

struct {char day ; char month ; int year ;} date1, date2, date3 ;

Fine! We can see every structure will be different and have different basic types in them.

So why not name the structures as structure A, structure B etc where A could be in our

case, {char day ; char month ; int year ;} ? Yes, it can be done! So we have:

struct date {char day ; char month ; int year ;} ;

This defines a “new type” struct date, yes unfortunately it has two words and not one

like we are used to so far like char, int etc.

Yes, this defines a prototype for the structure and don’t prototypes require a semicolon?

So now a structure declaration would consist of two steps:

1. Define a prototype. (Naming and defining the type of structure.)

2. Creating variables of that type.( A B ; )

Our structure could now be declared as:

struct date {char day ; char month ; int year ;} ; // prototype of type struct date

struct date date1, date2, date3 ; // create 3 variables of type struct date

struct date is the “new type” and date is the “tag” of the structure.

For clarity, the above steps are generally written as:

struct date

{

char day ;

char month ;

int year ;

} ;

struct date date1, date2, date3 ;

Page 84: C4micros

84

The above two steps may be combined to form:

struct date

{

char day ;

char month ;

int year ;

} date1, date2, date3 ;

Initialization is exactly like in arrays:

struct date {

char day ;

char month ;

int year ; } ;

int main ( void ) {

struct date date1 = { 0 } ; // all members made 0

struct date date2 = { 9 } ; // first member 9, others = 0

struct date date3 = { 9, 8, 7 } ; // all fields initialized

return ( 1 ) ;

}

Now how do we access the “internal variables” i.e., day, month and year of each

structure variable?

We do it with the . (Dot) operator as:

We get the day month and year of date1 as:

date1.day

date1.month

date1.year

Try:

struct date

{

char day ;

char month ;

int year ;

Page 85: C4micros

85

} ;

int main ( void ) {

char a, b ;

int c ;

struct date date1 ;

date1.day = 9 ;

date1.month = 9 ;

date1.year = 9 ;

a = date1.day + 1 ;

b = date1.month – 1 ;

c = date1.year + 2000 ;

return ( 1 ) ;

}

Let’s checkout the size and location of our structure:

struct date // prototype

{

char day ;

char month ;

int year ;

} ;

int main ( void ) {

struct date date1 ; // declare date1

char a, b, c, d, e ;

a = &date1 ; // start address of structure date1

b = &date1.day ; // address of day

c = &date1.month ;

d = &date1.year ;

e = sizeof ( date1 ) ; // total space allotted for the structure

return ( 1 ) ;

}

Page 86: C4micros

86

The total space may be equal to or greater than the sum of the sizes of the individual

types if some padding has been done.

OK now your boss says 3 dates? Oh No! I want 10!

Well, we go for an array of structures. Give him 20!

struct date // prototype

{

char day ;

char month ;

int year ;

} ;

int main ( void ) {

struct date dates[20] ; // an array of 20 structures

dates[19].day = 9 ; // the last structure is 19 not 20!

dates[19].month = 8 ;

dates[19].year = 7 ;

return ( 1 ) ;

}

Structures or structure members may be passed into functions.

It is pass by value and copies are made.

char add ( char, char ) ; // prototype

struct date // prototype

{

char day ;

char month ;

int year ;

} ;

int main ( void ) {

struct date date1 = { 9, 8, 7 } ;

char a, b, c ;

a = add ( date1.day, 2 ) ; b = &a ; // address of a

c = &date1.day ; // add. of day is not = add.of a, pass by value

Page 87: C4micros

87

return ( 1 ) ; }

char add ( char a, char b) {

return ( a + b ) ;

}

Here is how an entire structure is passed:

struct date // prototype of structure

{

char day ;

char month ;

int year ;

} ; struct date add ( struct date, char ) ; // prototype of function

int main ( void ) {

struct date date1 = { 9, 8, 7 } ; struct date date2 ; // stores the returned value

date2 = add ( date1, 2 ) ; // 2 is added to a copy of date1, date1 remains

// unchanged. The changed copy is assigned to

// date2

return ( 1 ) ; }

struct date add ( struct date datea, char a) {

datea.day = datea.day + a ;

datea.month = datea.month + a ;

datea.year = datea.year + a ;

return ( datea ) ;

}

Page 88: C4micros

88

Now do we want to pass by value? Not in an embedded system where memory may be in

short supply. So we require pointers to do our job.

We saw pointers to types like char, int etc. Now we require a pointer to type structure.

So we have a declaration like:

struct date* PSdate ; // read as PSdate is a pointer to structure date.

Next is naturally initialization, or allotting the address of a variable of type struct date, in

this case:

PSdate = &date1 ; // PSdate is assigned the address of date1

We can now access the various members of struct variable date1with:

PSdate -> day or PSdate -> month or PSdate -> year or (*PSdate).day etc.

The brackets are required as . has a higher precedence.

Try:

struct date // prototype of structure

{

char day ;

char month ;

int year ;

} ;

int main ( void ) {

struct date date1 = { 9, 8, 7 } ; // variable named date1

struct date* PSdate ; // pointer to struct date

PSdate = &date1 ; // PSdate points to date1

PSdate -> day = 6 ; // change day from 9 to 6

date1.month = PSdate -> day ; // change month to 6

return ( 1 ) ; }

Page 89: C4micros

89

Here’s passing structures by reference:

char add ( struct date*, char ) ; // prototype of function with pointer type as input

struct date // prototype of the structure

{

char day ;

char month ;

int year ;

} ;

int main ( void ) {

struct date date1 = { 9, 8, 7 } ; // structure date1 initialized

add ( &date1, 2 ) ; // address of structure date1 passed

return ( 1 ) ; }

char add ( struct date*PS, char b) // local variable names added in prototype

{

PS -> day =(PS->day + b ) ; // changes done and assigned

return ( 1 ) ; }

Unions have the same structure as a structure! They are obtained by replacing the name

struct with union. All the elements of an union have the same starting address. Thus

while a struct having a char and int would occupy 3 bytes, an union would have only two

bytes! So beware while using unions. They may be used in mutually exclusive situations

like day or night, or a traffic signal going red or green.

Try:

int main ( void )

{

struct { char a ; int b ; } str ;

union { char c ; int d ; }uni ;

char a, b ;

a = sizeof ( str ) ;

b = sizeof (uni ) ;

return ( 1 ) ;

}

Page 90: C4micros

90

Concept 35 – Preprocessor commands

Let’s understand some preprocessor commands or directives, which will be used quite

often. As the name suggests, a preprocessor command comes before the processor

command. The processor command directs the compiler to compile the code, which

obeys all C rules. So obviously the code we write is not fully C compliant and we make it

C compliant using the preprocessor directives.

Compilation is basically a two-step process:

1. First the preprocessor prepares the “document” we have written and makes it fully

C compliant.

2. The processor now has a fully compliant C code which it compiles.

A preprocessor directive always starts with a # symbol. A semicolon should not be put to

declare its end.

We have already come across the #define command to define constants.

The syntax was #define A B

The preprocessor replaces all instances of A with B in step 1 of the 2-step process of

compilation.

So what are the values B can have? “0”? “1”? “TRUE”? “FALSE”? “ANY THING”?

“ NOTHING”? “ ”? Right! B may be left blank.

#define A would be read as “define A” and A would become “live”. All instances of A

will be replaced by a blank space. Think on how this may be used to make code more

readable?

If you get mad and want to change the value of A to 4, it may be done by:

#undef A

#define A 4

Try:

int main ( void ) {

#define A

char b ;

b = A + 4 ; // b = + 4 ;

#undef A

#define A 4 b = A + 4 ; // b = 4 + 4 ;

return ( 1 ) ; }

Try to avoid such usage.

Page 91: C4micros

91

Check to see if your compiler gives a Preprocessor output file to see what will be

compiled by the processor.

Any A ( in #define A B ) can be checked if it is defined or not by the directives:

#ifdef A or

#ifndef A and appropriate action taken as in:

int main ( void ) {

char a = 4 ;

#define A

#ifdef A

a = 5 ;

#endif // #ifdef A

a = a + 1 ;

return ( 1 ) ;

}

The preprocessor will convert the code to:

int main ( void ) {

char a = 4 ;

a = 5 ;

a = a + 1 ;

return ( 1 ) ;

}

If #define A is commented out or if #ifdef A is changed to #ifndef A, the code is

converted to:

int main ( void ) {

char a = 4 ;

a = a + 1 ;

return ( 1 ) ; }

Page 92: C4micros

92

Every #if should have a corresponding #end if.

One of the major uses of conditional compilation is to block out or add code for

debugging an application. Modular checking of an application may be carried out and

when all modules are working as expected, the debugging code may be left out by

commenting the #define debug statement.

Other variations of #if directives are: #if, #else, #elif

Assembly language instructions are generally inserted using the directives #pragma asm

and #pragma endasm:

file: main.c

char assembly ( void ) ;

int main ( void ) {

char a = 5 ;

assembly ( ) ;

a = 6 ;

return ( 1 ) ;

}

file assembly.c

char assembly ( void ) {

#pragma asm

mov a, #4

#pragma endasm

return ( 1 ) ;

}

Page 93: C4micros

93

Concept 36 – Header files

Header files have to head all files and hence are placed right on top! They are identified

by their .h extensions. They generally contain global variable declarations, function

prototypes, #define constants, macro definitions, and register and port addresses of the

micro controller being used.

System header files, i.e., files that come with the compiler should not be modified and are

included by the directive #include < A.h > where A is the name of the file to be included.

“User” header files are included by the directive #include “A.h” where A is the name of

the file to be included and is generally placed in the same project directory.

Consider writing a program to determine the area of a circle:

#define PI 3.14 // global in this source file.

char radius ; // global declaration and definition

int main ( void ) {

float area ; // declaration. can it be put below the next statement?

radius = 2 ; // assignment. can it be put after its declaration above ?

area = ( PI * radius* radius ) ;

return ( 1 ) ; }

Now let Dick (You were Tom!) write a function to determine the circumference:

// file circum.c:

#define PI 3.14 // required as scope of #define in main is not valid

float circumference ( char radius) {

return ( 2 * PI * radius ) ; }

The main will now require a prototype and a new variable for circumference:

// main.c :

#define PI 3.14

char radius ; float circumference ( char) ; // prototype added

int main ( void )

Page 94: C4micros

94

{

float area, circum ; // circum not circumference added

radius = 2 ;

area = ( PI * radius* radius ) ; circum = circumference ( radius ) ; // circumference obtained

return ( 1 ) ; }

Add the two files to the project and compile. You should get the area and circumference

as 12.56.

Now make a mistake (as you always do?) and type #define PI 3 in Dick’s file.

What happens? You get 12.56 and 12 as the answers.

To avoid such mistakes and to have a single point reference we generally put values

required by multiple files in a single file called a header file and #include them in the

respective source files. Thus the above program would now become:

// main.c :

#include “globals.h”

char radius ; float circumference ( char) ; // only types are allowed as parameters, try float area ( 3 ) ;

int main ( void ) {

float area, circum ;

radius = 2 ;

area = ( PI * radius* radius ) ;

circum = circumference ( radius ) ;

return ( 1 ) ; }

// file circum.c:

#include “globals.h”

float circumference ( char radius) {

return ( 2 * PI * radius ) ; }

Page 95: C4micros

95

// and file globals.h:

#define PI 3.14

Now PI can have only one value and can be modified as required at only one place if any

changes are required.

It may so happen, that header files are included more than once especially if they are

nested. To avoid repetition of header files in the code, the following is done.

Consider:

// file main.c:

#include “globals.h”

int main ( void ) {

return ( 1 ) ; }

// and file globals.h:

char a ;

Compile and look at the list or preprocessor output files. Notice the listing of char a.

Now try this:

// file main.c:

#include “globals.h”

#include “globals.h”

int main ( void ) {

return ( 1 ) ; }

// and file globals.h:

char a ;

Compile and look at the list or preprocessor output files. Notice the listing of char a. It

now appears twice!

Page 96: C4micros

96

To prevent this we enclose the header file as follows:

// file globals.h:

#ifndef globals

#define globals

char a ; // the entire header file

#endif

char a appears only once now in the list files.

Now try this:

// file main.c:

#include “globals.h”

char add ( char ) ;

int main ( void ) {

char b ;

a = a + 1 ;

b = add ( a ) ;

return ( 1 ) ; }

// file add.c:

#include “globals.h”

char add ( char c ) {

return ( a + c ) ; }

Compile and get errors for redefinition.

To get over this add another file globals.c, put whatever was in globals.h in it and add it

to the project.

Now add the word extern to all variable declarations, and our problem is solved.

Page 97: C4micros

97

So we now have the following 4 files:

// file globals.c:

#ifndef globals

#define globals

char a ; // the entire header file

#endif

// file globals.h:

#ifndef globals

#define globals

extern char a ; // extern added

#endif

// file main.c:

#include “globals.h”

char add ( char ) ;

int main ( void ) {

char b ;

a = a + 1 ;

b = add ( a ) ;

return ( 1 ) ; }

// file add.c:

#include “globals.h”

char add ( char c ) {

return ( a + c ) ; }

Page 98: C4micros

98

Concept 37 – The last arithmetic operator, %

We have seen the four arithmetic operators +, -, * and /.

We have learnt almost the whole of C concepts with just these. Let’s for completion’s

sake learn the last of the arithmetic operators, the modulo operator represented by %.

This operator works only on integers.

The syntax is A % B ;

A is divided by B and the remainder is obtained.

Consider:

// | a | > | b |

int main ( void ) {

char a = 7, b = 5, c ; // a and b positive

c = a % b ; // a / b = 1 and remainder is 2

a = -7 ; // a and b negative

b = -5 ; c = a % b ; // a / b = 1 and remainder is - 2

b = 5 ; // a negative, b positive

c = a % b ; // remainder is – 2

a = 7 ; b = - 5 ; // a positive and b negative

c = a % b ; // remainder is 2

return ( 1 ) ; }

The sign of the remainder follows the sign of A.

Page 99: C4micros

99

Consider :

// | a | < | b |

int main ( void ) {

char a = 5, b = 7, c ; // a and b positive

c = a % b ; // a / b = 0 and remainder is 5

a = -5 ; // a and b negative

b = -7 ; c = a % b ; // a / b = 0 and remainder is - 5

b = 7 ; // a negative, b positive

c = a % b ; // remainder is – 5

a = 5 ; b = - 7 ; // a positive and b negative

c = a % b ; // remainder is 5

return ( 1 ) ; }

The sign of the remainder follows the sign of A.

If A were 0, the remainder would be 0.

Be careful to see B is not 0!

Page 100: C4micros

100

Concept 38 – Comparison operators

An expression has a certain value. For example x = 4 ; gives a value of 4 to x.

Another statement y = 3 ; gives a value of 3 to y.

The two expressions may be compared and a relationship found between them.

The possible relations between the two are:

1. x is identical to y.

2. x is not identical to y.

3. x is less than y.

4. x is less than or equal to y.

5. x is greater than y

6. x is greater than or equal to y.

The relational operators query the relationship between two expressions.

Since the only answers a micro controller can give is a 1 or 0, a query results in a 1 if the

relation is true and a 0 if the relation is false.

The above six relationships are checked with the following binary relation operators as:

1. x = = y is x identical to y?

2. x != y is x not identical to y?

3. x < y is x less than y?

4. x <= y is x less than or equal to y?

5. x > y is x greater than y?

6. x >= y is x greater than or equal to y?

The answers obtained are 1 for true and 0 for false.

Check out:

int main ( void ) {

char x = 4, y = 3; char z ; // Let’s put the value of the evaluation in z

z = (x == y ) ; // 0

z = (x != y ) ; // 1

z = (x < y ) ; // 0

z = (x <= y ) ; // 0

z = (x > y ) ; // 1

z = (x >= y ) ; // 1

return ( 1 ) ; }

Page 101: C4micros

101

Beware of using the equality operator for floating point numbers.

You may get unexpected results.

Try:

int main ( void ) {

1.01 == 1.0 ; // you would like it to be true as it is close enough, but !

return ( 1 ) ; }

Page 102: C4micros

102

Concept 39 - Bit operators

Bit operators work on the bits of any integer variable char, int or long. Thus it is possible

to manipulate the individual bits of a byte using bit operators.

The bit operations possible are:

Bit wise AND:

The syntax is A & B where A and B are integer variables and & is the bit wise AND ing

operator. The truth table for AND ing is :

A B A&B

0 0 0

0 1 0

1 0 0

1 1 1

An example of bit wise AND ing would be:

A 1001 0110

B 0111 1010

A&B 0001 0010

Any bit can be set to 0 using the & operator.

Let’s say we want to make bit 4 of A, 0. We make B 1110 1111 and AND it with A.

A 1001 0110

B 1110 1111

A&B 1000 0110

We have made bit 4, 0 and not touched the other bits. This is called masking.

Thus:

AND with 0 to make a bit 0

AND with 1 to copy the bit as it is (masking).

The simplest way (?) to clear a bit is make B = 0xFF – 2^bit where bit is the position of

the bit to be cleared. For multiple bits, B = 0xFF – (2^bit1 + 2^bit2 ….)

Page 103: C4micros

103

Let’s say we want to clear bits 1 and 4 of a byte.

Try:

int main ( void ) {

#define B 237 // B = 0xFF – (2^1 + 2^4) = 237

unsigned char a = 0x96 ; // 1001 0110 or 0x96

a = a & B ; // 1000 0100 or 0x84

return ( 1 ) ; }

Now that we know how to clear bits, let’s try setting them.

Bit wise OR:

The syntax is A | B where A and B are integer variables and | is the bit wise OR ing

operator. The truth table for OR ing is :

A B A&B

0 0 0

0 1 1

1 0 1

1 1 1

An example of bit wise OR ing would be:

A 1001 0110

B 0111 1010

A|B 1111 1110

Any bit can be set to 1 using the | operator.

Let’s say we want to make bit 5 of A, 1. We make B 0010 0000 and OR it with A.

A 1001 0110

B 0010 0000

A|B 1011 0110

We have made bit 5, 1 and not touched the other bits. This is called masking.

Thus:

OR with 1 to make a bit 1

OR with 0 to copy the bit as it is (masking).

Page 104: C4micros

104

The simplest way (?) to set a bit is make B = 2^bit where bit is the position of the bit to

be set. For multiple bits, B = 2^bit1 + 2^bit2 ….

Let’s say we want to set bits 0 and 6 of a byte.

Try:

int main ( void ) {

#define B 65 // B = 2^0 + 2^6 = 65

unsigned char a = 0x96 ; // 1001 0110 or 0x96

a = a | B ; // 1101 0111 or 0xD7

return ( 1 ) ; }

Ok, so what if we want to invert a bit irrespective of it’s present status?

Bit wise XOR:

The syntax is A ^ B where A and B are integer variables and ^ is the bit wise XOR ing

operator. The truth table for XOR ing is :

A B A&B

0 0 0

0 1 1

1 0 1

1 1 0

An example of bit wise XOR ing would be:

A 1001 0110

B 0111 1010

A^B 1110 1100

Any bit can be inverted using the ^ operator.

Let’s say we want to invert bit 4 of A. We make B 0001 0000 and XOR it with A.

A 1001 0110

B 0001 0000

A^B 1000 0110

We have inverted bit 4, and not touched the other bits. This is called masking.

Thus:

XOR with 1 to invert a bit

Page 105: C4micros

105

XOR with 0 to copy the bit as it is (masking).

The simplest way (?) to invert a bit is make B = 2^bit where bit is the position of the bit

to be set. For multiple bits, B = 2^bit1 + 2^bit2 ….

Let’s say we want to invert bits 0 and 1 of a byte.

Try:

int main ( void ) {

#define B 3 // B = 2^0 + 2^1 = 3

unsigned char a = 0x96 ; // 1001 0110 or 0x96

a = a ^ B ; // 1001 0101 or 0x95

return ( 1 ) ; }

So we know how to set, clear or invert specific bits. What do we do to check the status of

a bit? Is it 1 or 0? Let’s say we want to know the status of bit 4 of a byte.

Try:

int main ( void ) {

#define BIT4 16 // 2^4 = 16

unsigned char a = 0x96, status ; // 1001 0110 or 0x96

status = a & BIT4 ; // > 0 if set, 0 if clear

return ( 1 ) ; }

Let’s see what do we do to invert a whole byte.

Page 106: C4micros

106

The ~ operator:

The ~ operator inverts all the bits of an integer variable. It is a unary operator and

requires only one operand.

Try:

int main ( void ) {

unsigned char a = 0x96 ; // 1001 0110 or 0x96

a = ~ a ; // 0110 1001 or 0x69

return ( 1 ) ; }

The shift left operator:

The syntax is A << B ;

Where A is an integer variable, << is the shift left operator and B is the number of bit

positions the bits are shifted. It can be seen shifting left once is equivalent to

multiplication of the variable by 2, or A << B is equivalent to A * 2^B

Try:

int main ( void ) {

unsigned char a = 3, b ;

b = a << 4 ; // shift left 4 times, 3 * 2^4 = 48 or 0x30

return ( 1 ) ; }

Page 107: C4micros

107

The shift right operator:

The syntax is A >> B ;

Where A is an integer variable, >> is the shift right operator and B is the number of bit

positions the bits are shifted. It can be seen shifting right once is equivalent to integer

division by 2, or A >>B is equivalent to A / 2^B

Try:

int main ( void ) {

unsigned char a = 205, b ;

b = a >> 4 ; // shift right 4 times, 205 / 2^4 = 12 or 0x0C

return ( 1 ) ; }

Page 108: C4micros

108

Concept 40 - Logic operators

We have 3 logic operators the AND, OR and NOT.

Logic operators, like the relational operators and unlike the bit wise operators only test

the LOGIC and give an YES or NO answer represented by TRUE or FALSE 1 or 0.

They are distinguished from their bit wise counterparts by being represented by &&, ||

and ! symbols. Remember they were &, | and ~ for the bit wise operators.

Since they are logic operators they evaluate to True or False. True is 1 while False is 0.

They are used when evaluating multiple conditions at a time, for example:

a > b && c >d

The AND and OR operators are binary and require 2 variables or expressions on either

side. The ! is an unary operator and requires only one variable or expression to its right.

The syntax for the 3 operators are:

( Exp 1 ) && ( Exp2 ) ; // is Exp 1 AND Exp 2 True?

( Exp 1 ) || ( Exp2 ) ; // is Exp 1 OR Exp 2 True?

( !Exp 1 ) ; // is Exp 1 FALSE (Zero) ?

The truth table for the 3 operators may be tabulated as:

Exp 1 Exp 2 ( Exp 1 ) && ( Exp2 ) ;

returns

( Exp 1 ) || ( Exp2 ) ;

returns

( !Exp 1 ) ;

returns

0 0 0 0 1

0 1 0 1 1

1 0 0 1 0

1 1 1 1 0

It can be seen that the compiler need not and indeed doesn’t evaluate Exp2, if Exp 1 is 0

for AND logic or 1 for OR logic. BE AWARE of the side effects of this statement.

An expression is True if it evaluates to any positive or negative value, 9, -3, 2.5 etc.

An expression is False only if it evaluates to 0. The truth table may also be written as:

Exp 1 Exp 2 ( Exp 1 ) && ( Exp2 ) ;

returns

( Exp 1 ) || ( Exp2 ) ;

returns

( !Exp 1 ) ;

returns

False False False False True

False True False True True

True False False True False

True True True True False

Try:

Page 109: C4micros

109

int main ( void ) {

unsigned char a = 0, b = 1, c =2, d = 3, e ;

e = ( a < b ) && ( c < d ) ; // is a < b AND c < d ? True ( 1 )

e = ( a > b ) && ( c > d ) ; // False ( 0 )

e = (a < b ) || ( c < d ) ; // is a < b OR c < d ? True ( 1 )

e = (a > b ) || ( c > d ) ; // False ( 0 )

e = ( ! a ) ; // is a False (Zero) ? True ( 1 )

e = ( ! b ) ; // is b naught ? False ( 0 )

e = 2 && 3 ; // True ( 1 )

return ( 1 ) ; }

Suppose we want True “returned” if an expression (Exp1) or variable (a) is True, or IS

NOT = ZERO.

We could write as ( Exp1 ) ; If Exp1 is any non zero value, it returns True or 1. Or,

We could write as ( a ) ; If a is any non zero value, it returns True or 1.

Another way would be to write:

( a ! = 0 ) ; // Is a NOT = ZERO

Suppose we want True “returned” if an expression (Exp1) or variable (a) is False, or IS =

ZERO.

We could write as ( !Exp1 ) ; If Exp1 is zero , it returns True or 1. Or,

We could write as ( !a ) ; If a is zero, it returns True or 1.

Another way would be to write:

( a = = 0 ) ; // Is a = ZERO

Try:

int main ( void ) {

#define TRUE 1

#define FALSE 0

unsigned char a, b ;

a = ( TRUE ) ; // is TRUE True? 1 if TRUE != 0

b = ( !FALSE ) ; // is FALSE False? 1 if FALSE = = 0

return ( 1 ) ; }

Page 110: C4micros

110

Concept 41 – If Statements

The logic and relational operators return only a True or False answer (1 or 0).

So how can they be used? They can be used to decide whether a block of code is to be

executed or not. This is done by using them in IF statements.

Generally statements are executed in sequence:

statement 1 ;

statement 2 ;

statement 3 ;

Quite often, we have to take a decision and execute some code if a certain condition is

True or skip the code if the condition is False. This can be represented in Flow Chart

Form as:

statement 1 ;

statement 2 ;

........

( condition )

next statement ;

True ( ! = 0 )False ( = 0 )

i f

" if " statements

The syntax is:

if ( condition ) {

statement 1 ;

statement 2 ;

}

next statement ;

Page 111: C4micros

111

Observe:

1. No semi colon after if ( condition ), as a semi colon is used to indicate a

completion of job and we have just started a job.

2. No semi colon after either curly bracket as they just group a series of statements {

a block } and are not statements by themselves.

3. If (condition) evaluates to True (any number other than zero, generally 1), the

block of statements below if ( condition ) are executed, and execution moves on

to the next statement ;

4. If (condition) evaluates to False (zero only), the block of statements below if (

condition ) are skipped, and execution moves on to the next statement ;

5. The next statement ; is the first statement after the “if block”,i.e., the block of

statements following if ( condition ).

If there is only one statement in the “if block” the curly brackets may be deleted.

However for clarity we shall always put the curly brackets after an if ( condition ).

An example would be in order:

int main ( void ) {

char a = 0 ;

if ( 4 > 2 ) // will evaluate to True (1)

{

a = 5 ; // will be executed, and moves on

}

a = a + 1 ; // a = 6

return ( 1 ) ; }

Try it out with if ( 4 > 5 ), a = 1 after execution.

What if we require a code “A” to be executed if the condition is True and code “B” if the

condition is False?

For such an eventuality we have the if – else statement.

A flow chart representation would be:

Page 112: C4micros

112

False ( = 0 )

else

statement 1 ;

statement 2 ;

........

( condition )

next statement ;

True ( ! = 0 )

statement 1 ;

statement 2 ;

........

i f

" if else" statements

The syntax would be:

if ( condition ) {

statement 1 ;

statement 2 ; }

else {

statement 3 ;

statement 4 ; }

next statement ;

Let’s call the block of statements below if ( condition ) as the “if block” and the block of

statements below else as the “else block”.

The “if block” is executed when (condition) evaluates to True and the “else block” is

executed when (condition) evaluates to False.

After any one block is executed, the execution moves on to the “next statement”.

The “next statement” is the first statement after the “else block”.

Page 113: C4micros

113

An example:

int main ( void ) {

char a = 1 ;

if ( 4 > 2 ) // try 4 < 2 next

{

a = 5 ; }

else {

a = 3 ; }

a = a + 1 ; // next statement

return ( 1 ) ; }

a = 6, or a = 4

Page 114: C4micros

114

What if :

code “A” is to be executed if ( condition 1) is true,

code “B” is to be executed if ( condition 2) is true,

code “C” is to be executed if ( condition 3) is true,

Any one block of code should be executed and control move on to the “next statement”

A flow chart representation would be:

False ( = 0 )

else

statements ;

( condition 1 )

next statement ;

True ( ! = 0 )

( condition 2 )

i f

i f

statements ;

True ( ! = 0 )

( condition 3 )

statements ;

True ( ! = 0 )

False ( = 0 )

else

i f

False ( = 0 )

" if else if " statements

Page 115: C4micros

115

The syntax would be:

if ( condition 1 ) {

statements ; }

else if ( condition 2 ) {

statements ; }

else if ( condition 3 ) {

statements ; }

next statement ;

Observations: The code is executed from top to bottom:

1. If ( condition 1 ) is True, the corresponding “if block” is executed, all consecutive

“else if” blocks are skipped and control moves on to the “next statement”.

2. If ( condition 1 ) is False, the “if block” is skipped, and control moves on to the

next “ else if “ condition.

3. If ( condition 2 ) is True, the corresponding “else if block” is executed, all

consecutive “else if” blocks are skipped and control moves on to the “next

statement”.

4. If ( condition 2 ) is False, the corresponding “else if block” is skipped, and

control moves on to the next “ else if “ condition.

5. If ( condition 3 ) is True, corresponding “else if block” is executed, and control

moves on to the “next statement”.

6. If ( condition 3 ) is False, the corresponding “else if block” is skipped, and

control moves on to the “next statement”.

7. The “next statement” is the first statement after all consecutive “else if” blocks.

Page 116: C4micros

116

An example:

int main ( void ) {

char a = 9, b = 0 ; // Try with a = 4, 2, 0

if ( a > 5 ) {

b = 1 ; }

else if ( a > 3 ) {

b = 2 ; }

else if ( a > 1 ) {

b = 3 ; }

a = 0 ; // next statement

return ( 1 ) ; }

Oh Gosh! We have a few more combinations left!

The if – else – if - else,

The if – if ,

The if – if – else and yes

The if – if – else again!

Flow charts and examples follow:

Page 117: C4micros

117

False ( = 0 )

else

statements ;

( condition 1 )

next statement ;

True ( ! = 0 )

( condition 2 )

i f

i f

statements ;

True ( ! = 0 )

( condition 3 )

statements ;

True ( ! = 0 )

False ( = 0 )

else

i f

" if else if else" statements

statements ;

False ( = 0 )

else

The syntax:

if ( condition 1 ) {

statements ; }

else if ( condition 2 ) {

statements ; }

else if ( condition 3 ) {

statements ; }

Page 118: C4micros

118

else {

statements ; }

next statement ;

Observations: The code is executed from top to bottom:

1. If ( condition 1 ) is True, the corresponding “if block” is executed, all consecutive

“else if” and “else” blocks are skipped and control moves on to the “next

statement”.

2. If ( condition 1 ) is False, the “if block” is skipped, and control moves on to the

next “ else if “ condition.

3. If ( condition 2 ) is True, the corresponding “else if block” is executed, all

consecutive “else if” and “else” blocks are skipped and control moves on to the

“next statement”.

4. If ( condition 2 ) is False, the corresponding “else if block” is skipped, and

control moves on to the next “ else if “ condition.

5. If ( condition 3 ) is True, corresponding “else if block” is executed, the “ else

block” skipped and control moves on to the “next statement”.

6. If ( condition 3 ) is False, the corresponding “else if block” is skipped, the

corresponding “ else block” executed and control moves on to the “next

statement”.

7. The “next statement” is the first statement after all consecutive “else if” and

“else” blocks.

8. The last “else block” can be considered the “default block” which is executed if

all other conditions fail.

Page 119: C4micros

119

An example:

int main ( void ) {

char a = 9, b = 0 ; // Try with a = 4, 2, 0

if ( a > 5 ) {

b = 1 ; }

else if ( a > 3 ) {

b = 2 ; }

else if ( a > 1 ) {

b = 3 ; }

else {

b = 4 ; }

a = 0 ; // next statement

return ( 1 ) ; }

Page 120: C4micros

120

statements ;

( condition 1 )

next statement ;

True ( ! = 0 )

( condition 2 )

i f

i f

True ( ! = 0 )False ( = 0 )

" if if " statements

False ( = 0 )

The syntax:

if ( condition 1 ) { // the code works without this bracket pair

if ( condition 2 ) {

statements ; }

}

next statement ;

Page 121: C4micros

121

Observations:

1. If ( condition 1 ) is True, the corresponding “if block” is executed and control

moves on to the “next statement”.

2. If ( condition 1 ) is false, the corresponding “if block” is skipped and control

moves on to the “next statement”.

3. May be used to check if a series of conditions are True and code “A” executed or

continue with main code if any condition is False.

4. Can’t the same be achieved with the logic operator &&?

Try:

int main ( void ) {

char a = 9, b = 0 ; // try a = 4, 20

if ( a > 5 ) {

if ( a < 18 ) {

b = 5 ; }

}

b = b + 1 ; // next statement

return ( 1 ) ; }

int main ( void ) {

char a = 9, b = 0 ; // try a = 4, 20

if ( a > 5 && a < 18 ) {

b = 5 ; }

b = b + 1 ;

return ( 1 ) ; }

Page 122: C4micros

122

statements ;

( condition 1 )

next statement ;

True ( ! = 0 )

( condition 2 )

i f

i f

True ( ! = 0 )False ( = 0 )

else

" if if else" statements

False ( = 0 )

statements ;

The syntax:

if ( condition 1 ) { // code works without this bracket pair

if ( condition 2 ) {

statements ; }

else // else belongs to the previous if in the same block

{

statements ; }

}

next statement ;

Page 123: C4micros

123

Observations:

1. If ( condition 1 ) is True, the corresponding “if block” is executed and control

moves on to the “next statement”.

2. If ( condition 1 ) is false, the corresponding “if block” is skipped and control

moves on to the “next statement”.

An example:

int main ( void ) {

char a = 9, b = 0 ;

if ( a > 5 ) // try a = 4 and a = 20

{

if ( a < 18 ) {

b = 1 ; // for a = 9

}

else {

b = 2 ; // for a = 20

}

}

a = 0;

return ( 1 ) ; }

Page 124: C4micros

124

statements ;

( condition 1 )

next statement ;

True ( ! = 0 )

( condition 2 )

i f

i f

True ( ! = 0 )

False ( = 0 )

else

" if if else" statements

False ( = 0 )

statements ;

The syntax:

if ( condition 1 ) { // code doesn’t work without this bracket pair

if ( condition 2 ) {

statements ; }

}

else // else belongs to the first if above the block

{

statements ; }

Page 125: C4micros

125

An example:

int main ( void ) {

char a = 9, b = 0 ;

if ( a > 5 ) // try a = 4 and a = 20

{

if ( a < 18 ) {

b = 1 ; // for a = 9

}

}

else {

b = 2 ; // for a = 4

}

a = 0; // next statement

return ( 1 ) ; }

Page 126: C4micros

126

Concept 42 – goto statements

goto statements are considered unprofessional in structured programming and hence are

rarely used unless you are in deep trouble and would like to jump out!

We shall go through them to better understand “switch statements” a concept we shall

learn next.

A program consists of a series of statements like:

a = a + 1 ; // statement 1

b = b + 2 ; // statement 2

c = c + 3 ; // statement 3

d = d + 4 ; // statement 4

e = e + 5 ; // statement 5

They are executed in sequence, starting from statement 1 up to statement 5.

We can identify each statement by giving it a name or “label”.

“label” is any name given to a statement, followed by a full colon.

Examples are

cat:

dog:

statement1 :

statement2 : and so on.

So our program now becomes:

int main ( void ) {

char a, b, c, d, e ;

statement1 : a = a + 1 ; // statement 1

statement2 : b = b + 2 ; // statement 2

statement3 : c = c + 3 ; // statement 3

statement4 : d = d + 4 ; // statement 4

statement5 : e = e + 5 ; // statement 5

return ( 1 ) ; }

Our program executes as usual, the names making no difference to the sequence of

execution. The “ labels” may just as well be missing!

Page 127: C4micros

127

So what use can a “label” be?

Suppose we want to execute statement 4 after the first statement and come back to

statement 1 after statement 5, how do we do it? As we have identified the statements by

giving them names or “labels” we can use the goto key word (considered unstructured

programming).

The syntax is:

goto label ;

Our program becomes:

int main ( void ) {

char a, b, c, d, e ;

statement1 : a = a + 1 ; // statement 1

goto statement4 ; // jump statement

statement2 : b = b + 2 ; // statement 2

statement3 : c = c + 3 ; // statement 3

statement4 : d = d + 4 ; // statement 4

statement5 : e = e + 5 ; // statement 5

goto statement1 ; // jump statement

return ( 1 ) ; }

Well, statements 2 and 3 will never be executed. So what?

All we want to understand is:

1. “labeling” a statement makes no difference to the sequence of execution and

the execution just “falls through” ignoring the “labels”.

2. A “label” is a “name” or “ identifier” with a “full colon”.

3. Using a goto we can jump anywhere.

Page 128: C4micros

128

Concept 43 – The switch

We have been learning key words, here we shall master four key words at one shot, the

switch, case, break and default!

A “switch statement” is used if multiple choices are available for a condition.

Execution is switched to different labels, depending on the evaluation of an expression.

A flow chart representation would be:

case 0 : statement 1 ;

statement 2 ;

b r e a k

0 ,1, 2, x

The " switch"

0

1

2

X &

(d e f a u l t )

case 1 : statement 3 ;

statement 4;

case 2 : statement 5 ;

b r e a k

switch ( int expr)

default : statement 6;

statement 7;

X &

( n o d e fa u l t )

x is any value other than

listed with a case label

g o t

o

Page 129: C4micros

129

The syntax:

switch ( int exprn ) {

case const 1 :

statements ;

break ;

case const 2 : statements ; // no statements are also fine

case const 3 :

statements ;

break ;

default : statements ; // a break may be added

}

switch is the key word initiating the process.

( int exprn ) is the expression in parenthesis and should evaluate to an integer value( 0, 1

etc., ) or a character ( ‘a’, ‘z’ etc.,) at compile time.

case const 1 : , case const 2 : , etc., are labels.

const 1, const 2, etc., are integer or character values, the ( int expr ) may evaluate to.

default : is the label execution is transferred to, if ( int expr ) does not evaluate to any

of the constants in the “case const x” labels.

break is a key word to transfer control out of the “switch block”.

Observations:

1. As the execution is from top to bottom, braces are not required after every

statement.

2. A break is required after every case, if distinct operations have to be performed

for each case.

3. A case need not have any statements or breaks, in which case operation just falls

through. This is useful if two or more cases require the same operation to be

performed.

Page 130: C4micros

130

Examples:

int main ( void ) {

char a = 2 ;

switch ( 2 ) // no semicolon

{

case 0 : a = 3 ; // distinct operations for each case

break ;

case 1 : a = 4 ;

break ;

case 2 : a = 5 ;

break ;

default : a = 6 ;

break ; }

return ( 1 ) ; }