Chapter 3. C Programming Language

There are several C compilers on the market for the PIC18 series of microcontrollers. These compilers have many similar features, and they can all be used to develop C-based high-level programs for PIC18 microcontrollers.

Some of the C compilers used most often in commercial, industrial, and educational PIC18 microcontroller applications are:

  • mikroC

  • PICC18

  • C18

  • CCS

The popular and powerful mikroC, developed by MikroElektronika (web site: www.microe.com), is easy to learn and comes with rich resources, such as a large number of library functions and an integrated development environment with a built-in simulator and an in-circuit debugger (e.g., mikroICD). A demo version of the compiler with a 2K program limit is available from MikroElektronika.

PICC18, another popular C compiler, was developed by Hi-Tech Software (web site: www.htsoft.com) and is available in two versions: standard and professional. A powerful simulator and an integrated development environment (Hi-Tide) are provided by the company. PICC18 is supported by the PROTEUS simulator (www.labcenter.co.uk) which can be used to simulate PIC microcontroller–based systems. A limited-period demo version of this compiler is available on the developer’s web site.

C18 is a product of Microchip Inc. (web site: www.microchip.com). A limited-period demo version, as well as a limited functionality version of C18 with no time limit, are available from the Microchip web site. C18 includes a simulator and supports hardware and software development tools such as in-circuit emulators (e.g., ICE2000) and in-circuit debuggers (e.g., ICD2).

CCS has been developed by the Custom Computer Systems Inc. (web site: www.ccsinfo.com). The company offers a limited-period demo version of their compiler. CCS provides a large number of built-in functions and supports an in-circuit debugger (e.g., ICD-U40) which are very helpful in the development of PIC18 microcontroller– based systems.

In this book we are mainly concentrating on the use of the mikroC compiler, and most of the projects are based on this compiler.

Structure of a mikroC Program

Figure 3.1 shows the simplest structure of a mikroC program. This program flashes an LED connected to port RB0 (bit 0 of PORTB) of a PIC microcontroller in one-second intervals. Do not worry if you don’t understand the operation of the program at this stage, as all will come clear as this chapter progresses. Some of the programming elements in Figure 3.1 are described in detail here.

Table 3.1. Structure of a simple C program

/*********************************************************                                  LED FLASHING PROGRAM                                  ************************This program flashes an LED connected to port pin RB0 of PORTB with onesecond intervals.Programmer  : D. IbrahimFile               : LED.CDate             : May, 2007Micro           : PIC18F452**********************************************************void main(){     for(;;)                                   // Endless loop     {           TRISB = 0;                    // Configure PORTB as output           PORTB.0 = 0;               // RB0 = 0           Delay_Ms(1000);          // Wait 1 second           PORTB.0 = 1;               // RB0 = 1           Delay_Ms(1000);          // Wait 1 second     }                                         // End of loop}

Comments

Comments are used to clarify the operation of the program or a programming statement. Comment lines are ignored and not compiled by the compiler. In mikroC programs comments can be of two types: long comments, extending several lines, and short comments, occupying only a single line. Comment lines at the beginning of a program can describe briefly the program’s operation and provide the author’s name, the program filename, the date the program was written, and a list of version numbers, together with the modifications in each version. As shown in Figure 3.1, comments can also be added after statements to describe the operations that the statements perform. Clear and succinct comment lines are important for the maintenance and thus the lifetime of a program, as a program with good comments is easier to modify and/or update.

As shown in Figure 3.1, long comments start with the character “/*” and terminate with the character “*/”. Similarly, short comments start with the character “//” and do not need a terminating character.

Beginning and Ending of a Program

In C language, a program begins with the keywords:

void main ()

After this, a curly opening bracket is used to indicate the beginning of the program body. The program is terminated with a closing curly bracket. Thus, as shown in Figure 3.1, the program has the following structure:

void main()
{
        program body
}

Terminating Program Statements

In C language, all program statements must be terminated with the semicolon (“;”) character; otherwise a compiler error will be generated:

j = 5;            // correct
j = 5             // error

White Spaces

White spaces are spaces, blanks, tabs, and newline characters. The C compiler ignores all white spaces. Thus, the following three sequences are identical:

int i;    char j;
or
int i;
char j;
or
int i;
          char j;

Similarly, the following sequences are identical:

i = j + 2;
or
i = j
      + 2;

Case Sensitivity

In general, C language is case sensitive and variables with lowercase names are different from those with uppercase names. Currently, however, mikroC variables are not case sensitive (although future releases of mikroC may offer case sensitivity) so the following variables are equivalent:

total    TOTAL    Total    ToTal    total    totaL

The only exception is the identifiers main and interrupt, which must be written in lowercase in mikroC. In this book we are assuming that the variables are case sensitive, for the sake of compatibility with other C compilers, and variables with the same name but different cases are not used.

Variable Names

In C language, variable names can begin with an alphabetical character or with the underscore character. In essence, variable names can include any of the characters a to z and A to Z, the digits 0 to 9, and the underscore character “_”. Each variable name should be unique within the first 31 characters of its name. Variable names can contain uppercase and lowercase characters (see Section 3.1.5), and numeric characters can be used inside a variable name. Examples of valid variable names are:

Sum    count    sum100    counter    i1    UserName
_myName

Some names are reserved for the compiler itself and cannot be used as variable names in a program. Table 3.1 gives a list of these reserved names.

Table 3.1. mikroC reserved names

asm

enum

signed

auto

extern

sizeof

break

float

static

case

for

struct

char

goto

switch

const

if

typedef

continue

int

union

default

long

unsigned

do

register

void

double

return

volatile

else

short

while

Variable Types

The mikroC language supports the variable types shown in Table 3.2. Examples of variables are given in this section.

Table 3.2. mikroC variable types

Type

Size (bits)

Range

unsigned char

8

0 to 255

unsigned short int

8

0 to 255

unsigned int

16

0 to 65535

unsigned long int

32

0 to 4294967295

   

signed char

8

−128 to 127

signed short int

8

−128 to 127

signed int

16

−32768 to 32767

signed long int

32

−2147483648 to 2147483647

float

32

±1.17549435082E-38 to ±6.80564774407E38

double

32

±1.17549435082E-38 to ±6.80564774407E38

long double

32

±1.17549435082E-38 to ±6.80564774407E38

(unsigned) char or unsigned short (int)

The variables (unsigned) char, or unsigned short (int), are 8-bit unsigned variables with a range of 0 to 255. In the following example two 8-bit variables named total and sum are created, and sum is assigned decimal value 150:

unsigned char total, sum;
sum = 150;

or

char total, sum;
sum = 150;

Variables can be assigned values during their declaration. Thus, the above statements can also be written as:

char total, sum = 150;

signed char or (signed) short (int)

The variables signed char, or (signed) short (int), are 8-bit signed character variables with a range of −128 to +127. In the following example a signed 8-bit variable named counter is created with a value of –50:

signed char counter = –50;

or

short counter = –50;

or

short int counter = –50;

(signed) int

Variables called (signed) int are 16-bit variables with a range −32768 to +32767. In the following example a signed integer named Big is created:

int Big;

unsigned (int)

Variables called (unsigned) int are 16-bit unsigned variables with a range 0 to 65535. In the following example an unsigned 16-bit variable named count is created and is assigned value 12000:

unsigned int count = 12000;

(signed) long (int)

Variables called (signed) long (int) are 32 bits long with a range −2147483648 to +2147483647. An example is:

signed long LargeNumber;

unsigned long (int)

Variables called (unsigned) long (int) are 32-bit unsigned variables having the range 0 to 4294967295. An example is:

unsigned long VeryLargeNumber;

float or double or long double

The variables called float or double or long double, are floating point variables implemented in mikroC using the Microchip AN575 32-bit format, which is IEEE 754 compliant. Floating point numbers range from ±1.17549435082E-38 to ±6.80564774407E38. In the following example, a floating point variable named area is created and assigned the value 12.235:

float area;
area = 12.235;

To avoid confusion during program development, specifying the sign of the variable (signed or unsigned) as well as the type of variable is recommended. For example, use unsigned char instead of char only, and unsigned int instead of unsigned only.

In this book we are using the following mikroC data types, which are easy to remember and also compatible with most other C compilers:

unsigned char

0 to 255

signed char

−128 to 127

unsigned int

0 to 65535

signed int

−32768 to 32767

unsigned long

0 to 4294967295

signed long

−2147483648 to 2147483647

float

±1.17549435082E-38 to ±6.80564774407E38

Constants

Constants represent fixed values (numeric or character) in programs that cannot be changed. Constants are stored in the flash program memory of the PIC microcontroller, thus not wasting valuable and limited RAM memory. In mikroC, constants can be integers, floating points, characters, strings, or enumerated types.

Integer Constants

Integer constants can be decimal, hexadecimal, octal, or binary. The data type of a constant is derived by the compiler from its value. But suffixes can be used to change the type of a constant.

In Table 3.2 we saw that decimal constants can have values from −2147483648 to +4294967295. For example, constant number 210 is stored as an unsigned char (or unsigned short int). Similarly, constant number −200 is stored as a signed int.

Using the suffix u or U forces the constant to be unsigned. Using the suffix L or l forces the constant to be long. Using both U (or u) and L (or l) forces the constant to be unsigned long.

Constants are declared using the keyword const and are stored in the flash program memory of the PIC microcontroller, thus not wasting valuable RAM space. In the following example, constant MAX is declared as 100 and is stored in the flash program memory of the PIC microcontroller:

const MAX = 100;

Hexadecimal constants start with characters 0x or 0X and may contain numeric data 0 to 9 and hexadecimal characters A to F. In the following example, constant TOTAL is given the hexadecimal value FF:

const TOTAL = 0xFF;

Octal constants have a zero at the beginning of the number and may contain numeric data 0 to 7. In the following example, constant CNT is given octal value 17:

const CNT = 017;

Binary constant numbers start with 0b or 0B and may contain only 0 or 1. In the following example a constant named Min is declared as having the binary value 11110000:

const Min = 0b11110000

Floating Point Constants

Floating point constant numbers have integer parts, a dot, a fractional part, and an optional e or E followed by a signed integer exponent. In the following example, a constant named TEMP is declared as having the fractional value 37.50:

const TEMP = 37.50

or

const TEMP = 3.750E1

Character Constants

A character constant is a character enclosed within single quote marks. In the following example, a constant named First_Alpha is declared as having the character value “A”:

const First_Alpha = 'A';

String Constants

String constants are fixed sequences of characters stored in the flash memory of the microcontroller. The string must both begin and terminate with a double quote character (“). The compiler automatically inserts a null character as a terminator. An example string constant is:

"This is an example string constant"

A string constant can be extended across a line boundary by using a backslash character (“”):

"This is first part of the string 
and this is the continuation of the string"

This string constant declaration is the same as:

"This is first part of the string and this is the continuation
of the string"

Enumerated Constants

Enumerated constants are integer type and are used to make a program easier to follow. In the following example, constant colors stores the names of colors. The first element is given the value 0:

enum colors {black, brown, red, orange, yellow, green, blue, gray,
white};

Escape Sequences

Escape sequences are used to represent nonprintable ASCII characters. Table 3.3 shows some commonly used escape sequences and their representation in C language. For example, the character combination “ ” represents the newline character.

Table 3.3. Some commonly used escape sequences

Escape sequence

Hex value

Character

a

0×07

BEL (bell)



0×08

BS (backspace)

0×09

HT (horizontal tab)

0×0A

LF (linefeed)

v

0×0B

VT (vertical feed)

f

0×0C

FF (formfeed)

0×0D

CR (carriage return)

xH

 

String of hex digits

An ASCII character can also be represented by specifying its hexadecimal code after a backslash. For example, the newline character can also be represented as “x0A”.

Static Variables

Static variables are local variables used in functions (see Chapter 4) when the last value of a variable between successive calls to the function must be preserved. As the following example shows, static variables are declared using the keyword static:

static unsigned int count;

External Variables

Using the keyword extern before a variable name declares that variable as external. It tells the compiler that the variable is declared elsewhere in a separate source code module. In the following example, variables sum1 and sum2 are declared as external unsigned integers:

extern int sum1, sum2;

Volatile Variables

Volatile variables are especially important in interrupt-based programs and input-output routines. Using the keyword volatile indicates that the value of the variable may change during the lifetime of the program independent of the normal flow of the program. Variables declared as volatile are not optimized by the compiler, since their values can change unexpectedly. In the following example, variable Led is declared as a volatile unsigned char:

volatile unsigned char Led;

Enumerated Variables

Enumerated variables are used to make a program more readable. In an enumerated variable, a list of items is specified and the value of the first item is set to 0, the next item is set to 1, and so on. In the following example, type Week is declared as an enumerated list and MON = 0, TUE = 1, WED = 2, and so on):

enum Week {MON, TUE, WED, THU, FRI, SAT, SUN};

It is possible to imply the values of the elements in an enumerated list. In the following example, black = 2, blue = 3, red = 4, and so on.

enum colors {black = 2, blue, red, white, gray};

Similarly, in the following example, black = 2, blue = 3, red = 8, and gray = 9:

enum colors {black = 2, blue, red = 8, gray};

Variables of type enumeration can be declared by specifying them after the list of items. For example, to declare variable My_Week of enumerated type Week, use the following statement:

enum Week {MON, TUE, WED, THU, FRI, SAT, SUN} My_Week;

Now we can use variable My_Week in a program:

My_Week = WED     // assign 2 to My_Week

or

My_Week = 2    // same as above

After defining the enumerated type Week, we can declare variables This_Week and Next_Week of type Week as:

enum Week This_Week, Next_Week;

Arrays

Arrays are used to store related items in the same block of memory and under a specified name. An array is declared by specifying its type, name, and the number of elements it will store. For example:

unsigned int Total[5];

This array of type unsigned int has the name Total and has five elements. The first element of an array is indexed with 0. Thus, in this example, Total[0] refers to the first element of the array and Total[4] refers to the last element. The array Total is stored in memory in five consecutive locations as follows:

Total[0]

Total[1]

Total[2]

Total[3]

Total[4]

Data can be stored in the array by specifying the array name and index. For example, to store 25 in the second element of the array we have to write:

Total[1] = 25;

Similarly, the contents of an array can be read by specifying the array name and its index. For example, to copy the third array element to a variable called Temp we have to write:

Temp = Total[2];

The contents of an array can be initialized during the declaration of the array by assigning a sequence of comma-delimited values to the array. An example follows where array months has twelve elements and months[0] = 31, months[1] = 28, and so on:

unsigned char months[12] = {31,28,31,30,31,30,31,31,30,31,30,31};

The same array can also be declared without specifying its size:

unsigned char months[ ] = {31,28,31,30,31,30,31,31,30,31,30,31};

Character arrays can be declared similarly. In the following example, a character array named Hex_Letters is declared with 6 elements:

unsigned char Hex_Letters[ ] = {'A', 'B', 'C', 'D', 'E', 'F'};

Strings are character arrays with a null terminator. Strings can be declared either by enclosing the string in double quotes, or by specifying each character of the array within single quotes and then terminating the string with a null character. The two string declarations in the following example are identical, and both occupy five locations in memory:

unsigned char Mystring[ ] = "COMP";

and

unsigned char Mystring[ ] = {'C', 'O', 'M', 'P', ''};

In C programming language, we can also declare arrays with multiple dimensions. One-dimensional arrays are usually called vectors, and two-dimensional arrays are called matrices. A two-dimensional array is declared by specifying the data type of the array, the array name, and the size of each dimension. In the following example, a two-dimensional array named P is created having three rows and four columns. Altogether, the array has twelve elements. The first element of the array is P[0][0], and the last element is P[2][3]. The structure of this array is shown below:

P[0][0]

P[0][1]

P[0][2]

P[0][3]

P[1][0]

P[1][1]

P[1][2]

P[1][3]

P[2][0]

P[2][1]

P[2][2]

P[2][3]

Elements of a multidimensional array can be specified during the declaration of the array. In the following example, two-dimensional array Q has two rows and two columns, its diagonal elements are set to 1, and its nondiagonal elements are cleared to 0:

unsigned char Q[2][2] = { {1,0}, {0,1} };

Pointers

Pointers are an important part of the C language, as they hold the memory addresses of variables. Pointers are declared in the same way as other variables, but with the character (“*”) in front of the variable name. In general, pointers can be created to point to (or hold the addresses of) character variables, integer variables, long variables, floating point variables, or functions (although mikroC currently does not support pointers to functions).

In the following example, an unsigned character pointer named pnt is declared:

unsigned char *pnt;

When a new pointer is created, its content is initially unspecified and it does not hold the address of any variable. We can assign the address of a variable to a pointer using the (“&”) character:

pnt = &Count;

Now pnt holds the address of variable Count. Variable Count can be set to a value by using the character (“*”) in front of its pointer. For example, Count can be set to 10 using its pointer:

*pnt = 10;      // Count = 10

which is the same as

Count = 10;     // Count = 10

Or, the value of Count can be copied to variable Cnt using its pointer:

Cnt = *pnt;     // Cnt = Count

Array Pointers

In C language the name of an array is also a pointer to the array. Thus, for the array:

unsigned int Total[10];

The name Total is also a pointer to this array, and it holds the address of the first element of the array. Thus the following two statements are equal:

Total[2] = 0;

and

*(Total + 2) = 0;

Also, the following statement is true:

&Total[j] = Total + j

In C language we can perform pointer arithmetic which may involve:

  • Comparing two pointers

  • Adding or subtracting a pointer and an integer value

  • Subtracting two pointers

  • Assigning one pointer to another

  • Comparing a pointer to null

For example, let’s assume that pointer P is set to hold the address of array element Z[2]:

P = &Z[2];

We can now clear elements 2 and 3 of array Z, as in the two examples that follow. The two examples are identical except that in the first example pointer P holds the address of Z[3] at the end of the statements, and it holds the address of Z[2] at the end of the second set of statements:

*P = 0;                       // Z[2] = 0
P = P + 1;                    // P now points to element 3 of Z
*P = 0;                       // Z[3] = 0

or

*P = 0;                       // Z[2] = 0
*(P + 1) = 0;                 // Z[3] = 0

A pointer can be assigned to another pointer. In the following example, variables Cnt and Tot are both set to 10 using two different pointers:

unsigned int *i, *j;          // declare 2 pointers
unsigned int Cnt, Tot;        // declare two variables
i = &Cnt;    // i points to Cnt
*i = 10;     // Cnt = 10
j = i;       // copy pointer i to pointer j
Tot = *j;    // Tot = 10

Structures

A structure can be used to collect related items that are then treated as a single object. Unlike an array, a structure can contain a mixture of data types. For example, a structure can store the personal details (name, surname, age, date of birth, etc.) of a student.

A structure is created by using the keyword struct, followed by a structure name and a list of member declarations. Optionally, variables of the same type as the structure can be declared at the end of the structure.

The following example declares a structure named Person:

struct Person
{
     unsigned char name[20];
     unsigned char surname[20];
     unsigned char nationality[20];
     unsigned char age;
}

Declaring a structure does not occupy any space in memory; rather, the compiler creates a template describing the names and types of the data objects or member elements that will eventually be stored within such a structure variable. Only when variables of the same type as the structure are created do these variables occupy space in memory. We can declare variables of the same type as the structure by giving the name of the structure and the name of the variable. For example, two variables Me and You of type Person can be created by the statement:

struct Person Me, You;

Variables of type Person can also be created during the declaration of the structure as follows:

struct Person
{
      unsigned char name[20];
      unsigned char surname[20];
      unsigned char nationality[20];
      unsigned char age;
} Me, You;

We can assign values to members of a structure by specifying the name of the structure, followed by a dot (“.”) and the name of the member. In the following example, the age of structure variable Me is set to 25, and variable M is assigned to the value of age in structure variable You:

Me.age = 25;
M = You.age;

Structure members can be initialized during the declaration of the structure. In the following example, the radius and height of structure Cylinder are initialized to 1.2 and 2.5 respectively:

struct Cylinder
{
      float radius;
      float height;
} MyCylinder = {1.2, 2.5};

Values can also be set to members of a structure using pointers by defining the variable types as pointers. For example, if TheCylinder is defined as a pointer to structure Cylinder, then we can write:

struct Cylinder
{
      float radius;
      float height;
} *TheCylinder;

TheCylinder −> radius = 1.2;
TheCylinder −> height = 2.5;

The size of a structure is the number of bytes contained within the structure. We can use the sizeof operator to get the size of a structure. Considering the above example,

sizeof(MyCylinder)

returns 8, since each float variable occupies 4 bytes in memory.

Bit fields can be defined using structures. With bit fields we can assign identifiers to bits of a variable. For example, to identify bits 0, 1, 2, and 3 of a variable as LowNibble and to identify the remaining 4 bits as HighNibble we can write:

struct
{
      LowNibble   : 4;
      HighNibble  : 4;
} MyVariable;

We can then access the nibbles of variable MyVariable as:

MyVariable.LowNibble = 12;
MyVariable.HighNibble = 8;

In C language we can use the typedef statements to create new types of variables. For example, a new structure data type named Reg can be created as follows:

typedef struct
{
      unsigned char name[20];
      unsigned char surname[20];
      unsigned age;
} Reg;

Variables of type Reg can then be created in the same way other types of variables are created. In the following example, variables MyReg, Reg1, and Reg2 are created from data type Reg:

Reg MyReg, Reg1, Reg2;

The contents of one structure can be copied to another structure, provided that both structures are derived from the same template. In the following example, structure variables of the same type, P1 and P2, are created, and P2 is copied to P1:

struct Person
{
      unsigned char name[20];
      unsigned char surname[20];
      unsigned int age;
      unsigned int height;
      unsigned weight;
}
struct Person P1, P2;
........................
........................
P2 = P1;

Unions

Unions are used to overlay variables. A union is similar to a structure and is even defined in a similar manner. Both are based on templates, and the members of both are accessed using the “.” or “->” operators. A union differs from a structure in that all variables in a union occupy the same memory area, that is, they share the same storage. An example of a union declaration is:

union flags
{
      unsigned char x;
      unsigned int y;
} P;

In this example, variables x and y occupy the same memory area, and the size of this union is 2 bytes long, which is the size of the biggest member of the union. When variable y is loaded with a 2-byte value, variable x will have the same value as the low byte of y. In the following example, y is loaded with 16-bit hexadecimal value 0xAEFA, and x is loaded with 0xFA:

P.y = 0xAEFA;

The size of a union is the size (number of bytes) of its largest member. Thus, the statement:

sizeof(P)

returns 2.

This union can also be declared as:

union flags
{
      unsigned char x;
      unsigned int y;
}
union flags P;

Operators in C

Operators are applied to variables and other objects in expressions to cause certain conditions or computations to occur.

mikroC language supports the following operators:

Arithmetic Operators

Arithmetic operators are used in arithmetic computations. Arithmetic operators associate from left to right, and they return numerical results. The mikroC arithmetic operators are listed in Table 3.4.

Table 3.4. mikroC arithmetic operators

Operator

Operation

+

Addition

Subtraction

*

Multiplication

/

Division

%

Remainder (integer division)

++

Auto increment

−−

Auto decrement

The following example illustrates the use of arithmetic operators:

/* Adding two integers */
5 + 12                                  // equals 17

/* Subtracting two integers */

120 – 5                                 // equals 115
10 – 15                                 // equals –5

/* Dividing two integers */
5 / 3                                   // equals 1
12 / 3                                  // equals 4

/* Multiplying two integers */

3 * 12                                  // equals 36

/* Adding two floating point numbers */
3.1 + 2.4                               // equals 5.5
/* Multiplying two floating point numbers */
2.5 * 5.0                               // equals 12.5
/* Dividing two floating point numbers */
25.0 / 4.0                              // equals 6.25
/* Remainder (not for float) */
7 % 3                                   // equals 1
/* Post-increment operator */
j = 4;
k = j++;                                // k = 4, j = 5
/* Pre-increment operator */
j = 4;
k = ++j;                                // k = 5, j = 5
/* Post-decrement operator */
j = 12;
k = j−−;                               // k = 12, j = 11
/* Pre-decrement operator */
j = 12;
k = −−j;                                // k = 11, j = 11

Relational Operators

Relational operators are used in comparisons. If the expression evaluates to TRUE, a 1 is returned; otherwise a 0 is returned.

All relational operators associate from left to right. A list of mikroC relational operators is given in Table 3.5.

Table 3.5. mikroC relational operators

Operator

Operation

= =

Equal to

!=

Not equal to

>

Greater than

<

Less than

>=

Greater than or equal to

<=

Less than or equal to

The following example illustrates the use of relational operators:

x = 10
x > 8     // returns 1
x = = 10  // returns 1
x < 100   // returns 1
x > 20    // returns 0
x != 10   // returns 0
x >= 10   // returns 1
x <= 10   // returns 1

Logical Operators

Logical operators are used in logical and arithmetic comparisons, and they return TRUE (i.e., logical 1) if the expression evaluates to nonzero, and FALSE (i.e., logical 0) if the expression evaluates to zero. If more than one logical operator is used in a statement, and if the first condition evaluates to FALSE, the second expression is not evaluated.

The mikroC logical operators are listed in Table 3.6.

Table 3.6. mikroC logical operators

Operator

Operation

&&

AND

||

OR

!

NOT

The following example illustrates the use of logical operators:

/* Logical AND */
x = 7;

x > 0 && x < 10      // returns 1
x > 0 || x < 10      // returns 1
x >=0 && x <=10      // returns 1
x >=0 && x < 5       // returns 0

a = 10; b = 20; c = 30; d = 40;
a > b && c > d       // returns 0
b > a && d > c       // returns 1
a > b || d > c       // returns 1

Bitwise Operators

Bitwise operators are used to modify the bits of a variable. The mikroC bitwise operators are listed in Table 3.7.

  • Bitwise AND returns 1 if both bits are 1, otherwise it returns 0.

  • Bitwise OR returns 0 if both bits are 0, otherwise it returns 1.

  • Bitwise XOR returns 1 if both bits are complementary, otherwise it returns 0.

  • Bitwise complement inverts each bit.

  • Bitwise shift left and shift right move the bits to the left or right respectively.

Table 3.7. mikroC bitwise operators

Operator

Operation

&

Bitwise AND

|

Bitwise OR

^

Bitwise EXOR

~

Bitwise complement

<<

Shift left

>>

Shift right

The following example illustrates the use of bitwise operators:

  1. 0xFA & 0xEE returns 0xEA
    0xFA:    1111 1010
    0xEE:    1110 1110
    - - - - - - - - - - - -
    0xEA:    1110 1010
  2. 0x01 | 0xFE returns 0xFF
    0x08:    0000 0001
    0xFE:    1111 1110
    - - - - - - - - - - - -
    0xFE:    1111 1111
  3. 0xAA ^ 0x1F returns
    0xAA:    1010 1010
    0x1F:    0001 1111
    - - - - - - - - - - - -
    0xB5:    1011 0101
  4. ~0xAA returns 0x
    0xAA:   1010 1010
    ~:      0101 0101
    - - - - - - - - - - - -
    0x55:   0101 0101
  5. 0x14 >> 1 returns 0x08 (shift 0x14 right by 1 digit)
    0x14:    0001 0100
    >>1 :    0000 1010
    - - - - - - - - - - - -
    0x0A:    0000 1010
  6. 0x14 >> 2 returns 0x05 (shift 0x14 right by 2 digits)
    0x14:    0001 0100
    >> 2:    0000 0101
    - - - - - - - - - - - -
    0x05:    0000 0101
  7. 0x235A << 1 returns 0x46B4 (shift left 0x235A left by 1 digit)
    0x235A:  0010 0011 0101 1010
    <<1 :    0100 0110 1011 0100
    - - - - - - - - - - - - - - - - - - -
    0x46B4 : 0100 0110 1011 0100
  8. 0x1A << 3 returns 0xD0 (shift left 0x1A by 3 digits)
    0x1A:    0001 1010
    <<3 :    1101 0000
    - - - - - - - - - - - -
    0xD0:    1101 0000

Assignment Operators

In C language there are two types of assignments: simple and compound. In simple assignments an expression is simply assigned to another expression, or an operation is performed using an expression and the result is assigned to another expression:

Expression1 = Expression2

or

Result = Expression1 operation Expression2

Examples of simple assignments are:

Temp = 10;
Cnt = Cnt + Temp;

Compound assignments have the general format:

Result operation = Expression1

Here the specified operation is performed on Expression1 and the result is stored in Result. For example:

j += k;   is same as:   j = j + k;

also

p *= m;    is same as    p = p * m;

The following compound operators can be used in mikroC programs:

+=    −=    *=    /=   %=
&=    |=    ^=    >>=  <<=

Conditional Operators

The syntax of a conditional operator is:

Result = Expression1 ? Expression2 : Expression3

Expression1 is evaluated first, and if its value is true, Expression2 is assigned to Result, otherwise Expression3 is assigned to Result. In the following example, the maximum of x and y is found where x is compared with y and if x > y then max = x, otherwise max = y:

max = (x > y) ? x : y;

In the following example, lowercase characters are converted to uppercase. If the character is lowercase (between a and z), then by subtracting 32 from the character we obtain the equivalent uppercase character:

c = (c > = a && c < = z)? (c – 32) : c;

Preprocessor Operators

The preprocessor allows a programmer to:

  • Compile a program conditionally, such that parts of the code are not compiled

  • Replace symbols with other symbols or values

  • Insert text files into a program

The preprocessor operator is the (“#”) character, and any line of code leading with a (“#”) is assumed to be a preprocessor command. The semicolon character (“;”) is not needed to terminate a preprocessor command.

mikroC compiler supports the following preprocessor commands:

#define    #undef
#if        #elif      #endif
#ifdef     #ifndef
#error
#line

#define, #undef, #ifdef, #ifndef

The #define preprocessor command provides macro expansion where every occurrence of an identifier in the program is replaced with the value of that identifier. For example, to replace every occurrence of MAX with value 100 we can write:

#define MAX 100

An identifier that has already been defined cannot be defined again unless both definitions have the same value. One way to get around this problem is to remove the macro definition:

#undef MAX

Alternatively, the existence of a macro definition can be checked. In the following example, if MAX has not already been defined, it is given value 100, otherwise the #define line is skipped:

#ifndef MAX
      #define MAX 100
#endif

Note that the #define preprocessor command does not occupy any space in memory.

We can pass parameters to a macro definition by specifying the parameters in a parenthesis after the macro name. For example, consider the macro definition:

#define ADD(a, b)    (a + b)

When this macro is used in a program, (a, b) will be replaced with (a + b) as shown:

p = ADD(x, y)    will be transformed into p = (x + y)

Similarly, we can define a macro to calculate the square of two numbers:

#define SQUARE(a)    (a * a)

We can now use this macro in a program:

p = SQUARE(x)    will be transformed into p = (x * x)

#include

The preprocessor directive #include is used to include a source file in our program. Usually header files with extension “.h” are used with #include. There are two formats for using #include:

#include <file>

and

#include "file"

In first option the file is searched in the mikroC installation directory first and then in user search paths. In second option the specified file is searched in the mikroC project folder, then in the mikroC installation folder, and then in user search paths. It is also possible to specify a complete directory path as:

#include "C:	emplast.h"

The file is then searched only in the specified directory path.

#if, #elif, #else, #endif

The preprocessor commands #if, #elif, #else, and #endif are used for conditional compilations, where parts of the source code can be compiled only if certain conditions are met. In the following example, the code section where variables A and B are cleared to zero is compiled if M has a nonzero value, otherwise the code section where A and B are both set to 1 is compiled. Notice that the #if must be terminated with #endif:

#if M
      A = 0;
      B = 0;
#else
      A = 1;
      B = 1;
#endif

We can also use the #elif condition, which tests for a new condition if the previous condition was false:

#if M
      A = 0;
      B = 0;
#elif N
      A = 1;
      B = 1;
#else
      A = 2;
      B = 2;

#endif

In the above example, if M has a nonzero value code section, A = 0; B = 0; are compiled. Otherwise, if N has a nonzero value, then code section A = 1; B = 1; is compiled. Finally, if both M and N are zero, then code section A = 2; B = 2; is compiled. Notice that only one code section is compiled between #if and #endif and that a code section can contain any number of statements.

Modifying the Flow of Control

Statements are normally executed sequentially from the beginning to the end of a program. We can use control statements to modify this normal sequential flow in a C program. The following control statements are available in mikroC programs:

Selection Statements

There are two selection statements: if and switch.

if Statement

The general format of the if statement is:

if(expression)
      Statement1;
else
      Statement2;

or

if(expression)Statement1; else Statement2;

If the expression evaluates to TRUE, Statement1 is executed, otherwise Statement2 is executed. The else keyword is optional and may be omitted. In the following example, if the value of x is greater than MAX then variable P is incremented by 1, otherwise it is decremented by 1:

if(x > MAX)
       P++;
else
       P−−;

We can have more than one statement by enclosing the statements within curly brackets. For example:

if(x > MAX)
{
       P++;
       Cnt = P;
       Sum = Sum + Cnt;
}
else
       P−−;

In this example, if x is greater than MAX then the three statements within the curly brackets are executed, otherwise the statement P−− is executed.

Another example using the if statement is:

if(x > 0 && x < 10)
{
       Total += Sum;
       Sum++;
}
else
{
       Total = 0;
       Sum = 0;
}

switch Statement

The switch statement is used when a number of conditions and different operations are performed if a condition is true. The syntax of the switch statement is:

switch (condition)
{
      case condition1:
                 Statements;
                 break;
      case condition2:
                 Statements;
                 break;
      .....................
      .....................
      case condition:
                 Statements;
                 break;
      default:
                 Statements;
}

The switch statement functions as follows: First the condition is evaluated. The condition is then compared to condition1 and if a match is found, statements in that case block are evaluated and control jumps outside the switch statement when the break keyword is encountered. If a match is not found, condition is compared to condition2 and if a match is found, statements in that case block are evaluated and control jumps outside the switch statements, and so on. The default is optional, and statements following default are evaluated if the condition does not match any of the conditions specified after the case keywords.

In the following example, the value of variable Cnt is evaluated. If Cnt = 1, A is set to 1. If Cnt = 10, B is set to 1, and if Cnt = 100, C is set to 1. If Cnt is not equal to 1, 10, or 100 then D is set to 1:

switch (Cnt)
{
      case 1:
                A = 1;
                break;
      case 10:
                B = 1;
                break;
      case 100:
                C = 1;
                break;
      default:
                D = 1;
}

Because white spaces are ignored in C language we can also write the preceding code as:

switch (Cnt)
{
      case 1:      A = 1;    break;
      case 10:     B = 1;    break;
      case 100:    C = 1;    break;
      default:     D = 1;
}

Example 3.1

In an experiment the relationship between X and Y values are found to be:

X    Y
1    3.2
2    2.5
3    8.9
4    1.2
5    12.9

Write a switch statement that will return the Y value, given the X value.

Solution 3.1

The required switch statement is:

switch (X)
{
      case 1:
                 Y = 3.2;
                 break;
      case 2:
                 Y = 2.5;
                 break;
      case 3:
                 Y = 8.9;
                 break;
      case 4:
                 Y = 1.2;
                 break;
      case 5:
                 Y = 12.9;
}

Iteration Statements

Iteration statements enable us to perform loops in a program, where part of a code must be repeated a number of times. In mikroC iteration can be performed in four ways. We will look at each one with examples:

for Statement

The syntax of a for statement is:

for(initial expression; condition expression; increment expression)
{
      Statements;
}

The initial expression sets the starting variable of the loop, and this variable is compared against the condition expression before entry into the loop. Statements inside the loop are executed repeatedly, and after each iteration the value of the increment expression is incremented. The iteration continues until the condition expression becomes false. An endless loop is formed if the condition expression is always true.

The following example shows how a loop can be set up to execute 10 times. In this example, variable i starts from 0 and increments by 1 at the end of each iteration. The loop terminates when i =10, in which case the condition i < 10 becomes false. On exit from the loop, the value of i is 10:

for(i = 0; i < 10; i ++)
{
      statements;
}

This loop could also be started by an initial expression with a nonzero value. Here, i starts with 1 and the loop terminates when i = 11. Thus, on exit from the loop, the value of i is 11:

for(i = 1; i <= 10; i++)
{
      Statements;
}

The parameters of a for loop are all optional and can be omitted. If the condition expression is left out, it is assumed to be true. In the following example, an endless loop is formed where the condition expression is always true and the value of i starts with 0 and is incremented after each iteration:

/* Endless loop with incrementing i */
for(i=0; ; i++)
{
      Statements;
}

In the following example of an endless loop all the parameters are omitted:

/* Example of endless loop */
for(; ;)
{
      Statements;
}

In the following endless loop, i starts with 1 and is not incremented inside the loop:

/* Endless loop with i = 1 */
for(i=1; ;)
{
      Statements;
}

If there is only one statement inside the for loop, he curly brackets can be omitted as shown in the following example:

for(k = 0; k < 10; k++)Total = Total + Sum;

Nested for loops can also be used. In a nested for loop, the inner loop is executed for each iteration of the outer loop. In the following example the inner loop is executed five times and the outer loop is executed ten times. The total iteration count is fifty:

/* Example of nested for loops */
for(i = 0; i < 10; i++)
{
      for(j = 0; j < 5; j++)
      {
            Statements;
      }
}

In the following example, the sum of all the elements of a 3 × 4 matrix M is calculated and stored in a variable called Sum:

/* Add all elements of a 3x4 matrix */
Sum = 0;
for(i = 0; i < 3; i++)
{
      for(j = 0; j < 4; j++)
      {
          Sum = Sum + M [i][j];
      }
}

Since there is only one statement to be executed, the preceding example could also be written as:

/* Add all elements of a 3x4 matrix */
Sum = 0;
for(i = 0; i < 3; i++)
{
      for(j = 0; j < 4; j++) Sum = Sum + M [i][j];
}

while Statement

The syntax of a while statement is:

while (condition)
{
      Statements;
}

Here, the statements are executed repeatedly until the condition becomes false, or the statements are executed repeatedly as long as the condition is true. If the condition is false on entry to the loop, then the loop will not be executed and the program will continue from the end of the while loop. It is important that the condition is changed inside the loop, otherwise an endless loop will be formed.

The following code shows how to set up a loop to execute 10 times, using the while statement:

/* A loop that executes 10 times */
k = 0;
while (k < 10)
{
      Statements;
      k++;
}

At the beginning of the code, variable k is 0. Since k is less than 10, the while loop starts. Inside the loop the value of k is incremented by 1 after each iteration. The loop repeats as long as k < 10 and is terminated when k = 10. At the end of the loop the value of k is 10.

Notice that an endless loop will be formed if k is not incremented inside the loop:

/* An endless loop */
k = 0;
while (k < 10)
{
      Statements;
}

An endless loop can also be formed by setting the condition to be always true:

/* An endless loop */
while (k = k)
{
      Statements;
}

Here is an example of calculating the sum of numbers from 1 to 10 and storing the result in a variable called sum:

/* Calculate the sum of numbers from 1 to 10 */
unsigned int k, sum;
k = 1;
sum = 0;
while(k <= 10)
{
     sum = sum + k;
     k++;
}

It is possible to have a while statement with no body. Such a statement is useful, for example, if we are waiting for an input port to change its value. An example follows where the program will wait as long as bit 0 of PORTB (PORTB.0) is at logic 0. The program will continue when the port pin changes to logic 1:

while(PORTB.0 == 0);     // Wait until PORTB.0 becomes 1

or

while(PORTB.0);

It is also possible to have nested while statements.

do Statement

A do statement is similar to a while statement except that the loop executes until the condition becomes false, or, the loop executes as long as the condition is true. The condition is tested at the end of the loop. The syntax of a do statement is:

do
{
      Statements;
} while (condition);

The first iteration is always performed whether the condition is true or false. This is the main difference between a while statement and a do statement.

The following code shows how to set up a loop to execute 10 times using the do statement:

/* Execute 10 times */
k = 0;
do
{
      Statements;
      k++;
} while (k < 10);

The loop starts with k = 0, and the value of k is incremented inside the loop after each iteration. At the end of the loop k is tested, and if k is not less than 10, the loop terminates. In this example because k = 0 is at the beginning of the loop, the value of k is 10 at the end of the loop.

An endless loop will be formed if the condition is not modified inside the loop, as shown in the following example. Here k is always less than 10:

/* An endless loop */
k = 0;
do
{
      Statements;
} while (k < 10);

An endless loop can also be created if the condition is set to be true all the time:

/* An endless loop */
do
{
      Statements;
} while (k = k);

It is also possible to have nested do statements.

Unconditional Modifications of Flow

goto Statement

A goto statement can be used to alter the normal flow of control in a program. It causes the program to jump to a specified label. A label can be any alphanumeric character set starting with a letter and terminating with the colon (“:”) character.

Although not recommended, a goto statement can be used together with an if statement to create iterations in a program. The following example shows how to set up a loop to execute 10 times using goto and if statements:

/* Execute 10 times */
k = 0;
Loop:
      Statements;
      k++;
if(k < 10)goto Loop;

The loop starts with label Loop and variable k = 0 at the beginning of the loop. Inside the loop the statements are executed and k is incremented by 1. The value of k is then compared with 10 and the program jumps back to label Loop if k < 10. Thus, the loop is executed 10 times until the condition at the end becomes false. At the end of the loop the value of k is 10.

continue and break Statements

continue and break statements can be used inside iterations to modify the flow of control. A continue statement is usually used with an if statement and causes the loop to skip an iteration. An example follows that calculates the sum of numbers from 1 to 10 except number 5:

/* Calculate sum of numbers 1,2,3,4,6,7,8,9,10 */
Sum = 0;
i = 1;
for(i = 1; i <= 10; i++)
{
      if(i == 5) continue;    // Skip number 5
      Sum = Sum + i;
}

Similarly, a break statement can be used to terminate a loop from inside the loop. In the following example, the sum of numbers from 1 to 5 is calculated even though the loop parameters are set to iterate 10 times:

/* Calculate sum of numbers 1,2,3,4,5 */
Sum = 0;
i = 1;
for(i = 1; i <= 10; i++)
{
      if(i > 5) break;    // Stop loop if i > 5
      Sum = Sum + i;
}

Mixing mikroC with Assembly Language Statements

It sometimes becomes necessary to mix PIC microcontroller assembly language statements with the mikroC language. For example, very accurate program delays can be generated by using assembly language statements. The topic of assembly language is beyond the scope of this book, but techniques for including assembly language instructions in mikroC programs are discussed in this section for readers who are familiar with the PIC microcontroller assembly languages.

Assembly language instructions can be included in a mikroC program by using the keyword asm (or _asm, or __asm). A group of assembly instructions or a single such instruction can be included within a pair of curly brackets. The syntax is:

asm
{
      assembly instructions
}

Assembly language style comments (a line starting with a semicolon character) are not allowed, but mikroC does allow both types of C style comments to be used with assembly language programs:

asm
{
      /* This assembly code introduces delay to the program*/
      MOVLW 6             // Load W with 6
      ...............
      ...............
}

User-declared C variables can be used in assembly language routines, but they must be declared and initialized before use. For example, C variable Temp can be initialized and then loaded to the W register as:

unsigned char Temp = 10;
asm
{
      MOVLW Temp                  // W = Temp = 10
      ..................
      ..................
}

Global symbols such as predefined port names and register names can be used in assembly language routines without having to initialize them:

asm
{
      MOVWF PORTB
      .....................
      .....................
}

PIC Microcontroller Input-Output Port Programming

Depending on the type of microcontroller used, PIC microcontroller input-output ports are named as PORTA, PORTB, PORTC, and so on. Port pins can be in analog or digital mode. In analog mode, ports are input only and a built-in analog-to-digital converter and multiplexer circuits are used. In digital mode, a port pin can be configured as either input or output. The TRIS registers control the port directions, and there are TRIS registers for each port, named as TRISA, TRISB, TRISC, and so on. Clearing a TRIS register bit to 0 sets the corresponding port bit to output mode. Similarly, setting a TRIS register bit to 1 sets the corresponding port bit to input mode.

Ports can be accessed as a single 8-bit register, or individual bits of a port can be accessed. In the following example, PORTB is configured as an output port and all its bits are set to a 1:

TRISB = 0;                // Set PORTB as output
PORTB = 0xFF;             // Set PORTB bits to 1

Similarly, the following example shows how the 4 upper bits of PORTC can be set as input and the 4 lower bits of PORTC can be set as output:

TRISC = 0xF0;

Bits of an input-output port can be accessed by specifying the required bit number. In the following example, variable P2 is loaded with bit 2 of PORTB:

P2 = PORTB.2;

All the bits of a port can be complemented by the statement:

PORTB = ~PORTB;

Programming Examples

In this section, some simple programming examples are given to familiarize the reader with programming in C.

Example 3.2

Write a program to set all eight port pins of PORTB to logic 1.

Solution 3.2

PORTB is configured as an output port, and then all port pins are set to logic 1 by sending hexadecimal number 0xFF:

void main()
{
      TRISB = 0;           // Configure PORTB as output
      PORTB = 0xFF;        // Set all port pins to logic a
}

Example 3.3

Write a program to set the odd-numbered PORTB pins (bits 1, 3, 5, and 7) to logic 1.

Solution 3.3

Odd-numbered port pins can be set to logic 1 by sending the bit pattern 10101010 to the port. This bit pattern is the hexadecimal number 0xAA and the required program is:

void main()
{
      TRISB = 0;           // Configure PORTB as output
      PORTB = 0xAA;        // Turn on odd numbered port pins
}

Example 3.4

Write a program to continuously count up in binary and send this data to PORTB. Thus PORTB requires the binary data:

00000000
00000001
00000010
00000011
............
............
11111110
11111111
00000000
............

Solution 3.4

A for loop can be used to create an endless loop, and inside this loop the value of a variable can be incremented and then sent to PORTB:

void main()
{
      unsigned char Cnt = 0;
      for(;;)                  // Endless loop
      {
           PORTB = Cnt;       // Send Cnt to PORTB
           Cnt++;             // Increment Cnt
      }
}

Example 3.5

Write a program to set all bits of PORTB to logic 1 and then to logic 0, and to repeat this process ten times.

Solution 3.5

A for statement can be used to create a loop to repeat the required operation ten times:

void main()
{
      unsigned char j;
      for(j = 0; j < 10; j++)   // Repeat 10 times
      {
      PORTB = 0xFF;             // Set PORTB pins to 1

      PORTB = 0;                // Clear PORTB pins
      }
}

Example 3.6

The radius and height of a cylinder are 2.5cm and 10cm respectively. Write a program to calculate the volume of this cylinder.

Solution 3.6

The required program is:

void main()
{
      float Radius = 2.5, Height = 10;
      float Volume;
      Volume = PI *Radius*Radius*Height;
}

Example 3.7

Write a program to find the largest element of an integer array having ten elements.

Solution 3.7

At the beginning, variable m is set to the first element of the array. A loop is then formed and the largest element of the array is found:

void main()
{
      unsigned char j;
      int m, A[10];
      m = A[0];    // First element of array
      for(j = 1; j < 10; j++)
      {
           if(A[j]> m)m = A[j];
      }
}

Example 3.8

Write a program using a while statement to clear all ten elements of an integer array M.

Solution 3.8

As shown in the program that follows, NUM is defined as 10 and variable j is used as the loop counter:

#define NUM 10
void main()
{
      int M[NUM];
      unsigned char j = 0;
      while (j < NUM)
      {
           M[j] = 0;
           j++;
      }
}

Example 3.9

Write a program to convert the temperature from °C to °F starting from 0°C, in steps of 1°C up to and including 100°C, and store the results in an array called F.

Solution 3.9

Given the temperature in °C, the equivalent in °F is calculated using the formula:

F = (C – 32.0)/1.8

A for loop is used to calculate the temperature in °F and store in array F:

void main()
{
      float F[100];
      unsigned char C;
      for(C = 0; C <= 100; C++)
      {
           F[C] = (C – 32.0) / 1.8;
      }
}

Summary

There are many assembly and high-level languages for the PIC18 series of microcontrollers. This book focuses on the mikroC compiler, since it is easy to learn and a free demo version is available that allows users to develop programs as large as 2K in size.

This chapter presented an introduction to the mikroC language. A C program may contain a number of functions and variables plus a main program. The beginning of the main program is indicated by the statement void main().

A variable stores a value used during the computation. All variables in C must be declared before they are used. A variable can be an 8-bit character, a 16-bit integer, a 32-bit long, or a floating point number. Constants are stored in the flash program memory of PIC microcontrollers, so using them avoids using valuable and limited RAM memory.

Various flow control and iteration statements such as if, switch, while, do, break, and so on have been described in the chapter, with examples.

Pointers are used to store the addresses of variables. As we shall see in the next chapter, pointers can be used to pass information back and forth between a function and its calling point. For example, pointers can be used to pass variables between a main program and a function.

Exercises

1.

Write a C program to set bits 0 and 7 of PORTC to logic 1.

2.

Write a C program to count down continuously and send the count to PORTB.

3.

Write a C program to multiply each element of a ten element array by 2.

4.

Write a C program to add two matrices P and Q. Assume that the dimension of each matrix is 3 × 3 and store the result in another matrix called W.

5.

Repeat Exercise 4 but this time multiply matrices P and Q and store the product in matrix R.

6.

What do the terms variable and constant mean?

7.

What does program repetition mean? Describe the operation of while, do-while, and for loops in C.

8.

What is an array? Write example statements to define the following arrays:

  1. An array of ten integers

  2. An array of thirty floats

  3. A two-dimensional array having six rows and ten columns

9.

Trace the operation of the following loops. What will be the value of variable z at the end of each loop?

  1. unsigned char j = 0, z = 0;
    while(j < 10)
      {
         z++;
            j++;
      }
  2. unsigned char z = 10;
    for(j = 0; j < 10; j++)z––;

10.

Given the following variable definitions, list the outcome of the following conditional tests in terms of “true” or “false”:

unsigned int a = 10, b = 2;
if(a > 10)
if(b >= 2)
if(a == 10)
if(a > 0)

11.

Write a program to calculate whether a number is odd or even.

12.

Determine the value of the following bitwise operations using AND, OR, and EXOR operations:

Operand 1:    00010001
Operand 2:    11110001

13.

How many times does each of the following loops iterate, and what is the final value of the variable j in each case?

  1. for(j = 0;  j < 5; j++)
  2. for(j = 1;  j < 10; j++)
  3. for(j = 0;  j <= 10; j++)
  4. for(j = 0;  j <= 10; j += 2)
  5. for(j = 10; j > 0; j −= 2)

14.

Write a program to calculate the sum of all positive integer numbers from 1 to 100.

15.

Write a program to evaluate factorial n, where 0! and 1! evaluate to 1 and n! = n × (n – 1)!

16.

Write a program to calculate the average value of the numbers stored in an array. Assume that the array is called M and has twenty elements.

17.

Modify the program in Exercise 16 to find the smallest and largest values of the array. Store the smallest value in a variable called Sml and the largest value in a variable called Lrg.

18.

Derive equivalent if-else statements for the following tests:

  1. (a > b) ? 0 : 1

  2. (x < y) ? (a > b) : (c > d)

19.

Given that f1 and f2 are both floating point variables, explain why the following test expression controlling the while loop may not be safe:

do
{
      ...............
      ...............
} while(f1 != f2);

Why would the problem not occur if both f1 and f2 were integers? How would you correct this while loop?

20.

What can you say about the following while loop?

k = 0;
Total = 0;
while (k < 10)
{
     Sum++;
     Total += Sum;
}

21.

What can you say about the following for loop?

Cnt = 0;
for(;;)
{
     Cnt++;
}

 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset