Chapter 1: Introduction to Programming 1.1 What is problem solving ? We encounter a variety of problems in our daily lives, both ‘big’ and ‘small’. Some are exciting and challenging to solve. Some are dull and boring which we feel like postponing all the time (like, mopping the house)... How do we solve them? Do we have any broad strategies for solving problems? At least some general guide lines? Let us start with a simple daily life problem... If I have to look into the dictionary for the word 'procrastinate', How do we do it ? Should I start from page 1? Keep going page by page ? How do I find out the meaning ? What procedure should I adopt ? What is the best (fastest way) to find the meaning of the word I want ? The same problem can also be viewed through a different but a similar example. If I would like to find out the phone number of Mr Zimba. How should I go about in doing it? (We know that, almost all of us, involuntarily will go to the last few pages by skipping a big bunch of pages in the beginning. We internally know that, ‘Z’ starts only towards the end of the directory. The key question would be, how do we translate this involuntary action into a methodical and stepbystep approach ? What algorithm should we adopt? All these problems certainly would require procedural steps, that are less time consuming and more efficient. 1.1.1 Some aspects of problem solving • Identify the problem. Then, clearly (not cleverly !) write down the problem definition. • Get started with the problem by trying to understand the intricacies • Look for possible (alternative solution strategies; choose specific examples!) • Look for generalizations and similarities with other known problems (and their solutions) • Select the best solution strategy • Write down the solution procedure into simple and step by step set of instructions. We encounter a variety of problems in our daily lives. A vast number of them can be classified as calculation oriented. For example think about buying groceries from Tashi store. The person servicing has to enter the code of an items which in turn will pickup the corresponding price of the item bought. If there are several such items, all of them are entered and the list of all the items bought is displayed. If a big note is given, the exact amount to be paid is deducted and the excess is returned. If there can be so many calculations associated with such a simple everyday example, then imagine the huge and complex number of calculations associated with huge systems like, banks, airlines, Revenue authorities, financial institutions etc... Solutions to many problems in science can also be cast into a calculation (more precisely, arithmetic calculation) based approaches. 1.2 Algorithms In order to solve a problem it is necessary to evolve a detailed and precise stepbystep method of solution. An algorithm is a sequence of steps or instructions for solving a problem. Attributes of an Algorithm : Precision: An algorithm should be precise and simple. Each step of the algorithm should be precisely defined, that is, the steps must be unambiguous. Uniqueness: An algorithmic step should have only one way (unique) to perform a task. Finiteness : An algorithm should end in finite number of instructions. It should not prolong infinitely. (We can’t program a statement like, Tell me the sum of values up to infinity, although summing up to certain huge number is meaningful). Input : It should have some initial data/inputs (they can be called input variables). Output: An algorithm should focus on the end result (output) Generality: An algorithm should be general. As a simple example, if the student is asked to program 1+2+... + 10, It is highly recommended that, the student should program for the case of 1+2+...+N, where N can be 10. The same program can indeed be used later for any value of N. Furthermore, N becomes the input and Sum of the values can be called as Output. Note that, N can’t be infinity (the mathematical notion is fine. But, it should be representable on the machine as a finite number). Looking for the above characteristics in every algorithm would give the students the much needed confidence on how to write an algorithm. Example 1.2.1 A Simple Real Life Example of an Algorithm : Let's say that you have a friend arriving at the bus stop in Trashigang, and your friend needs to get from the bus stop to your hostel in Sherubtse College, Kanglung. Here are three different algorithms that you might give your friend for getting to your hostel: • • • The taxi algorithm: 1. Go to the taxi stand. 2. Get in a taxi. 3. Give the driver my hostel name. The callme algorithm: 1. When your bus arrives, call my cell phone. 2. Meet me at the bus stand. The bus algorithm: 1. Get on a bus. 2. Get off in college gate, call my cell phone. 3. Meet me at the college gate. All three of these algorithms accomplish exactly the same goal, but each algorithm does it in completely different way. Each algorithm also has a different cost and a different travel time. You choose the algorithm based on the circumstances. Example 1.2.2 I am hungry. I would like to buy coffee from a coffee shop? What should I do ? (What are the algorithmic steps for buying coffee from a coffee shop?) The approach: What are the inputs ? (money) What is the output ? (Hot coffee from the coffee) Then, how should the steps be written in a sequence ? The Algorithmic steps : Step 1: Locate a coffee shop. Step 2: Ask a coffee from a service person. Step 3: Pay money. Step : get your coffee and enjoy Example 1.2.3 Algorithm to add two integer numbers Inputs: two numbers Output: sum of two number Sequential steps: Step 1: Read the first number Step 2: Read the second number Step 3: Add first number and second number Step 4: Print result (sum) Quick Exercise 1. Write down the algorithmic steps to prepare Ema Datse (What are the inputs ? What is the output ? What are the sequential steps ? ) 2. Write down the algorithmic steps to find the average of two integer numbers (What are the inputs ? What is the output ? What are the sequential steps ? ) 1.3 Flow Charts A flowchart is a graphical representation of an algorithm. These flowcharts play a vital role in the programming of a problem and are quite helpful in understanding the logic of complicated and lengthy problems. Once the flowchart is drawn, it becomes easy to write the program in any high level language. Often we see how flowcharts are helpful in explaining the program to others. Hence, it is correct to say that a flowchart is a must for the better documentation of a complex program. Flowcharts are usually drawn using some standard symbols; however, Start or end of the program Computational steps or processing function of a program Input or output operation Decision making and branching Connector or joining of two parts of program The following are some guidelines in flowcharting: a. In drawing a proper flowchart, all necessary requirements should be listed out in logical order. b. The flowchart should be clear, neat and easy to follow. There should not be any room for ambiguity in understanding the flowchart. c. The usual direction of the flow of a procedure or system is from left to right or top to bottom. d. Only one flow line should come out from a process symbol. or e. Only one flow line should enter a decision symbol, but two or three flow lines, one for each possible answer, should leave the decision symbol. Only one flow line is used in conjunction with terminal symbol. • • • If the flowchart becomes complex, it is better to use connector symbols to reduce the number of flow lines. Avoid the intersection of flow lines if you want to make it more effective and better way of communication. Ensure that the flowchart has a logical start and finish. It is useful to test the validity of the flowchart by passing through it with a simple test data. Example 1.3.1 Problem : Write an algorithm and draw the flowchart for finding the average of two numbers Algorithm: Input: two numbers x and y Output: the average of x and y Steps: 1. 2. 3. 4. 5. START input x input y sum = x + y average = sum /2 output average Input x Input y Sum = x + y Average = sum/2 Output Averag Quick exercise END Problem : Write an algorithm for finding the area of a rectangle Hints: • • • define the inputs and the outputs define the steps draw the flowchart 1.4 Computer Systems Having seen what is a problem solving all about and some of its facet. It is time for us to handle the key question on how do we implement the solution on a computer ? Let us take a brief look at the computer systems and some of their functionalities. Computers are very simple minded (in fact, dumb is the most appropriate word) but, very powerful machines. At the outset, the two words look contradictory but, there is truth in it. Computer is like an indefatigable workaholic. Its ability comes from its speed. It can process millions of calculations in split seconds, when it is appropriately programmed to do so. However, it should be pointed out that, they are capable of performing only simple tasks. For example, if we can go to the machine level (hardware level), they can obey simple commands such as, add two numbers, assign a value to a location, compare two numbers etc... Such instruction sets are called machine language. The most surprising part of these instructions is that, they can be combined in a coherent and complex way to perform a rich set of useful operations. Let us look at a few simple definitions. Computer : A machine designed to perform operations (e.g. add two numbers, compare two numbers, etc.), when specified with a set of instructions called program Software : Consists of all intangible forms (ideas, algorithms, programs). These programs control the computer and allow the user to perform useful tasks: Eg. word processor, accounting, library catalog, web browser, email) Hardware : Consists of all tangible (visible) objects of the computer Ex: CPU, Mother Board, Terminal (Monitor), Hard Disk, key board, mouse, printers etc. Three Kinds of software: 1. Operating system: acts as interface between user programs or applications, and the hardware • Examples: Windows, DOS, Unix, Linux, ... • Example programs: copy file, save file, delete file, ... 2. Application software: someone else wrote it for you • Examples: word processor, spreadsheet, web browser, ... 3. User programs: what you write • e.g. write a C program to calculate the area of a circle with a given diameter • you’ll see plenty of examples in this module! Computer hardware architecture Most computers have the same basic architecture: May be, in a more simplistic sense, we can say : A typical human being has 2 eyes, 2 ears, 1 nose,1 heart, 1 brain etc... which can be called hardware. The ideas, the thought process embedded in the brain, the nervous system is ‘software’. But, please note that the ‘software’ is executed by the underlying ‘hardware’. In a bit advanced modules you are going to learn that, both hardware and software are logically equivalent. Any operation performed by software can in principle, be built into the hardware as well. Furthermore, any instruction which is executed by the hardware, can also be simulated by the software. However, the decision to put certain operations in hardware is indeed based on factors such as, cost, reliability, speed and frequency of expected changes etc. There are no hard and fast rules and designers with different goals may often make different decisions. CPU, is the commonly used acronym for the Central Processing Unit. Well known examples are, Pentium – III, Pentium IV, Celeron, etc. This guy, CPU is essentially the heart of the computer. It consists of the processor and the ALU – The Arithmetic Logic Unit. The processor, is the part which controls all the other parts of the computer. When we give an instruction through the keyboard, it essentially does the following steps: (1) It accepts input values, stores them in memory (2) It interprets the instruction (3) For example, if we want to add two numbers in memory, the processor will retrieve the Values from memory and sends them to ALU (4) ALU performs addition and the processor then, stores the result in memory. The microprocessor is essentially, the CPU. It is contained in a single IC chip, which contains Millions of components in a small area. RAM : Random access memory (Or) Volatile memory. This is what we refer to when we simply say memory. Something loaded into this memory space is simply lost when the computer is switched off. RAM is the workspace of the computer. RAM is like a large work table on which we place many different things. There are lots of issues like, the internal design of the computer and the functionality of each of the components etc... This field of study is known as computer architecture. However, it is much more meaningful to focus on issues like how to make better use of the computer, which is the objective of CS101. Quick Exercise Categorize the following into either Hardware or Software (a) MS POWER POINT (b) C Compiler (c) CD Writer (d) Pentium Processor (e) CISCO Router (f) ADOBE Acrobat Reader (f) Modem (g) A file on the UNIX system 1.5 Computer Programming To carry out some task, the computer must be told exactly what to do and how to do it. An algorithm is the series of steps involved in carrying out a particular task. To carry out these steps, the algorithm must be expressed in a form that the computer can understand. An algorithm expressed in such a form is called a program. The user can then tell the computer to execute or run this program. A computer program is written in a programming language. “Natural” languages are for communicating with people. Programming languages are for communicating with computer ** Refer slides ** Chapter 2: Constants, Variables and Data Types 2.1 Constants Constants in C refer to fixed values that do not change during the execution of a program. C supports several types of constants; integer, real, single character and string constants. Integer constants: refers to a sequence of digits. Eg. 45 Real constants: also called floating point constants refers to the number containing fractional parts. Eg. 23.89 Single character constants: or simply character constant contains a single character enclosed within a pair of single quote marks. Eg. '5' , 'X', ';', ' ' Note that the character constant '5' is not the same as the number 5. String constants: is a sequence of characters enclosed in double quotes. The characters maybe letters, numbers, special characters and blank space. Eg. “Hello” , “2356” , “well done” , “5+4”, “X” Remember that the character constant 'X' is not equivalent to the single character string constant “X”. Escape sequences : Certain non-printing characters, as well as the backslash (\), single (') and double (“) can be expressed in terms of escape sequences. These are special character that are used in output functions. 2.2 Keywords Given below are the keywords (also called reserve words) of the C language. These words have definite meaning associated with them. Keywords serve as building blocks for program statements. All keywords must be written in lowercase. We will discuss the meaning of each of these words, only in an appropriate context (we may not cover the meaning of all words in our CS101 !). 2.3 Identifiers Identifiers refer to the names of variables, functions and arrays. These are user-defined names and consist of a sequence of letters and digits, with a letter as a first character. There are some rules in constructing identifiers (It is always good to have some rules to avoid meaningless names). As a matter of convention, our parents do not name us with numbers like, 6, 28, although they are nice and perfect numbers in mathematics. Recently in China, a father named his son as @. This was promptly rejected by the federal government. So, we understand that even unwritten conventions are well followed. Rules in choosing identifier names : 1. First character must be an alphabet ('a'..'z', 'A'..'Z') or underscore (_). 2. Must consist of only letters('a'..'z', 'A'..'Z'), digits ('0'..'9') or underscore (_). 3. C identifiers are case sensitive, so cs101, CS101 and cS101 are distinct identifiers. 4. Cannot use a keyword. 5. Must not contain white space. There are several conventions and issues in choosing the right identifier names. Perhaps it is too early to talk about those conventions and other good practices. But, for the time being, it is good enough to know that choosing appropriate names (identifiers), which are relevant to the context is an art, with a view on the program readability. Some Legal/ Illegal identifiers L2 norm → Illegal (blank space not allowed) Powerpoint → Legal automobile-engine → Illegal (hyphen not allowed) integer → Legal (only int is a keyword; But better to avoid ) vector_sum → Legal (using an underscore results in readable programs) $ToPound → Illegal (can not have with a $ symbol) Quick Exercise Identify legal and illegal identifiers. Suggest an alternative if it is illegal. (a) amino-acids (b) 2nd_number (c) sum6 (d) ATCG (e) short (f) graph (g) main (h) int 2.4 Variables "Variable"s provide a mechanism to identify each data item with a name. This is just like our names in real life. They have a location in memory. Unlike constants that remain unchanged during the execution of a program, a variable may take different values at different times during execution. In C programming, all variables should be declared before their first use. As mentioned earlier (in Identifier), variable names may consist of letters, digits and underscore (_) characters. In addition to the rules of identifiers, variable length should not be more than eight characters. Some examples of valid variable names are: a, avg, sum_ egg, n1, T_money. Invalid variable names are: 1n, $avg, 132, sum egg. 2.5 Data Types C supports several different types of data, each of which may be represented differently within the computer's memory. The basic data types and typical memory requirements are listed in the table below: Data Type Description Format Typical Memory Requirements int Integer quantity %d 2 bytes (16 bits) char Single character %c 1 byte (8 bits) float Floating-point number %f 1 word (4 bytes=32 bits) double Double-precision floating-point number %lf 2 words (8 bytes = 64 bits) 2.6 Variable Declaration and Assignment of Values All the variables must be declared before they are used in the program. Declaration does two things: 1. It tells the compiler what the variable name is. 2. It specifies what type of data that variable will hold. The syntax for declaring a variable is: data_type v1,v2,v3,..vn; where data_type is the valid data types and v1,v2,v3,..vn are the names of variables. Variables are separated by commas. A declaration must end with a semicolon. Example: Valid declarations are: int a, b, c; float f1, f2; char c; a, b, and c are declared to be integer variables, f1 and f2 are floating point variables, c is a char-type variable. These declarations could also have been written as follows: int a; int b; int c; float f1; float f2; char c; Example program: Declaration of variables main() /*Program name */ { /* Declaration */ int a, b, c; float f1, f2; char c; }/*Program ends */ main()is the beginning of the program. The opening brace { signals the execution of the program. Declaration of variables is usually done immediately after the opening brace of the program. The lines beginning with / * and ending with */ are known as comment lines. These are used in the program to enhance its readability and understanding. They help the programmers and other users in understanding the various functions and operations of a program. Comment lines are not executable statements and therefore anything between /* and */ is ignored by the compiler. To assign values to the variable, assignment operator (=) is used. Assignment is of the form: variable_name = value; But before the assignment, the variable must be declared. Examples: int a,b; a = 5; b = 2; It is also possible to assign a value to the variable at the time of declaration. data_type variable_name = value; Examples: int a = 5; char yes = 'y'; More than one variable can be assigned in one statement using multiple assignment operators. Example: int a,b; a=b=5; Example program: Assignments main( ) { /* declaration */ int a, b; /* declaration and assignment */ int var = 5; int a, b=6, c; /* declaration and multiple assignment */ int p, q, r; p = q = r = 6; } 2.8 scanf( ) and printf( ) Both scanf( ) and printf( ) are library functions which requires the header file #include<stdio.h> before main() function. scanf() is used for reading the input from the keyboard and printf() will write the output on the screen. scanf( ) The general format of input function is: scanf(“format-string”, &var1, &var2, ...,&varn); The format-string contains the format of data to be stored in the list of variables var1, var2, ...,varn . The ampersand symbol & before each variable name is an operator that specifies the variable name's address. We must always use this operator, otherwise unexpected results may occur. Examples: If there is a variable n1, declared as an int, we can read the variable as : scanf("%d", &n1); when this statement is encountered by the computer, the execution stops and waits for the value of the variable n1 to be typed in. Since the format-string “%d” specifies that an integer value is to be read, we have to type in the value in integer form. Once the number is typed in and the 'Return' key is pressed, the computer then proceeds to the next statement. Thus the use of scanf provides an interactive feature and makes the program 'user friendly'. The value is assigned to the variable n1. We can also read more variables in the same statement using scanf() as follows : int n1, n2, n3; scanf("%d%d%d", &n1, &n2, &n3); However, if our objective is to read real values using these variables, we should declare them as float data type. Furthermore, the above statements should be written as follows: float n1, n2, n3; scanf("%f%f%f", &n1, &n2, &n3); While, reading a mixture of integer and non-integer values through the keyboard, it could be written as follows (notice the right sequence being used for the format descriptors): int i1, i2 ; float f1, f2 ; scanf("%d%f%f%d", &i1, &f1, &f2, &i2); printf( ) The general format of output function is: printf(“format-string”, var1,var2,var3,...,varn); This library function is useful for printing the values and other desired text on the screen. Format string (in double quotes) and the variable names are the required inputs. With in the double quotes, we use the appropriate format descriptors just like the scanf() function. But, for printing the variable, we DO NOT use the address of (&) operator. Example: printf("Have you eaten lunch today? \n"); The printf( ) statement above will copy the contents Have you eaten lunch today? as it is on to the screen. \n refers to skip to a new line. Example: int x ; float y; x=2; y = 3.52; printf ("x = %d\n", x); will write on the screen as: x = 2 printf ("x : %d, y : %f\n", x, y); will write on the screen as: x : 2, y : 3.520000 Four zeros after 3.52 could be a little puzzling, but %f specifier typically writes output upto 6 decimal places. To force only to 2 decimal places, we can write as follows: printf ("x : %d, y : %.2f\n", x, y); Result would be : x : 2, y : 3.52 More examples: int i1, i2 ; float f1, f2 ; i1 = 3; i2 = 4; f1 = 7.1; f2 = 1.2 ; printf("%d,%f,%d,%f.", i1, f1, i2, f2); Screen output will be : 3,4,7.100000,1.200000. Please notice the order of the format specifiers and the commas – how they are exactly reproduced on the screen. Example program: printf( ) and scanf( ) main( ) { int n1; float n2; /* Input data from keyboard */ printf(“Enter an integer number:”); scanf(“%d”, &n1); printf(“\n Enter a floating point number:”); scanf(“%f”, &n2); /* output the data entered by the user */ printf (“Integer number = %d”, n1); printf (“Floating point number = %f”, n2); } Quick exercise: 1. Write printf statements to output the following: 1. int a, b, c; 2. float x, y, z; 3. char c; 2. Write printf statements to print the following table: Name Average Pass/Fail Pema 30 Fail Tashi 80 Pass Lekey 45 Pass 3. Write scanf function to read two integers, one floating-point number, one character and a string. 2.9 Symbolic Constants A symbolic constant is a name that substitutes for a sequence of characters. The characters may represent a numeric constant, a character constant or a string constant. Thus, a symbolic constant allows a name to appear in place of a numeric constant, a character constant or a string. Symbolic constants are usually defined at the beginning of a program and does not end with semicolon. A symbolic constant is defined by: #define symbolic-name constant-value Example: A C program contains the following symbolic constant definitions. #define PI 3.141593 #define TAXRATE 0.23 #define TRUE 1 #define FALSE 0 #define FRIEND “Tshering” Suppose the program contains the statement area = PI * radius * radius; During the compilation process, each occurrence of a symbolic constant will be replaced by its corresponding value. Thus, the above statement will become area = 3.141593 * radius * radius; ********************************************************************************************* Please bring all the errors to my notice either by sending an email to [email protected] or in person. I will appreciate your kind gesture! ********************************************************************************************** Chapter 3: Operators and Expressions 3.1 Operators Operator is a symbol that tells the computer to perform certain actions on variables (or) expressions. An expression is a sequence of operands and operators that reduces to a single value. C operators can be classified into a number of categories. They include: 1. Arithmetic operators 2. Relational and Equality operators 3. Logical operators 4. Assignment operators 5. Increment and decrement operators 6. Conditional operators 3.1.1 Arithmetic Operators The multiplication and division symbols are typically * and /. The meaning of + and – follows the usual Math. Style. But, % is called the remainder (or) modulus operator. The modulus operator produces the remainder of an integer division. All these operators are explained below with the help of an example. Name Symbol Expression Addition + (x+y) Subtraction - (x-y) Multiplication * (x*y) Division / (x/y) Modulus % (x%y) Examples: If there are three variables x, y and z, which store integer values (declared as: int x,y,z;), and if we have the following initial values : x = 5; y = 2; (This is called initialization) then, different arithmetic operations are carried out on the two variables. z = x+y; /* z stores 7 */ z = x-y; /* z stores 3 */ z = x*y; /* z stores 10 */ z = x/y; z = x%y ; /* z stores 2 . Decimal part truncated */ /* z stores 1 */ In the statements above, there should be no confusion for addition(+), subtraction (-) and multiplication(*). But, the serious question is with respect to the division operator( /). The division between two integers results in truncated integer value and not a rounded off value. The Key point is that, we do not care about the value after the decimal place, while the integer division is performed. To reinforce this idea further, 1/3 will be 0, 2/3 will be 0 and 3/3 will be 1. Note that, the value after the decimal is simply discarded. Obviously, this would also yield a value of zero for 99/100… However, if you insist on getting the mathematically correct result, we should perform what is called a floating point division. To achieve this, the variables should be declared as a float variable. Furthermore, the above expression of division should be modified to one of the cases below (z should be declared as float): (a) z = 5.0 /2 ; (b) z = 5 /2.0 ; (C) z = 5.0/2.0 ; and a statement z = 5/2 would only store 2.0, while all the cases above will store 2.5. Now coming to the modulus operator, 5%2 , results in 1. We can think of 5/2 as 2 1/2 , where 1 is the remainder (when 5 is divided with 2). Hence, 5%2 gives the remainder, which is 1. This / (division) and % (modulus) operators are very useful operators. One should master the meaning of these arithmetic operations early on, to avoid any confusion. Multiply (*), divide (/), and modulus (%) have precedence over add (+) and subtract (-). Thus, (1+2) * 4 yields 12, while 1+2*4 yields 9. Quick Exercise: 1. Write the value of the variable stored in the questions below (x is declared as a float variable, while z is declared as an int variable) ? (a) z = 1940 % 4; (b) z = 200/3 ; (c) z = 20%4; (d) x = 19.0/4; (e) x = 19/4 ; (f) x = 19.0/4.0 ; (g) x = 10/1.5; (h) z = 10/1.5; 2. Write a small program which would read two variables from the user and will compute the sum and product of the two variables. Input is through the keyboard and output on the screen. 3.1.2 Relational and Equality Operators Relational and Equality operators express the relation between two operands (or variables). We make use of the operators to extract the relation between two variables. They are very useful in building test conditions and to formulate expressions. The symbol used for the operation, depends on the language. In C, we use the following symbols. Operator Meaning < Less than <= Less than or equality > Greater than >= Greater than or equality == Equal to != Not equal to Example: Suppose that i, j and k are integer variables whose values are 1, 2, and 3, respectively. Several logical expressions involving these variables are shown below: Expression i<j (i + j) >= k (j +k) > (I + 5) k != 3 j = =2 Interpretation True True False False True Value 1 1 0 0 1 Quick exercise: Evaluate the following Test Conditions: int x, y, z; x = 3; y = 5; z = 8; (a) (y > x) (b) (y >= x) (c) (y < z) (d) (y <= z) (e) (x+y == z) (f) (y-x != z-x) Note that all the above Test Conditions are True 3.1.3 Logical Operators In addition to the relational operators, C has the following three logical operators. Operator && Meaning Logical AND || Logical OR ! NOT The logical operators && and || are used when we test more than one condition and make decision. Given below is a truth table, which is the result of applying &&, || and ! operators on op1 and op2. op1 True True False False op2 True False True False op1 && op2 True False False False op1 || op2 True True True False !op1 False False True True !op2 False True False True Example: Suppose that i is an integer whose value is 7,f is a float-point variable whose value is 5.5, and c is a character variable that represents the character ‘w’. Expression Interpretation Value (i >= 6) && (c = = ‘w’) True 1 (i >= 6) || (c = = 119) True 1 (f < 11) && (i > 100) False 0 (c != ‘p’) || ((i + f) <= 10) true 1 3.1.4 Assignment operators Assignment operators are used to assign the result of an expression to a variable. We have seen the usual assignment operator, ‘=’. In addition, C has a set of shorthand assignment operators. Example: Statement with simple assignment operator a=a+b a=a-b a = a * (n + 1) a = a / (n+1) a=a%b Statement with shorthand operator a += b a -= b a *= n + 1 a /= n + 1 a %= b 3.1.5 Increment and decrement operators C allows two very useful operators not generally found in other languages. These are the increment and decrement operators: ++ and - The operator ++ adds 1 to the operand, while -- subtracts 1. However, the operator ++ and - - can be used either as pre increment or post increment form. Increment Operator ++ post increment x++; pre increment ++x; Decrement Operator -post decrement x--; pre decrement --x; A pre increment operator first adds 1 to the operand and then the result is assigned to the variable on left. On the other hand, a post increment operator first assigns the value to the variable on left and then increments the operand. Example: 3.1.6 Conditional operators Conditional/ternary operator is one which contains three operands. The only ternary operator available in C language is conditional operator pair “?:”. It has the following syntax: exp1 ? exp2 : exp3; This operator works as follows: exp1 is evaluated first. If the result is true then exp2 is executed otherwise exp3 is executed. Example: a = 10; b = 15; x = (a > b ? a : b); in this expression the value of b (15) will be assigned to x. ********************************************************************************** Please bring all the errors to my notice either by sending an email to [email protected] or in person. I will appreciate your kind gesture! *********************************************************************************** Chapter 4: Decision Control Statements Programming statements are executed in order – first statement, second statement and so on. (in a sequential form). The control statements of a programming language allow this execution to be altered. In fact, the ability to skip a line or a block of code is important when we want to execute one outcome from all other outcomes, based on a Test Condition. This mandates the need to master the statements that allow us to control the program flow. 4.1 Test Conditions We implement a variety of decisions in our daily lives. At every point of the decision making process, we can think of two choices (Yes/ No; to be/ Not to be; Doing something/ Not doing something; Presence/ Absence etc). For example, if I score more than, 90 in Mathematics, I plan to go for Mathematics major else, never look at Math for a life time. The real life decisions may not be that simple and straightforward (as the above statement) but, we can formulate a compound test condition by appropriately combining simple conditions. There are several stages in life (of a programmer), where we are compelled to make decisions based on the situation at hand. Whenever a decision is made, certain type of Action is chosen. For example, how to spend Sunday may have lot of choices: (a) Go to Samdrup Jongkhar for shopping (b) Go for a hike to Yongphula (c) Stay in the room and study. We can also have a default action of doing nothing (as good as sitting in front of the TV and watching some junk program). All these different actions can be implemented, based on a single or several test conditions and their outcomes. 4.2 Simple if Statement Programs would be very boring if they can produce only one type of outcome all the time. Decision control statements allow us to skip or execute a block of code, based on a test condition. Branch off is naturally applied by these decision control statements. It takes the following form: if (TC) { Action ; } Here, TC refers to the Test Condition, and if it is True, Action is executed only once. If it is False, it will skip Action. Example: 1. if(day = = “sunday”) { Go for shopping; } 2. if(x < 0) { printf(“%f”, x); } 3. if(average > = 40) { printf(“Pass”); } 4. if(a >= b) { b = c + d * x; } 4.3 if-else Statement The if-else statement is an extension of the simple if statement. The general form is: if (TC) { Action _1; } else { Action_2; } If the test condition is true Action_1 is executed; otherwise Action_2 is executed. In either case, either Action_1 or Action_2 is executed, not both. Example: if (score > 50) { printf ("Pass\n"); } else { printf ("Fail\n"); } Example: if(n = = 50) { x = x + 2; } else { x= x + 4; } It is possible to combine logical and relational operators to formulate compound Test Conditions, as per the context and need. The if-else statement can be replaced by conditional operator /Ternary operator (?:). To reflect the RHS of the above schematic, we normally write the 4 C programming lines on the left. Since C promotes compact style of writing, we can replace the one above, with the statement below: max = (a>b) ? a : b; In the above statement (a>b) is tested first. If it is true max is assigned with a otherwise max is assigned with b. Example program: #include <stdio.h> main() { int x=3, y=4, value; value = (x>y) ? x*x : y*y; printf ("value : %d\n",value); } Example program: /* magic number program */ main( ) { int magic = 223; int guess; printf(“Enter your guess for the number:”); scanf(“%d”, &guess); if(guess = = magic) { printf(“\n Congratulation! Right guess”); } else { printf( “\n Wrong guess”); } } 4.4 if else if else … sequence (else if ladder) Here, several test conditions (TC’s) can be applied one after another. When the TC is TRUE, corresponding Action component is executed. A number of test conditions are executed one after another to evaluate the specific part of the action component. It takes the following general form: if(TC1) { Action1; } else if(TC2) { Action2; } else if(TC3) { Action3; } else if(TC4) { Action4; } else Action5; The conditions are evaluated from top to down. As soon as a true condition is found, the action associated with it is executed and the control is transferred out of the if statement skipping the rest of the ladder. When all the n conditions become false, then the final else containing the default action will be executed. Figure below shows the logic of execution of else if ladder statements. Example: Consider grading the students in an academic institution. The grading is done according to the following rules. Average marks 80 to 100 60 to 79 50 to 59 40 to 49 0 to 39 Grade Honours First Division Second Division Third Division Fail if(marks>79) { printf( “Honours\n”); } else if(marks>59) { printf(“First Division\n”); } else if(marks>49) { printf(“Second Division\n”); } else if(marks>39) { printf(“Third Division\n”); } else printf(“Fail\n”); (1)We notice that, a new if else can be started at the end of every previous else. (2) All the Test Conditions are not evaluated at once but, one after another. (3) If all the test conditions are FALSE, action part of the last else will be executed. Example: if (code = = 1) { printf(“Colour = “RED”); } else if(code = = 2) { printf(“Colour = “GREEN”); } else if (code = = 3) { printf(“Colour = “WHITE”); } else printf(“Colour = “YELLOW”); Code numbers other than 1,2 or 3 are considered to represent YELLOW colour. Example program: /* This program prints the cell phone status */ #include <stdio.h> #define ON 1 #define OFF 0 main() { int status; printf(“Enter cell phone status:”); scanf(“%d”, &status); if (status == ON) { printf ("Switch off your cell phone now!\n"); } else if (status == OFF) { printf ("Good, the cell phone is switched off\n"); } else { printf ("Incorrect cell phone status\n"); } } Quick exercise: Use else if ladder for the following statements. temp is declared as a floating-point variable. 1. ICE, if the value of temp is less than 0. 2. WATER, if the value of temp lies between 0 and 100. 3. STEAM, if the value of temp exceeds 100. 4.5 Nesting of if-else statement It is possible to nest (i.e. embed) if –else statements, one within another. There are several different forms that nested if-else statements can take. The most general form is as follows: if(TC1) { if (TC2) { Action1; } else { Action2; } } else { Action3; } If the TC1 is false, Action3 will be executed; otherwise it continues to perform the second test. If the TC2 is true, Action1 will be evaluated; otherwise Action2 will be evaluated. Example program: /* finding the largest of 3 numbers */ main( ) { int a = 5, b = 2, c = 7; if(a > b) { if (a >c) { printf (“%d is greatest”, a); } else { printf(“%d is greatest”, c); } } else { if(b > c) { printf(“%d is greatest”, b); } else { printf(“%d is greatest”, c); } } } Example: if (code = = 1) { printf(“Colour = RED”); } else if(code = = 2) { printf(“Colour = GREEN”); } else if (code = = 3) { printf(“Colour = WHITE”); } else printf(“Colour = YELLOW”); The above code can be written by using nested if-else statements as follows: if(code !=1) { if(code !=2) { if(code !=3) { printf("colour = YELLOW"); } else { printf("Colour = WHITE"); } } else { printf("Colour = GREEN"); } } else { printf("Colour=RED"); } Quick exercise: 1. int x; if(x > 4) { printf(“Orange”); } else if ( x > 10) { printf(“Apple”); } else if (x > 21) { printf(“Mango”); } else printf(“Banana”); What will be the value of x so that “Apple” will be printed? 2. int i; i = 2; i++; if(i == 4) { printf(“i = 4”); } else { printf(“i = 3”); } What is the output of the program? 3. main() { int age, weight; age = 17; weight = 75; if (age>17 && weight >= 70 ) { printf ("selected for wrestling.\n"); } else { printf ("Rejected for wrestling.\n"); } } What is the output of the program? Rewrite the code using nested if-else statement. 4.6 Switch Statement The switch statement is similar to a chain of if/else statements. The switch statement causes a particular group of statements (actions) to be chosen from several available groups. The switch statement is a multi-way decision making statement, where, one of the many action components are executed for TRUE case and value of the TC decides how the branching takes place. Depending on the value of the TC, it branches to a specific action parts. The general form of a switch statement is: switch (TC) { case constant1: action1 break ; case constant2: action2 break; default: action3 } Rules for switch statement The switch statement evaluates the value of a TC and branches to one of the case labels. 1. The switch TC must be an integer or character. 2. Case labels must be an integer or character constant and no floating point value. 3. Duplicate labels are not allowed, so only one case will be selected. 4. Case labels can be in any order. 5. Case labels must end with semicolon. 6. Break statement transfers the control out of the switch statement. 7. Break statement is optional. That is, two or more case labels may belong to the same statements. 8. The default label can be put anywhere in the switch. When C sees a switch statement, it evaluates the TC and then looks for a matching case label. If none is found, the default label is used. If no default is found, the statement does nothing. The example program code below illustrates how a chain of if-else statement can be replaced by a switch statement. Example - if-else statement Example – switch statement #include<stdio.h> void main () { #include<stdio.h> void main () { char choice; char choice; printf("Enter color red):"); choice = getchar(); choice(type if(choice=='r') printf("RED \n"); else if(choice == 'b') printf("BLUE\n"); else if (choice == 'g') printf("GREEN\n"); else { printf("No color choice available. Try again!\n"); } getch(); } r= printf("Enter color choice(type r= red):"); choice = getchar(); switch (choice) { case ‘r’: printf("RED \n"); break; case ‘b’: printf("BLUE\n"); break; case ‘g’: printf("GREEN\n"); break; default: printf("No color choice available. Try again!\n"); } getch(); } The example program below illustrates a simple implementation and use of switch statement. #include <stdio.h> main() { int year; printf ("Enter your year of study\n"); scanf ("%d", &year); switch (year) { case 1: printf("You are a fresher\n"); case 2: printf ("You are in second year\n"); case 3: printf ("You are in third year\n"); case 4: printf("You are in fourth year\n"); default: printf ("Then, you are a graduate\n"); } } The switch statement is often used for menu selection: Example program: /* Simple Calculator Program */ main() { char character; int result,a,b; printf("\nSIMPLE CALCULATOR\n\n" ); printf("\n printf("\n printf("\n printf("\n printf("\n A Addition:" ); B Sub:"); M Mul:"); X to skip:"); Eenter your choice:"); character = getchar(); switch(character) { case 'A': printf("\nEnter two numbers:"); scanf("%d%d", &a,&b); result = a +b; printf("\nresult:%d", result); break; case 'B': printf("\nEnter two numbers:"); scanf("%d%d", &a,&b); result = a - b; printf("\nresult:%d", result); break; case 'M': printf("\nEnter two numbers:"); scanf("%d%d", &a,&b); result = a * b; printf("\nresult:%d", result); break; default: printf("\nNo operations available!"); } } Quick exercise 1. Write a switch statement that will examine the value of an integer variable called flag and print one of the following messages, depending on the value assigned to flag. a. HOT, if flag has a value of 1, b. LUKE WARM, if flag has a value of 2, c. COLD, if flag has a value of 3, d. OUT OF RANGE, if flag has any other value. 2. What is the output of the following code? main( ) { int i = 0; switch(i) { case 0: i++; case 1: ++i; } printf(“i = %d”, i++); printf(“i = %d”, i); } Practice Questions: 1. Find errors, if any, in each of the following segments: a. if(code > 1); a = b + c; else a = 0; b. if(p < 0) || (q <0) printf(“sign is negative”); c. fd 2. The following is a segment of a program: x = 1; y = 1; if(n > 0) { x = x + 1; y = y – 1; } printf(“%d %d”, x, y); What will be the values of x and y if n assumes a value of (a) 1 and (b) 0. 3. Rewrite each of the following without using compound relations: a. if(grade <= 59 && grade >= 50) second = second + 1; b. if(number > 100 || number < 0) printf(“Out of range”); else sum = sum +number; c. if((m1 > 60 && m2 > 60) || t > 200) printf(“Admited\n”); else printf(“Not admitted\n”); 4. What is the output from the code (or code segment) given below? (a) int x=8; int y=10; if (x==8) printf ("x=%d\n", x); else printf("y=%d\n",y); (b) #include <stdio.h> main() { int z; z = 17/5 - 8/3; printf ("z = %d", z); } (c) main( ) { int x=10, y, z; z y z x = y = x; -= x--; -= --x; -= --x - x--; printf("x=%d, y=%d, z=%d", x,y,z); } 5. Rewrite the following program, correcting all errors. include (stdio.h) main{} / Program execution begins here / ( people, legs integer; print ("How many people are there?/n); scanf ("%d", people); legs = people * 2 print ("There are %f legs."); [end main] ******************************************************************** Please bring all the errors (oversights) to my notice either by sending an Email([email protected]) or in person. I will appreciate your kind gesture! ************************************************************** Chapter 5: Decision Looping Statements Suppose we want to display “hello world” on output screen five times in five different lines, we might think of writing either five printf statement or one printf statement consisting of constant string “hello world\n” five times. What if we want to display “hello world” 500 times, should we write 500 printf statements? Not possible..! Therefore, we need some programming facility to repeat certain works. Such facility is available in the looping statements. Looping statements allow the program to repeat a section of code any number of times or until some condition occurs. 5.1 while statement The while statement is used when the program needs to perform repetitive tasks. The general form of a while statement is: while(TC) { action; } The program will repeatedly execute the action inside the while until the condition becomes false (0). (If the condition is initially false, the statement will not be executed.) 1 Example program: Illustrates how five printf statements to print hello five times on a screen can be easily implemented using while statement. main() { printf printf printf printf printf ("Hello ("Hello ("Hello ("Hello ("Hello world\n"); world\n"); world\n"); world\n"); world \n"); } main() { int count=0; /* Initialization */ while(count < 5) /* Testing */ { printf ("Hello world\n"); count++; /* Incrementing */ } } The body of the loop is executed 5 times for count=1,2,...,5, each time printing the message by incrementing the value of count inside the loop. The test condition may also be written as count <= 5 with the initialization of the count = 1; the result would be the same. The variable count is called counter or control variable. Example program: Program to find the sum of n natural numbers /* example 1 */ main( ) { int sum,count; sum = 0; count = 1; while(count <= 5) { sum +=count; count++; } printf("Sum = %d", sum); } /* example 2 */ main() { int n,sum,count; sum = 0; count = 1; printf(“Enter number(n):”); scanf(“%d”, &n); while(count <= n) { sum +=count; count++; } printf("Sum = %d", sum); } In example 1, the body of the loop is executed 5 times, each time adding the value of count to sum; count is then incremented inside the loop. In example 2, the body of the loop is executed n times for n = 1,2,3,....n, each time adding the value of count to sum; count is then incremented inside the loop. The value of n is entered by the user. When the condition of the while becomes false, we come out of the body of while loop and print the sum. 2 Example programs: /* To find the even number up to 20 */ main() { int count=1; while (count <= 20) { if(count%2 == 0) printf ("%d\n",count); count++; } /* To find the factorial of a number */ #include <stdio.h> main() { int num; int count = 1; int fact = 1; printf(“Enter number:”); scanf(“%d”, &num); while(count <= num) { fact = fact * count; count++; } printf(“The factorial of %d is %d”, num, fact); 5.2 do-while statement Same as the while loop. But, the Action part inside the while loop is guaranteed to be executed at least once, since the test condition is performed at the bottom of the loop. If the Test condition is True, Action component is executed again. The general form of the do-while statement is: do{ action; } while(TC); Note the use of semi-colon after while (TC). 3 Example program: main() { int count=1; /* Initialization */ do { printf ("Hello world\n"); count++; /* Incrementing */ } while(count <= 5); /* Testing */ } Example program: /* Finding the sum of n natural numbers */ main() { int n,sum,count; sum = 0; count = 1; printf(“Enter number(n):”); scanf(“%d”, &n); do { sum +=count; count++; } while(count <= n); printf("Sum = %d", sum); } 4 Quick exercise: 1. What is the output from the code below? #include <stdio.h> main() { int i=0, N=5, Sum=0; while(i<=N) { i = i*i*i; Sum = Sum + i; printf("i = %d, Sum = %d\n", i, Sum); i = i+1; } printf ("i = %d, Sum = %d\n", i, Sum); } 2. int i = 2, j = 2, m = 2, n = 2; do { m++; } while(i-- > 0 ); while(j-- > 0) { n++; } printf(“m and n = %d%d\n”,m,n); 5.3 for statement The general form of for statement is: for(Initialization; TC; Update ) { action; } This is a fixed iterative loop. It has 3 parts inside the parentheses: (i) Initialization, (ii) A Test condition (iii) an update. 5 The TC decides whether to continue Action and it controls the mechanics of loop working. The test condition typically results in a Boolean value. Control variable does the work of the loop. The sequence is as follows: (i) Initialization is done only once (ii) TC is tested. If the result is True, Action is executed. (iii) After executing Action, it goes to Update. The update is usually a counter which is incremented or decremented (depends on the program and what you are trying to do). (iv) Go to Step (ii) and repeat (ii) and (iii) until TC is false. Note: When TC is tested for the first time, and if it is false, Update is never performed. Initialization is done only once and is never revisited. After executing Action, it goes to update and from there to TC. Please see the arrows of the schematic starting from the start. while statement for statement Initialization; While (TC) { action; Update; } for(Initialization; TC; Update ) { action; } 6 Example: main() { int count; for(count = 1 ; count <=5; count++) { printf("Hello world\n"); } } Example: Sum of n natural numbers. main() { int count,n,sum; sum = 0; printf("Enter number(n):"); scanf("%d", &n); for(count = 1 ; count <=n; count++) { sum +=count; } printf("Sum = %d", sum); } 5.4 Nested while It is required when multiple conditions are to b tested. The syntax of nested while is: while(TC1) { Action1; while(TC2) { Action2; while(TC3) { Action3; } } } Example: int x; while(x) { int a = 0; 7 while(a < 10) { a++; } x--; } 5.5 Nested for It is used when multiple set of iterations are required. The syntax for nested for is: for(Initialization; TC1; Update) { action1; for(Initialization; TC; Update) { action2; } } Example: for(i = 0; i < 5; i++ ) { for(j = 0; j < 3; j++) { printf(“%d%d\t”, i,j); } printf(“\n”); } Output is: i iteration j iteration 5.6 break statement The break statement is used to terminate loops or to exit from a switch. When a break statement is encountered inside a loop, the control automatically passes to the first statement after the loop. When the loops are nested, the break would only exit from the 8 loop containing it. That is the break will exit only a single loop. It can be used within a ‘while’, ‘do-while’, ‘for’ or ‘switch’ statement. The break statement is written simply as break; without any embedded expression or statements. Example: /* for loop */ int x; for(x = 1; x <= 10; x++) { /* break loop only if x == 5 */ if(x == 5) { break; } printf("%d", x); } printf("\nBroke out of loop at x == %d",x); Output: 5.7 continue statement Like the break statement, C supports another similar statement called the continue statement. However, unlike the break which causes the loop to be terminated, the continue as the name implies, causes the loop to be continued with the next iteration after skipping any statements in between. The continue statement tells the compiler, “skip the following statements and continue with the next iteration”. The continue statement can be included within a while, do-while, or a for statement. It is written simply as continue; In while and do-while loops, continue causes the control to go directly to the testcondition then to continue the iteration process. In the case of for loop, the execution proceeds immediately to update. Then the test condition is evaluated, which determines whether to execute action or not. Example: /* for loop */ int x; for(x = 1; x <= 10; x++) { /* skip remaining code in loop only if x == 5 */ if(x == 5) 9 { continue; } printf("%d", x); } printf("\nUsed continue to skip printing the value 5); Output: Quick exercise: 1. Find the output: (a) main() { int i = 5; while(i) { i--; if(i ==3) { Continue; } printf(“\n Smile”); } } (b) main() { int i=0,x = 0; for(i = 1; i < 10; i++) { if(i%2 ==1) { x += i; } else { x--; } printf("%d ", x); break; } printf("\n x = %d",x); } 10 (c) main() { int i=0,x = 0; for(i = 1; i < 10; i++) { if(i%2 ==1) { x += i; } else { x--; } printf("%d ", x); continue; } printf("\n x = %d",x); } (d) main() { int i, j, x = 0; for(i = 0; i < 5; i++) { for(j = 0; j < i; j++) { x += (i + j - 1); printf(“%d”,x); break; } } printf(“\nx = %d”, x); } (e) int i; for(i=3; i--;) { if(i== 1) { continue; } printf (“i= %d\n”, i); } Practice Questions 1) Find output: (a) char a=’A’; int flag=1; while(flag) switch(a) { case ‘A’: printf(“%c ”,a);a=(int)a+2; break; case ‘B’: printf(“%c ”,a);a=(int)a+2; break; 11 case ‘C’: printf(“%c ”,a);a=(int)a+2; break; default : printf(“ Breaking..”); flag=0; } b) main() { int i=1,j=2,k=3; if(i==1) if(j==2) if(k==3) { printf("ok"); break; } else printf("continue"); printf("bye"); } (c) int i, j; for (i = 0; i <= 3; i = i + 1) { for (j = 0; j <= 4; j = j + 1) { if ( (i + 1 == j) ) { printf ("+"); } else { printf ("#"); } } printf ("\n"); } ******************************************************************** Please bring all the errors (oversights) to my notice either by sending an Email([email protected]/[email protected]) or in person. I will appreciate your kind gesture! ************************************************************** 12 Chapter 6: Functions 6.1 Introduction Let us have a fresh look at some of the following statements, which we are already familiar with the printf("Enter values of i and j\n"); The printf() used above is indeed a library function. We have been using this without being concerned about how it is implemented. We all know the following: If we use some text in double quotes, it is copied on to the screen. To achieve this, someone has written the corresponding interface. But, we do not really need to be concerned about how that interfacing has been implemented inside. However, we should be aware of how to make use of printf(). We have used functions in very program that we have discussed so far namely main( ), printf(), and scanf( ). The above functions are already implemented through the library. We do not really know (need not know) the implementation details. The library writers are typically software developers, compiler writers and other IT/Scientific solution providers. They can’t foresee all our requirements and write library functions suitable for all our requirements. Furthermore, in a research environment, each individual may have completely different requirements. So, we are compelled to write our own user-defined functions (Some of you would maintain your own library of functions at a later stage). There are basically two categories of function: 1. Library functions: available in the C standard library such as stdio.h, math.h, string.h etc. Examples include printf( ) and scanf( ). 2. User-defined functions: developed by the user at the time of writing a program. They are easy to define and use. Infact, this is one of the strengths of C language. 6.2 Need for user-defined functions As pointed out earlier, main is a specially recognized function in C. Every program must have a main function to indicate where the program has begun its execution. While it is possible to code any programs utilizing only main function, it leads to a number of problems. The program may become too large and complex and as a result the task of debugging, testing, and maintaining becomes difficult. If a program is divided into functional parts, then each part maybe independently coded and later combined into a single unit. These independently coded programs are called subprograms that are much easier to understand, debug, and test. In C, such subprograms are referred to as functions. A Function is a self contained block of statements that carries out some specific, welldefined task. 1 The notion of a function provides a convenient way to encapsulate (hide) some computation, which can then be used without being concerned about its implementation details. The use of function can also avoid the need for redundant (repeated) programming of the same instructions. For example, many programs require that a particular group of instructions be accessed repeatedly, from several different places within the program. The repeated instructions can be placed within a single function, which can then be accessed whenever it is needed. This saves both time and space. 6.3 Elements of user-defined functions We have used a variety of data types and variables in our program. We can also define functions and use them like any other variables in C programming. Both variables and functions share some similarities. 1. Both function names and variable names are considered identifiers and therefore they must adhere to the rules for identifiers. 2. Like variables, functions have types (such as int) associated with them. 3. Like variables, function names and their types must be declared and defined before they are used in a program. There are there main elements of a function: 1. Function declaration/ function prototype: Like the declaration of a variable before their use in the program, functions must be declared before their use. 2. Function call: that is call/invoke a function to be used in the program. 3. Function definition: is an independent program module that is specially written to implement the requirements of the function. We will look each of these in detail in the later part of this chapter. The general format of a function definition: function_type function_name(parameter list) { local variable declaration; executable statement1; executable statement1; ………………………… return statement; } 6.4 Development of program using get_sum() function We start with the simplest possible program to understand the mechanics of function development. We start with a very simple form for example, summing up two int values by using a user defined function get_sum(). 2 Example: /* Program to sum two integer values, without using a user defined function */ #include <stdio.h> main() { int a, b; int sum; a = 1; b = 2; sum = a + b; printf("%d + %d = %d\n", a, b, sum); } First thing I need to know is the existence of a function. This in function terminology is called – Function Prototype declaration (or) simply, function declaration. Second thing is, called invoking or calling a function (function call). A function which calls a function is called calling function and the functions which is called to perform the task is called the called function. But, the called function would ask the calling function a simple question what is that you want me to sum up? So, the calling function needs to supply the called function the values to sum up - these are called arguments in function terminology. C has a protection facility for these arguments being passed to the called function, to avoid accidental modification of the original values. So, the called function is passed with a copy of these values and this is called function call by value. When the called function is invoked with arguments, the control is transferred to the called function and performs the summation and returns the value of sum back to the calling function. The program to sum two integers using a user-defined functions get_sum()is shown here. Different initial stages of the main()function are denoted step by step, with self explanatory notes. The way in which get_sum()function is invoked is crucial to the understanding of functions. res = get_sum(a, b); This statement means assign the value returned by get_sum()function to the res variable. The function call results in a copy by value into the arguments i,j as shown. 3 6.5 Working of Functions ¾ A C program does not execute the statements in a function until the function is called by another part of the program. ¾ When C function is called, the program can send information to the function in the form of one or more what is called arguments although it is not a mandatory. ¾ Argument is a program data needed by the function to perform its task. ¾ When the function finished its processing, program returns to the same location that called the function. ¾ The following figure illustrates a function call. ¾ When a program calls a function, executions passed to the function and then back to the calling program’s code. ¾ Function can be called as many times as needed as shown for function2() in the above figure, and can be called in any order provided that it has been declared (as a prototype) and defined. Example: /* Program to find sum and product of two numbers using userdefined functions */ 4 #include<stdio.h> /* Function declarations */ int read_input(); int get_sum(int i, int j); int get_product(int i, int j); void main() { int n1,n2,sum,product; /* Function call */ n1 = read_input(); n2 = read_input(); sum = get_sum(n1,n2); product = get_product(n1,n2); /*Printing the result of sum and product */ printf("Sum = %d, Product = %d", sum,product); } /* Function definitions */ int read_input() { int num; printf("Enter number:"); scanf("%d",&num); return num; } int get_sum(int i, int j) { int k; k = i + j; return k; } int get_product(int i, int j) { int k; k= i * j; return k; } Sample output: 6.6 Function Terminology Writing user defined functions is very similar to writing the statements of a main() program. Mastering how to write them requires following a few basic principles and conventions with consistency. 5 6.6.1 Prototype Declarations Function prototype declaration is just like variable declaration. However, there are a few simple and straightforward conventions to be followed. There are several styles (they are all just small variations) acceptable to different compilers. Example : int get_sum(int i, int j); The function get_sum() above has two parameters i and j which are of type int and it returns an int value. This declaration should be viewed akin to the declarations for identifiers. But, the memory allocation for the identifiers i and j is not done here. So, if a function is declared inside main(), it becomes local to main(). If we want the function to be accessible to all other functions, it should be declared outside main(). If the declaration of a function B is inside a function A, then, function B becomes local to function A. This typically means, the location of Prototype declarations decides, which functions can invoke them. Function prototype declaration should be made BEFORE the function is used. If the declaration is outside any function, the declaration makes it known to all the functions after the declaration. If a function is declared before all other function definitions, then, it will be known to all functions. However, if the declaration is inside a function, that function becomes local to the function in which it is declared. 6.6.2 Function Definition To define a function means, to define its header and the body. One can define the function anywhere in a file. Before main() (or) after main(). If there are several functions in a program, they can appear in any order. Examples: typical function definitions (a)float mul(float x, float y) { float result; /* local variable */ result = x * y; /* computes the product */ return(result); / * returns the result */ } (b)void sum(int a, int b) { printf(“sum = %d”, a+b); /* no local variables */ return; /*optional */ } (c) void display(void) { /* no local variables */ printf(“No type, no parameters”); /* no return statement */ } Function definitions do not nest, unlike the other C statements. 6 Defining one function inside another is illegal : Example: void fun1 (int size) { /* some programming statements of fun1 */ int fun2 (char ch1) { /* Some programming statements */ } /* Remaining programming statements of fun1 */ } Execution of a program begins in the main()function. The main() function can call other functions, which in turn can call other functions (including main()). Some basic rules with function definitions: (i) See if the function prototype declaration and header of the function definition are consistent or not (except the semi-colon). (ii) If your function body returns a value, is the data type of the return value correctly reflected? (iii) All the variables declared inside the function are local to that function. They are not visible to other functions. This is the reason; different functions can have the same identifier name, yet do not cause any conflict with each other. 6.6.3 Function Call Function call refers to invoking a function (or calling someone to do some task). A function call transfers the control to the called function. A function call should have the same return data type as the identifier to which it is assigned. A function may return only one value at a time, when invoked. The order in which functions are defined and the order in which they are invoked/ called need NOT necessarily be the same. A function can be invoked any number of times. A function can invoke any other function. Even, main()can be called by some other function. 6.6.4 The return Statement return statement causes a termination of the current function and execution goes back to the calling function. Function returns a single value to the calling function. To return without a value is simply written as : return; To return the value exprn to the calling function : return exprn; Unfortunately, functions CAN NOT return more than one value to the invoker. Note: A Function depending on whether arguments are present or not and whether a value is returned or not, may belong to one of the following categories: 1) Functions with no arguments and no return values. 2) Functions with arguments and no return values; 3) Functions with arguments and one return value; 4) Functions with no arguments but return a value. 7 Quick exercise: 1. What is the output? Is the incrementing i, j in get_sum() function reflected in the variables i,j of the main() ? #include <stdio.h> int get_sum(int i, int j); main() { int sum; int i=4, j=5 ; sum = get_sum(i, j); printf ("i = %d, j = %d, sum = %d\n", i, j, sum); } int get_sum(int i, int j) { int k; k = i + j; i = i + 2; j = j + 3; return k; } 2. Make use of the function definition given below to compute the value of n! int get_fact(int n) { int n_fact, i ; n_fact = 1; for (i=1;i<=n;i++){ n_fact = n_fact*i; } return n_fact; } Invoke the function get_fact()inside main() and develop a program to print the n! 3. True/ False Exercise The statements in (1) to (3) are based on the skeleton code given below: #include <stdio.h> void get_f1(float a, float b); int main(void) { return 0; } void get_f2(int ch); void get_f1(float a, float b) { void get_f3(int a, int b); return ; 8 } void get_f3(int a, int b) { return; } void get_f2(int ch) { return ; } (1) get_f2()can be invoked by main()function. (2) Function get_f2()can be invoked by get_f1()but not by get_f3(). (3) Since the prototype declaration of get_f3()is inside get_f1(), get_f3()can be defined inside get_f1()function. (4) A function may return more than one value at a time. (5) An invoking function must pass arguments to the invoked function (6) The number of arguments and parameters can be different. (7) Every function returns a value to its calling function. (8) void is a data type that returns nothing to the calling function. (9) Parameters are declared in the functions header. 6.7 Structured Programming - Functions and structured programming are closely related. - Structured programming definition: In which individual program tasks are performed by independent sections of program code. - Normally, the reasons for using structured programming may be: 1. Easier to write a structured program. Complex programming problems or program are broken into a number of smaller, simpler tasks. Every task can be assigned to a different programmer and/or function. 2. It’s easier to debug a structured program. If the program has a bug (something that causes it to work improperly), a structured design makes it easier to isolate the problem to a specific section of code. 3. Reusability. Repeated tasks or routines can be accomplished using functions. This can overcome the redundancy of code writing, for same tasks or routines, it can be reused, no need to rewrite the code, or at least only need a little modification. - Advantages: Can save programmers’ time and a function written can be reused (reusability). - Structured program requires planning, before we start writing a single line of code. - The plan should be a list of the specific tasks that the program performs. 9 6.8 Variable Scope The scope of a variable is the area of the program in which the variable is valid. A global variable is valid everywhere (hence the name global), so its scope is the whole program. A local variable has a scope that is limited to the block in which it is declared and cannot be accessed outside that block. A block is a section of code enclosed in curly braces ({}). The figure below shows the difference between local and global variables. You can declare a local variable with the same name as a global variable. Normally, the scope of the variable count (first declaration) would be the whole program. The declaration of a second local count takes precedence over the global declaration inside the small block in which the local count is declared. In this block, the global count is hidden. The Figure below illustrates a hidden variable. 10 The variable count is declared as both a local variable and a global variable. Normally, the scope of count (global) is the entire program; however, when a variable is declared inside a block, that instance of the variable becomes the active one for the length of the block. The global count has been hidden by the local count for the scope of this block. The shaded block in the figure shows where the scope of count (global) is hidden. Definitions of local and global: Local: These variables only exist inside the specific function that creates them. They are unknown to other functions and to the main program. As such, they are normally implemented using a stack. Local variables cease to exist once the function that created them is completed. They are recreated each time a function is executed or called. Global: These variables can be accessed (i.e. known) by any function comprising the program. They are implemented by associating memory locations with variable names. They do not get recreated if the function is recalled. Example: Defining global variables /* Demonstrating Global variables #include <stdio.h> int add_numbers( void ); */ /* function prototype */ /* These are global variables and can be accessed by functions from this point on */ int value1, value2, value3; main() { int result; value1 = 10; value2 = 20; value3 = 30; result = add_numbers(); printf("\n The sum of %d + %d + %d is %d\n", value1, value2, value3,result); } int add_numbers( void ) { int result; result = value1 + value2 + value3; return result; } The scope of global variables can be restricted by carefully placing the declaration. They are visible from the declaration until the end of the current source file. 11 Example: #include <stdio.h> void no_access( void ); /* function prototypes */ void all_access( void ); int n2; /* n2 is known from this point onwards */ void no_access( void ) { n1 = 10; /* illegal, n1 not yet known */ n2 = 5; /* valid */ } int n1; /* n1 is known from this point onwards */ void all_access( void ) { n1 = 10; /* valid */ n2 = 3; /* valid */ } 6.9 Recursion Recursion is a process by which a function calls itself repeatedly, until some specified condition has been satisfied. In order to solve a problem recursively, two conditions must be met: - problem must be written in recursive form - problem statement must include a stopping condition. An example of recursion is to calculate the factorial of a given number. The factorial of a number n is expressed as a series of repetitive multiplication as shown below: factorial of n = n(n-1)(n-2)……..1 for example, factorial of 4 = 4x3x2x1 = 24 Example program: #include <stdio.h> int factorial(int n); //function prototype void main() { int n=4; int fact=factoral(n) printf("The factorial of %d is: %d", n,fact); } 12 int factorial(int n) { //test used to see if the factoral process is at the last stage if (n == 1) return 1; //recursive step of the factoral else { n *= factorial(n-1); //calls the factorial function. //The above calls the factorial function from within the factorial function. return n; } } Let us see how the recursion works. Assume n = 4. Since the value of n is not equal to 1, the statement n *= factorial(n-1); will be executed with n =4. That is, n = 4 * factorial(3); will be evaluated. The expression on the right hand side includes a call to factorial with n =3. This call will return the following value: 3 * factorial(2) Once again, factorial is called with n = 2. This will return the following value: 2 * factorial(1) factorial is called again with n = 1. This time, the function returns 1. The sequence of operations can be summarized as follows: n = = = = = 4 * 4 * 4 * 4 * 24 factorial(3) 3 * factorial(2) 3 * 2 * factorial(1) 3 * 2 * 1 Practice Questions 1. Find output //demonstrates variables scope within a block void myFunc(); int x = 10; int y = 23; // global variables declaration void main() { int x = 5; //local variable declaration printf("\nIn main x is:%d ",x); myFunc(); printf("\n\nBack in main, x is:%d ",x); } 13 void myFunc() { int x = 8; //local scope variable printf("\n\nWithin myFunc, local x is:%d ",x); printf("\nWithin myFunc, global y is:%d ",y); } 2. #include <stdio.h> void Funny(void); void main(void) { Funny(); printf("In main() – Yes it is me!\n"); Funny(); } void Funny(void) { printf("In Funny() – Silly me.\n"); } a. Number the events in order from 1 to 5. _____ - Funny() calls printf() first time _____ - Funny() calls printf() second time _____ - main() calls printf() _____ - main() calls Funny() _____ - main() calls Funny() b. When main() calls Funny(), which function do you think is the calling function? c. Which function do you think is the called function? d. When Funny() calls printf(), which function do you think is the calling function? e. Which function do you think is the called function? f. Which function gets called and also calls another function? g. When main() calls Funny(), is there an argument passed to Funny()? 3. 14 a. i is a local variable in main(), which means that only main() has access to it. What is the local variable in Funny()? b. main() sets the value of i to 54. Then it was printed. Execution then passed to Funny(). At the beginning of Funny() was the value of i equal to 54 or just “garbage”? c. Funny() set the value of i to -28. After coming back to main(), what was the value of i? d. Was the i in main() the same as i in Funny()? ******************************************************************** Please bring all the errors to my notice either by sending an email([email protected]/[email protected]) or in person. I will appreciate your kind gesture! ************************************************************** 15 Chapter 7: Arrays 7.1 Introduction and definition So far we have used only the fundamental data types, namely char, int, and float. Although these types are very useful, they are constrained by the fact that a variable of these types can store only one value at any given time. Therefore, they can be used only to handle limited amounts of data. In many applications, however, we need to handle a large volume of data in terms of reading, processing and printing. To process such large amounts of data, we need a powerful data type that would facilitate efficient storing, accessing and manipulation of data items. Such data type is called arrays. An array is a collection of related data elements (items) of the same type that are referenced by a common name. All the elements of an array occupy a set of contiguous memory locations and by using an index or subscript we can identify each element. The general format to declare an array is: data_type array_name[size]; where data-type is the data type of an array, array_name is the name of an array, and the size indicates the number of array elements. For example, instead of declaring mark1, mark2, ..., markN to store and manipulate a set of marks obtained by the students in certain courses, we could declare a single array variable named mark and use an index, such as j, to refer to each element in mark. This absolutely has simplified our declaration of the variables. Hence, mark[j] would refer to the jth element in the array mark. Thus by changing the value of j, we could refer to any element in the array, so it simplifies our declaration. For example, if we have 100 list of marks of integer type, we will declare it as follows: int mark1, mark2, mark3, … , mark100; If we have 100 marks to be stored, you can imagine how long we have to write the declaration part by using normal variable declaration? By using an array, we just declare like this: int mark[100]; This will reserve 100 contiguous/sequential memory locations for storing the integer data type. Graphically can be depicted as follows: 1 The value of the subscript must be in the range 0 to size-1. An array subscript value outside this range will cause a run-time-error. 7.2 One Dimensional Array: Declaration A list of items can be given one variable name using only one subscript and such a variable is called one-dimensional array. A single dimensional array declaration has the following form: array_element_data_type array_name[array_size]; Here, array_element_data_type declares the base type of the array, which is the type of each element in the array. array_size defines how many elements the array will hold. array_name is any valid C identifier name that obeys the same rule for the identifier naming. For example, to declare an array of 20 characters, named character, we could use: char character[20]; Can be depicted as follows: In this statement, the array character can store up to 20 characters with the first character occupying location character[0] and the last character occupying character[19]. Note that the index runs from 0 to 19. In C, an index always starts from 2 0 and ends with (array size-1). So, notice the difference between the array size and subscript terms. Examples of the one-dimensional array declarations: int x[20], y[50]; float price[10], yield; char letter[70]; The first example declares two arrays named x and y of type int. Array x can store up to 20 integer numbers while y can store up to 50 numbers. The second line declares the array price of type float. It can store up to 10 floating-point values. The third one declares the array letter of type char. It can store a string up to 69 characters. (Why 69? Remember, a string has a null character (\0) at the end, so we must reserve for it.) Just like ordinary variables, arrays of the same data type can be declared on the same line. They can also be mixed with ordinary variables of the same data type like in the second line together with yield. 7.3 Array Initialization An array may be initialized at the time of its declaration, which means to give initial values to an array. Initialization of an array takes the following form: Data_type array_name[size] = {value_list}; Examples: int id[7] = {1, 2, 3, 4, 5, 6, 7}; float x[5] = {5.6, 5.7, 5.8, 5.9, 6.1}; char vowel[6] = {‘a’, ‘e’, ‘i’, ‘o’, ‘u’, ‘\0’}; The first line declares an integer array id and it immediately assigns the values 1, 2, 3, …, 7 to id[0], id[1], id[2],…, id[6]. In the second line assigns the values 5.6 to x[0], 5.7 to x[1], and so on. Similarly the third line assigns the characters ‘a’ to vowel[0], ‘e’ to vowel[1], and so on. Note again, for characters we must use the single apostrophe (’) to enclose them. Also, the last character in the array vowel is the NULL character (‘\0’). Initialization of an array of type char for holding strings takes the following form: char array_name[size] = "string_lateral_constant"; For example, the array vowel in the above example could have been written more compactly as follows: char vowel[6] = "aeiou"; 3 When the value assigned to a character array is a string (which must be enclosed in double quotes), the compiler automatically supplies the NULL character but we still have to reserve one extra place for the NULL. 7.4 Array Manipulation: How to use an array and what array can do? 7.4.1 Accessing Array’s Element The following program example declares and initializes the array named y of type int. It uses a for loop with index i to access the successive elements in y. For each loop iteration, the value accessed is displayed. Note that the loop index i run from 0 to 3, not 1 to 4. Also, note that the array size N is declared in the #define statement. Example: Declare, initialize and print the array elements #include<stdio.h> #define N 4 void main() { int i, y[N] = {2,3,4,1}; for(i = 0; i < N; i++) { printf("%d ", y[i]); } } Sample output: Modify the above program to add the elements of an array y and then print the sum. #include<stdio.h> #define N 4 void main() { int i, total = 0, for(i = 0; i < N; { printf(" %d ", total += y[i]; } printf("\n Sum of } y[N] = {2,3,4,1}; i++) y[i]); 4 numbers in an arrary is : %d",total); 4 Sample output: Example: Read array elements from the user and find the sum #include<stdio.h> #define N 4 void main() { int i, total = 0, y[N]; printf("Enter array elements:\n"); for(i = 0; i < N; i++) { printf("\n y[%d]:",i); scanf("%d", &y[i]); / * reading from the user */ total += y[i]; } printf("\n Printing the array elements entered:\n"); for(i = 0; i < N; i++) { printf("\n\n y[%d]:%d",i, y[i]); } printf("\n\n Sum of 4 numbers in an arrary is : %d", total); } Sample output: 5 Example: Generalize the above program to read array size and elements. Then, find the sum. #include<stdio.h> #define N 100 void main() { int i, total = 0, y[N],n; printf("\nEnter array size:"); scanf("%d", &n); printf("Enter array elements:\n"); for(i = 0; i < n; i++) { printf("\n y[%d]:",i); scanf("%d", &y[i]); total += y[i]; } printf("\nPrinting the array elements entered:\n"); for(i = 0; i < n; i++) { printf("\n y[%d]:%d",i, y[i]); } printf("\n\n Sum of 4 numbers in an arrary is : %d", total); } Sample output: 7.4.2 Searching for a value To search an array for a value, the program looks at each element in the array and checks if it matches the given value using if expression. The following example, finds the character in the array named text. #include<stdio.h> 6 void main() { int i; char c, text[6]="smile"; printf("Enter character to search: "); scanf("%c", &c); for(i = 0; i < 6; i++) { if(c == text[i]) { printf("\nCharacter %c found at location %d", c,i); } } } Sample output: Example: To find the highest number from the given 5 numbers.[refer chapter 1 for algorithmic steps and flowchart ] #include<stdio.h> void main() { int i, max, count, arr[4] = {4,7,12,9}; max = arr[0]; for(i=1;i<4;i++) { if(arr[i]>max) { max = arr[i]; count = i; } } printf("The max number is %d found at location %d",max,count); } Sample output: 7.4.3 Exchanging Values of Variables It is sometimes necessary to shuffle the order of the elements of an array. Sorting arrays calls for the values of some elements to be exchanged. It would therefore be helpful to 7 first learn the technique of swapping variables. How would you swap the values of the variables, let say, num1 and num2 (that is exchanging the value of num1 in num2)? You must use a third variable as in the following example: //assign num1 to third_var third_var = num1; //then assigns num2 to num1 num1 = num2; //finally assigns third_var to num2 num2 = third_var; 7.4.4 Sorting Variables Sorting is defined as arranging data in a certain order, is a very common activity in data processing. Many algorithms (techniques) are available to perform sorting. One sorting algorithm, which is quite simple to understand, but not necessarily the best, is given in the following program example. It sorts a list of integers in ascending order of magnitude by using an array. #include<stdio.h> #define N 7 void main() { int i,j, temp, arr[N] = {4,7,12,9,3,2,1}; printf("Unsorted list:"); for(i=0;i<N;i++) /* Printing array elements */ { printf("%d ", arr[i]); } for(i=0;i<N-1;i++) /* Sorting */ { for(j= i+1; j<N; j++) { if(arr[i]>arr[j]) { temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } printf("\nThe sorted list in ascending: "); for(i=0; i<N;i++) { printf("%d ", arr[i]); } /* printing sorted array elements */ } 8 Sample output: 7.5 Two Dimensional Arrays Some data fit better in a table with several rows and columns. This can be constructed by using two-dimensional arrays. A two dimensional array has two subscripts/indexes. Its declaration has the following form: Data_type array_name[size1][size2]; A two dimensional array can be thought of as a table having rows and columns. The first subscript refers to the row, and the second, to the column. Examples: int x[3][4]; float matrix[20][25]; The first line declares x as an integer array with 3 rows and 4 columns and the second line declares a matrix as a floating-point array with 20 rows and 25 columns. Descriptively, int x[3][4] can be depicted as follows: You can see that for [3][4] two-dimension array size; the total array size (the total array elements) is equal to 12. Hence: For n rows and m columns, the total size equal to mn 9 The item list is read starting from the first row from left to right, and then goes to the next row and so on. A set of string s can be stored in a two-dimensional character array with the left index specifying the number of strings and the right index specifying the maximum length of each string. For example, to store a list of 4 names with a maximum length of 10 characters in each name, we can declare: char name[4][10]; //can store 4 names, each is 10 characters long Just like the one-dimensional array, a two dimensional array can also be initialized. For example, the previous first array declaration can be rewritten along with initial assignments in any of the following ways: int x[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; or The results of the initial assignments in both cases are as follows: x[0][0]=1 x[1][0]=5 x[2][0]=9 x[0][1]=2 x[1][1]=6 x[2][1]=10 x[0][2]=3 x[1][2]=7 x[2][2]=11 x[0][3]=4 x[1][3]=8 x[2][3]=12 You can also show how the rows are filled during its initialization. For example, the array named x can be declared as follows: int x[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12} }; If the number of values given is insufficient to fill in the whole array, an initial value of zero will be assigned to all those locations, which are not given initial values explicitly. Example: 10 So, an initial value of zero will be assigned to x[2][2] and x[2][3]. Similarly, in declaration: An initial value of zero will be assigned to x[0][3], x[1][3] and x[2][3]. You can fill the whole array with zeroes by using: int x[3][4]={0}; //all array elements will be 0 In memory, despite their table-form arrangement, the elements of a two-dimensional array are stored sequentially, that mean one after another contiguously. An array of string can also be initialized. For example, in the following declaration: char name[4][10] = {"Dorji", "Tshering", "Tashi", "Pema"}; The values assigned are as follows: name[0] = "Dorji" name[1] = "Tshering" name[2] = "Tashi" name[3] = "Pema" Note that the second subscript here is unnecessary and therefore omitted. 7.6 Two-Dimensional Array Manipulation Example: prints 3 x 3 array’s subscript and their element. #include<stdio.h> #define m 3 #define n 3 void main() { int i,j; int x[m][n]={{1,2,3,}, {4,5,6}, {7,8,9}}; printf("\n3x3 array's subscripts and their respective elements\n"); printf("\n----------------------------------------------------\n"); for(i=0; i<m; i++) { for(j=0; j<n; j++) { printf("x[%d][%d]=%d\n",i,j,x[i][j]); } } } 11 Sample output: Example: prints 3 x 3 array’s elements in table #include<stdio.h> #define m 3 #define n 3 main() { int i,j; int x[m][n]={{1,2,3,}, {4,5,6}, {7,8,9}}; printf("\n3x3 array in a form of table\n"); printf("\n----------------------------\n"); for(i=0; i<m; i++) { for(j=0; j<n; j++) { printf("%4d",x[i][j]); } printf("\n"); } } Sample output: Example: Input 2x2 array elements from the keyboard and display #include<stdio.h> #define m 2 #define n 2 main() { 12 int i,j; int x[m][n]; printf(“Enter array elements:\n\n”); for(i=0; i<m; i++) { for(j=0; j<n; j++) { printf("\nx[%d][%d]=",i,j); scanf("%d", &x[i][j]); } } printf("\nDisplaying the array elements entered\n\n"); for(i=0; i<m; i++) { for(j=0; j<n; j++) { printf("x[%d][%d]=%d\n",i,j,x[i][j]); } } } Sample output: Example: Add 2 2x2 arrays and display the result. /* add array x and y and store the result in array z */ #include<stdio.h> #define m 2 #define n 2 main() { int i,j; int x[m][n], y[m][n], z[m][n]; printf("\n First table:\n"); for(i=0; i<m; i++) 13 { for(j=0; j<n; j++) { scanf("%d", &x[i][j]); } } printf("\n Second table:\n"); for(i=0; i<m; i++) { for(j=0; j<n; j++) { scanf("%d", &y[i][j]); } } /* Compute sum */ for(i=0; i<m; i++) { for(j=0; j<n; j++) { z[i][j] = x[i][j] + y[i][j]; } } printf("\nDisplaying the sum \n\n"); for(i=0; i<m; i++) { for(j=0; j<n; j++) { printf("%2d",z[i][j]); } printf("\n"); } } Sample output: 14 7.7 Multidimensional Arrays When an array has more than one dimension, we call it a multidimensional array. We have already looked at multidimensional arrays with two dimensions. The declaration and manipulation of other multidimensional arrays in C are quite similar to that of the two dimensional array. The declaration takes the following form: Data_type name[size1][size2]…[sizeN]; Example: int y[4][5][3]; Declares a 3-dimensional array with a depth of 4, 5 rows and 3 columns. The are no limitation on the dimension of the arrays, but the dimension more than two dimensional arrays are rarely used because of the complexity and code readability. 7.8 Strings A string is defined as a character array that is terminated by a null character (\0). The null character indicates the end of string. For example, the string "Hello" is stored in a character array, msg[], as follows: char msg[6]; msg[0] = 'H'; msg[1] = 'e'; msg[2] = 'l'; msg[3] = 'l'; msg[4] = 'o'; msg[5] = '\0'; When the string is retrieved, it will be retrieved starting at index 0 and succeeding characters are obtained by incrementing the index until the first NULL character is reached signaling the end of the string. Although C does not have a string data type, it allows string constants. A string constant is a list of characters enclosed in double quotes. For example char msg[6] = “hello”; 15 You do not need to add the null character at the end of the string manually- the C compiler does this for you automatically. C supports a wide range of string manipulation functions. The most common are: Name strcpy(s1,s2) strcat(s1,s2) strlen(s1) strcmp(s1,s2) strchr(s1,ch) strstr(s1,s2) Function Copies s2 into s1 Concatenates s2 onto the end of s1 Returns the length of s1 Returns 0 if s1 and s2 are the same; less than 0 if s1<s2; greater than 0 if s1>s2 Returns a pointer to the first occurrence of ch in s1 Returns a pointer to the first occurrence of s2 in s1 These functions use the standard header file string.h. The following program illustrated the use of these string functions: #include<stdio.h> #include<string.h> main() { char s1[20], s2[20]; int ls1,ls2; puts("Enter first string (s1):"); gets(s1); puts("Enter second string(s2):"); gets(s2); ls1 = strlen(s1); //find the length of the strings s1 and s2 ls2 = strlen(s2); printf("\nstrlen(s1)= %d\nstrlen(s2)= %d\n",ls1,ls2); strcat(s1,s2); //string concatenation printf("\nstrcat(s1,s2)= %s\n", s1); if(!strcmp(s1,s2)) //string compare printf("\n strcmp(s1,s2)=The strings are equal\n"); else printf("\nstrcmp(s1,s2)=The strings are not equal\n"); strcpy(s1, s2); // string copy printf("\nstrcpy(s1,s2)= %s\n",s1); if(strchr(s1, 'i')) printf("\nstrchr(s1, 'i')= character i is in s1\n", s1); else printf("\nstrchr(s1, 'i' )= character i is not in s2\n",s1); if (strstr(s2, "me")) printf("\nstrstr(s2, \"me\”)= substring me found in s2\n"); else printf("\nstrstr(s2, \”me\”)= substring me me not found\n"); } 16 Sample output: gets( ): gets reads a whole line of input into a string until a newline or EOF is encountered. It is critical to ensure that the string is large enough to hold any expected input lines. puts( ): puts writes a string to the output, and follows it with a newline character. Note: strcmp( ) returns false if the strings are equal. Be sure to use the logical operator! to reverse the condition, as shown in the above program, if you are testing for equality. 7.9 Passing Arrays to Functions 7.9.1 One Dimensional Arrays Like the values of simple variables, it is also possible to pass the values of an array to a function. To pass one dimensional array to a called function, it is sufficient to list the name of the array, without any subscripts, and the size of the array as arguments. For example, the function call largest(arr,n); will pass the whole arrary arr to the called function. The called function expecting this call must be appropriately defined. The largest function header might look like: void largest (int arr[], int n) Example program: To find the highest number from the given numbers #define N 4 void largest(int arr[], int n); void main() { int arr[N] = {4,7,12,9}; largest(arr,N); 17 getch(); } void largest(int arr[], int n) { int i, max, count; max = arr[0]; for(i=1;i<n;i++) { if(arr[i]>max) { max = arr[i]; count = i; } } printf("The max number is %d found at location %d", max,count); } 7.9.2 Two Dimensional Arrays Like simple arrays, we can pass multi-dimensional arrays to functions. The approach is similar to the one we did with one-dimensional arrays. The rules are simple: 1. The function must be called by passing only the array name. 2. In the function definition, we must indicate that the array has two-dimensions by including two sets of brackets. 3. The size of the second dimension must be specified. 4. The prototype declaration should be similar to the function header. Example program: Add 2 2x2 arrays and display the results using functions. #define m 2 #define n 2 void readArray(int arr[][n]); void computeSum(int arr1[][n], int arr2[][n], int arr3[][n]); void displayArray(int arr[][n]); void main() { int x[m][n], y[m][n], z[m][n]; printf("\n First table:\n"); readArray(x); printf("\n Second table:\n"); readArray(y); computeSum(x,y,z); 18 printf("\nDisplaying the sum \n\n"); displayArray(z); } void readArray(int arr[][n]) { int i, j; for(i=0; i<m; i++) { for(j=0; j<n; j++) { scanf("%d", &arr[i][j]); } } } void computeSum(int arr1[][n], int arr2[][n], int arr3[][n]) { int i,j; for(i=0; i<m; i++) { for(j=0; j<n; j++) { arr3[i][j] = arr1[i][j] + arr2[i][j]; } } } void displayArray(int arr[][n]) { int i,j; for(i=0; i<m; i++) { for(j=0; j<n; j++) { printf("%4d",arr[i][j]); } printf("\n"); } } Quick exercise: 1. Describe the array that is defined in each of the following statements. Indicate what values are assigned to the individual array elements. a) float f[8] = {2., 3., 5.,7.}; b) char flag[5] = “TRUE”; c) int p[2][4] = {1,1,2,2,3,3,4,4}; d) int p[2][4] = { {1,3,5,7}, {2,3,4,6} 19 }; e)char colors[3][6]={“red”,”green”,”blue”}; 2. Write an appropriate array definition: a) define one-dimensional, 12-element integer array called w. Assign the values 1,3,5,7,9,11,13,15,17,19,21,23 to the array elements. b) define one-dimensional character array called point. Assign the string “NORTH” to the array elements. c) define one-dimensional, 5-element character array called vowels. Assign the characters ‘a’,’e’,’i’,’o’,’u’. d) define two-dimensional, 3 x 4 integer array called x. Assign the following values to the array elements: 10 0 0 12 20 32 14 43 23 0 0 0 Practice: 1. Describe the output generated by each of the following programs: a) int i; int array[5] = {1,2,3}; for (i=0; i<=4; i++){ printf("element number %d is %d\n", i+1, array[i]); } b) char str[]="abcdefghijklmn"; char vowels[]="aeiou"; int i, j; for (j=0; vowels[j]!=’\0’; j++){ for (i=0; str[i]!=’\0’; i++){ if (str[i]==vowels[j]){ str[i]=’Y’; break; } } } printf("string=%s\n", str); c) #include <stdio.h> void main(void) { int i, charges[5]={7,8,7,6,7}; printf("elements of charges[] are "); for (i=0;i<5;i++){ printf("%d ", charges[i]);} zap(charges, 5); /* function call will affect charges */ printf("\nnow elements of charges[] are "); for (i=0;i<5;i++){ printf("%d ", charges[i]);} 20 } void zap(int array1[], int size) { int i; for (i = 0; i < size; i++) { array1[i] = 0; /* sets all elements to 0 */ } } d) #include<stdio.h> #include<string.h> main( ) { char str1[] = “The Pentagon”; char str2[] = “lost world”; printf (“%s”, strcpy(str1,str2)); } e) int student[3][3] = {{1,4,7}, {2,5,8}, {3,6,9}}; printf(“Mark = %d\n”, student[1][2]); printf(“Mark = %d\n”, student[2][1]); ********************************************************************************** Please bring all the errors to my notice either by sending an email ([email protected]/ [email protected]) or in person. I will appreciate your kind gesture! *********************************************************************************** 21 8. Structures and Unions 8.1 Structure A scalar variable holds a single value of a single type. An array holds several values of a single type. It is sometimes convenient to be able to group together items of information which are of different types. For example: an employee’s number, name, age and position are logically related, but these pieces of data are of different types and so are not suitable for storage in an array. A structure allows the programmer to group together data items of different types. Thus, a single structure can contain integer elements, floating-point elements and character elements. The individual structure elements are referred to as members. 8.2 Defining Structures A structure is a collection of related data items stored in one place and can be referenced by more than one names. Usually these data items are different basic data types. Therefore, the number of bytes required to store them may also vary. In general terms, the composition of the structure can be defined as: struct struct_name{ member 1; member 2; ……….. member m; }; The keyword struct tells the compiler that a structure is being declared; struct_name is a name/tag that identifies structures; and member 1, member 2,and member m; are individual member/field declarations. The individual members can be ordinary variables, pointers, arrays or other structures. The member names within a particular structure must be distinct from one another and individual members cannot be initialized within a structure type declaration. For example, to store and process a employee’s record with the elements number 1 (employee number), name, age and position, we can declare the following structure template. Here, struct is a keyword that tells the compiler that a structure template/composition is being declared and Employee is a tag/structure name that identifies structure. Tag is not a variable; it is a label for the structure’s template. Note that there is a semicolon after the closing curly brace. A structure tag is simply a label for the structure’s template but you name the structure tag using the same rules for naming variables. Compiler will not reserve memory for a structure or define a new variable. It just defines a new datatype, called Employee. Now that we’ve defined the new datatype, we can define variables to be of type Employee (just as we can define variables to be of type int, char, float): Structure variables can be declared in one of the following ways: 1. struct Employee{ int number; char name[30]; int age; char position[30]; }emp1,emp2; 2. struct{//no tag int number; char name[30]; int age; char position[30]; }emp1,emp2; 3. struct Employee{ int number; char name[30]; int age; char position[30]; }; Then in programs we can declare the structure something like this: struct Employee emp1,emp2; 2 The above three ways are used to declares two variables, emp1 and emp2, to be of type Employee. Each of these structure variables has four fields: number, name, age and position 8.3 Accessing the Structure Elements/members A structure element can be accessed and assigned a value by using the structure variable name, the dot operator (.) and the element’s name. Example: emp1.number = 4321; emp1.age = 34; emp1.name = Pema Tenzin emp1.position = lecturer strcpy(emp1.name, "Tashi Tsheden"); scanf("%s", emp1.position); To print out the contents of this structure we could do it as follows: printf(“Employee number: %d\n”, emp2.number); printf(“Name: %s\n”, emp2.name); printf(“Age: %d\n”, emp2.age); printf(“Position: %s\n”, emp2.position); It is often a good idea to define a function to print out structures of this type (saves you rewriting this over and over; makes your program easier to understand, improve, debug,…): 3 void printEmployee (struct Employee e) { printf("Employee number: %d\n", e.number); printf("Name: %s\n", e.name); printf("Age: %d\n", e.age); printf("Position: %s\n", e.position); } 8.4 Initializing the structure members The members of a structure variable can be assigned initial values in much the same manner as the elements of an array. The initial values must appear in the order in which they will be assigned to their corresponding structure members, enclosed in braces and separated by commas. The general form is: struct tag variable = {value 1, value 2,…,value m}; where value 1 refers to the value of the first member, value 2 refers to the value of the second member, and so on. Example: illustrates the assignment of initial values to the members of a structure variable #include<stdio.h> struct Employee{ int number; char name[30]; int age; char position[30]; }; void printEmployee(struct Employee e); void main() { struct Employee emp1 = {1234, "Tashi Tsheden", 32, "lecturer"}; printfEmployee(emp1); } void printEmployee(struct Employee e) { printf("Employee number: %d\n",e.number); printf("Name: %s\n", e.name); printf("Age: %d\n", e.age); printf("Position: %s\n",e.position); } Sample output: 4 Example: To read and print employee record. #include<stdio.h> struct Employee{ int number; char name[30]; int age; char position[30]; }emp1; void readEmployee(); void printEmployee(struct Employee e); void main() { void readEmployee(); printEmployee(emp1); } void readEmployee() { printf("Enter employee number:"); scanf("%d", &emp1.number); printf("Enter name:"); scanf("%s", &emp1.name); printf("Enter position:"); scanf("%s",&emp1.position); printf("Enter age:"); scanf("%d", &emp1.age); } void printEmployee(struct Employee e) { printf("\nEmployee number: %d",e.number); printf("\nName: %s", e.name); printf("\nAge: %d", e.age); printf("\nPosition: %s",e.position); } Sample output: Example: Passing structure to a function and function returning a structure /* To read and print two employee data */ #include<stdio.h> 5 struct Employee{ int number; char name[30]; int age; char position[30]; }emp1,emp2; struct Employee readEmployee(); void printEmployee(struct Employee e); void main() { printf("Enter information for a employee\n\n"); emp1 = readEmployee(); emp2 = readEmployee(); printf("\n\nDisplay information for a employee:\n\n"); printEmployee(emp1); printEmployee(emp2); } // Function returns a structure struct Employee readEmployee() { struct Employee emp; printf("\nEnter employee number:"); scanf("%d", &emp.number); printf("Enter name:"); scanf("%s", emp.name); printf("Enter age:"); scanf("%d", &emp.age); printf("Enter position:"); scanf("%s",emp.position); return emp; } void printEmployee(struct Employee e) { printf("\nEmployee number: %d\n",e.number); printf("Name: %s\n", e.name); printf("Age: %d\n", e.age); printf("Position: %s\n",e.position); } Sample output: 6 8.5 Nested Structures Structures can be nested, i.e. structures within structures. For example, suppose we defined a structure template as follows: struct Address { long int c_number; char dzongkhag[20]; }; We could then incorporate this into our Employee structure template as follows: struct Employee{ int number; char name[30]; int age; char position[30]; struct Address{ long int c_number; char dzongkhag[20]; } addr; }emp1; OR struct Address{ long int c_number; char dzongkhag[20]; }; struct Employee{ int number; char name[30]; int age; char position[30]; struct Address addr; }emp1; Example: Illustrates nested structures. #include<stdio.h> struct Employee { int number; char name[30]; int age; char position[30]; struct Address { long int c_number; char dzongkhag[20]; } addr; }emp1; 7 void printEmployee(struct Employee e); void main() { printf("Enter employee number:"); scanf("%d", &emp1.number); printf("Enter name:"); scanf("%s", &emp1.name); printf("Enter position:"); scanf("%s",&emp1.position); printf("Enter age:"); scanf("%d", &emp1.age); printf("Enter contact number:"); scanf("%ld", &emp1.addr.c_number); printf("Enter dzongkhag:"); scanf("%s", &emp1.addr.dzongkhag); printfEmployee(emp1); getch(); } void printEmployee(struct Employee e) { printf("\nEmployee number: %d",e.number); printf("\nName: %s", e.name); printf("\nAge: %d", e.age); printf("\nPosition: %s",e.position); printf("\nContact number: %ld", e.addr.c_number); printf("\nDzongkhag: %s",e.addr.dzongkhag); } Sample output: 8.6 Arrays of Structures Suppose you would like to store the information of 100 employees. It would be tedious and unproductive to create 100 different Employee variables and work with them 8 individually. It would be much easier to create an array of Employee structures. Structures of the same type can be grouped together into an array. We can declare an array of structures just like we would declare a normal array of variables. For example, to store and manipulate the information contained in 100 employee records, we use the following structure template: struct Employee{ int number; char name[30]; int age; char position[30]; }emp[100]; OR struct Employee{ int number; char name[30]; int age; char position[30]; }; struct Employee emp[100]; These statements declare 100 variables of type Employee (a structure). As in arrays, we can use a subscript to reference a particular Employee structure or record. For example, to print the name of the seventh employee, we could write the following statement: printf(“%s”, emp[6].name); Example: illustrates the use of arrays in structures /* Reads in the information of two employees and display */ #include<stdio.h> struct Employee { int number; char name[30]; int age; char position[30]; }emp1[2]; void printEmployee(struct Employee e[],int i); void main() { int i; for(i=0;i<2;i++) { printf("\nEnter record for employee # %d\n", i+1); printf("Enter employee number:"); scanf("%d", &emp1[i].number); 9 printf("Enter name:"); scanf("%s", &emp1[i].name); printf("Enter position:"); scanf("%s",&emp1[i].position); printf("Enter age:"); scanf("%d", &emp1[i].age); } for(i=0;i<2;i++) { printf("\n\nDisplay record for Employee # %d", i+1); printEmployee(emp1,i); } } void printEmployee(struct Employee e[],int i) { printf("\nEmployee number: %d",e[i].number); printf("\nName: %s", e[i].name); printf("\nAge: %d", e[i].age); printf("\nPosition: %s",e[i].position); } Sample output: 8.7 Difference between arrays and structures. Arrays: - same data type. 10 - - Memory (array size) is fixed. Accessing an element in array takes constant time. e.g. 5th element in an array can be accessed as array[4] i.e. searching/accessing an element in array takes comparatively less time than in structures. Adding/deleting an element in the array is comparatively difficult since the data already present in the array needs to be moved. Structures: - different data type. - Memory (structure size) can be changed dynamically. - Accessing an element in a list takes more time comparatively since we need to traverse through the list. - Adding/deleting takes less time than in arrays. 8.8 User-defined data types (typedef) The typedef is the user-defined new data types. Once a user-defined data type has been established, then new variables, arrays, structures can be declared in terms of this new data type. In general terms, a new data type is defined as: typedef type new-type; where type refers to an existing data type, and new-type refers to the new user-defined data type. Example: typedef int age; In this declaration age is a user-defined data type, which is equivalent to type int. Hence, the variable declaration, age male, female; is equivalent to writing int male, female; Similarly, the declaration typedef float height[50]; height male, women; defines height as a 50-element , floating point array type. Another way to express this is typedef float height; height men[50], women[50]; There is one application of typedef that you may find especially useful; it can be used to simplify the declaration of structure, union, or enumeration variables. 11 In defining structures, it eliminates the need to repeatedly write struct tag whenever a structure is referenced. For example, consider the following declaration; struct employee{ int number; char name[30]; int age; char position[30]; }; To declare a variable of type employee you must use a declaration like this: struct employee e; While there is certainly nothing whatsoever wrong with this declaration, it does require the use of two identifiers: struct and employee. However, if you apply typedef to the declaration of employee, as shown here, typedef struct employee{ int number; char name[30]; int age; char position[30]; }Emp; then you can declare variables of this structure using the following declaration: Emp e; Example: Modify the previous program by using typedef #include<stdio.h> typedef struct Employee{ int number; char name[30]; int age; char position[30]; }Emp; Emp readEmployee(); void printfEmployee(Emp e); void main() { Emp e1,e2; printf("Enter information for a employee\n\n"); e1 = readEmployee(); e2 = readEmployee(); printf("\n\nDisplay information for a employee:\n\n"); printfEmployee(e1); printfEmployee(e2); } 12 Emp readEmployee() { Emp emp; printf("Enter employee number:"); scanf("%d", &emp.number); printf("Enter name:"); scanf("%s", emp.name); printf("Enter age:"); scanf("%d", &emp.age); printf("Enter position:"); scanf("%s",emp.position); return emp; } void printfEmployee(Emp e) { printf("\nEmployee number: %d\n",e.number); printf("Name: %s\n", e.name); printf("Age: %d\n", e.age); printf("Position: %s\n",e.position); } You can see how easy it is to declare structure variable using typedef. It is certainly worth doing if you declare several structure variables. Using typedef can make your code easier to read. 8.9 Enumeration (enum) data type This is another user-defined data type consisting of a set of named constants called enumerators. Using a keyword enum, it is a set of integer constants represented by identifiers. The syntax is shown below: enum identifier{value1, value 2, vauue3,…value n}; the identifier is a user-defined enumerated data type which is used to declare variables as follows: enum identifier v1,v2, …vn; The compiler automatically sets integer digits beginning with 0 , unless specified otherwise to all the enumeration constants and are incremented by 1. Example: enum days{Mon,Tue,Wed,Thu,Fri,Sat,Sun}; Creates a new data type, enum days, in which the identifiers are set automatically to the integers 0 to 6. To number the days 1 to 7, use the following enumeration: enum days {Mon = 1, Tue, Wed, Thu, Fri, Sat, Sun}; Or we can re-arrange the order: 13 enum days {Mon, Tue, Wed, Thu = 7, Fri, Sat, Sun}; Then, the integer numbers assigned are: Mon Tue Wed Thu Fri Sat Sun = = = = = = = 0 1 2 7 8 9 10 Example: illustrates enum #include<stdio.h> enum days{mon = 1,tue,wed,thu,fri,sat,sun}; main() { enum days count; //declaring enum data type printf("Simple day count using enum\n\n"); for(count = mon; count<=sun; count++) { printf("%d\n",count); } } Sample output: 8.10 Unions Unions are a concept borrowed from structures and therefore follow the same syntax as structures. However there is major distinction between them in terms of storage. In structures, each member has its own storage location; where as all the members of a union shares the same location. This implies that, although a union may contain many members of different types, it can handle only one member at a time. The number of bytes used to store a union must be at least enough to hold the largest member. Like structures, a union can be declared using the keyword union as follows: union tag{ member 1; member 2; -------member m; }; 14 Example: union item { int p; float q; char n; }; Unions may be initialized when the variable is declared. But, unlike structures, it can be initialized only with a value of the same type as the first union member. Example: union item code = {12}; is valid but the declaration union item code = {12.5}; is invalid. This is because the type of the first member is int. Other members can be initialized by either assigning values or reading from the keyboard. Example: to print the contents of the unions. Only one at a time! #include<stdio.h> union item{ int p; float q; char n; }code; void writeOutput(union item c); main() { code.p = 12; code.q = 23.45; code.n = 'C'; printf("Display the union storage content\nONLY one at a time\n"); writeOutput(code); printf("\n\nSee, some of the contents are rubbish!\n"); code.n = 'd'; code.p = 13; code.q = 34.32; writeOutput(code); printf("\n\nSee, another inactive contents, rubbish!\n"); code.n = 'u'; code.q = 13.23; code.p = 32; 15 writeOutput(code); printf("\n\nSee, another inactive contents again, rubbish!”); printf(“\nBetter use struct data type!!\n"); } void writeOutput(union item c) { printf("\n p = %d", c.p); printf("\n q = %f",c.q); printf("\n n = %c",c.n); } Sample output: 8.11 Size of Structures and Unions The sizeof() function is used to tell us the size of a structure, union, or any variables. The size of a structure is equal to or greater than the sum of the sizes of its members. Example: struct s{ char c; int i; float f; }svar; Here, sizeof(svar) = 7 (1+2+4) atleast union u{ char c[10]; int i; }uvar; Here, sizeof(uvar) = 12(10+2) atleast The size of a union is always equal to the size of its largest member. 16 Example: union u{ char c; int i; float f; }uvar; Here,sizeof(uvar) = 4 8.12 Casting Casting will force an expression to be a specific type by using a cast, or typecast operation. The general form of cast is: (type)expression; where type is a valid C data type. Example: The expression (float)x/2; will make sure that the expression x/2 evaluates to type float. Example: main() { int i,j; float k; i = 5; j = 3; k = i/j; // this will print wrong value k = (float)(i/j); // this will print correct value printf(“%f”, k); } Quick exercise: 1. What will be the output generated by this program: #include<stdio.h> typedef union{ int i; float f; }udef; void func(udef u); main() { udef u; u.i = 100; 17 u.f = 0.5; func(u); printf(“Values in main:\n”); printf(“%d\n”,u.i); printf(“%f\n”,u.f); } void func(udef u) { u.i = 200; printf(“Values in function:\n”); printf(“%d\n”,u.i); printf(“%f\n”,u.f); } 2. Which of the following are correct statements for declaring one-dimensional array of structures of type struct item_bank? a) b) c) d) e) int item_bank items[10]; struct items[10] item_bank; struct item_bank items(10); struct item_bank items[10]; struct items item_bank[10]; 3. What will be the output of the following program? a) main() { struct xyz { int xyz; }; struct xyz xyz; xyz.xyz = 10; printf("%d",xyz.xyz); } b) struct xxx{ int i; char j; }; void abc(struct xxx aaa); main() { struct xxx zzz = {1,'a'}; abc(zzz); } void abc(struct xxx aaa) { printf("%d.....%c",aaa.i,aaa.j); } ***************************************************************************** Please bring all the errors to my notice either by sending an email ([email protected]/ [email protected]) or in person. I will appreciate your kind gesture! ***************************************************************************** 18 9 Pointers 9.1 What is a pointer? Before defining pointer lets start with a discussion of C variables in general. A variable in a program is something with a name, the value of which can vary. The way the compiler and linker handles this is that it assigns a specific block of memory within the computer to hold the value of that variable. The size of that block depends on the range over which the variable is allowed to vary. For example, on PC's the size of an integer variable is 2 bytes, and that of a long integer is 4 bytes. In C the size of a variable type such as an integer need not be the same on all types of machines. When we declare a variable we inform the compiler of two things, the name of the variable and the type of the variable. For example, we declare a variable of type integer with the name k by writing: int k; On seeing the "int" part of this statement the compiler sets aside 2 bytes of memory (on a PC) to hold the value of the integer. It also sets up a symbol table. In that table it adds the symbol k and the relative address in memory where those 2 bytes were set aside. Thus, later if we write: k = 2; we expect that, at run time when this statement is executed, the value 2 will be placed in that memory location reserved for the storage of the value of k. In C we refer to a variable such as the integer k as an "object". In a sense there are two "values" associated with the object k. One is the value of the integer stored there (2 in the above example) and the other the "value" of the memory location, i.e., the address of k. A pointer is a variable that contains the memory address (i.e. the memory location) of another variable (k), where, the actual data (2 in above example) is stored. In C when we define a pointer variable we do so by preceding its name with an asterisk. In C we also give our pointer a type which, in this case, refers to the type of data stored at the address we will be storing in our pointer. For example, consider the variable declaration: int *ptr; ptr is the name of our variable (just as k was the name of our integer variable). The '*' informs the compiler that we want a pointer variable, i.e. to set aside however many bytes is required to store an address in memory. The int says that we intend to use our pointer variable to store the address of an integer. Such a pointer is said to "point to" an integer. However, note that when we wrote int k; we did not give k a value. If this definition is made outside of any function ANSI compliant compilers will initialize it to zero. 1 Similarly, ptr has no value that is we haven't stored an address in it in the above declaration. In this case, again if the declaration is outside of any function, it is initialized to a value guaranteed in such a way that it is guaranteed to not point to any C object or function. A pointer initialized in this manner is called a "null" pointer. Now to store in ptr the address of our integer variable k. To do this we use the unary & operator and write: ptr = &k; What the & operator does is retrieve the address of k and copies that to the contents of our pointer ptr. Now, ptr is said to "point to" k. The "dereferencing operator" is the asterisk and it is used as follows: *ptr = 7; will copy 7 to the address pointed to by ptr. Thus if ptr "points to" (contains the address of) k, the above statement will set the value of k to 7. That is, when we use the '*' this way we are referring to the value of that which ptr is pointing to, not the value of the pointer itself. Similarly, we could write: printf("%d",*ptr); to print to the screen the integer value stored at the address pointed to by ptr;. Example: Program to print the address of a variable along with its value and shows how to access the value stored in a variable using pointer. #include<stdio.h> main() { int a =1, b=2; int *ptr; // declare a pointer variable ptr ptr = &a; //assign address of a to ptr printf("a = %d is stored at address %u\n", a, &a); printf("b = %d is stored at address %u\n", b, &b); printf("ptr = %u is stord at address %u\n", ptr, &ptr); printf("ptr points to the value %d\n", *ptr); } Sample output: 2 Note that we have used %u format for printing address values. Memory addresses are unsigned integers. Example: Program to assign a value to the address of the variable using pointer #include<stdio.h> main() { int k; //declare an integer variable k int *ptr; //declare a pointer variable ptr ptr = &k; //assign address of k to ptr *ptr=7; // copy 7 to the address pointed to by ptr printf(" Address of k = %u\n”,&k); printf(“Value of ptr = %u, stored at address = %u\n”,ptr, &ptr); printf(“Value of k = %d”, *ptr); } The statement *ptr = 7; puts the value 7 at the memory location whose address is the value of ptr. We know that the value of ptr is the address of k and therefore, k is assigned a value 7. Thus, it is equivalent to k = 7; Sample output: 9.2 Chain of pointers It is possible to make a pointer to point to another pointer, thus creating a chain of pointers as shown. p2 p1 variable address 2 address 1 value Here, the pointer variable p2 contains the address of the pointer variable p1, which points to the location that contains the desired value. This is known as multiple indirections. 3 A variable that is a pointer to a pointer must be declared using additional indirection operators symbols in front of the name. Example: int **p2; This declaration tells the compiler that p2 is a pointer to a pointer of int type. Remember, the pointer p2 is not a pointer to an integer, but rather a pointer to an integer pointer. We can access the target value indirectly pointed to by pointer by applying the indirection operator twice. Example: Program to illustrate the use of pointer to a pointer (**p2) main() { int x, *p1, **p2; x = 100; p1 = &x; /* address of x */ p2 = &p1; /* address of p1 */ printf("p1 = %d\n", *p1); printf("p2 = %d",**p2); } Sample output: 9.3 Pointer Expressions Like other variables, pointer variables can be used in expressions. Example: If p1 and p2 are properly declared and initialized pointers, then the following statements are valid. y = sum z = *p2 *p1 * *p2; same as (*p1) * (*p2) = sum + *p1; 5* - *p2/ *p1; same as (5*(-(*p2)))/(*p1) = *p2 + 10; Note that there is a blank space between / and *. The symbol /* is considered as the beginning of a comment and therefore the statement fails. Example: main() { int a,b,sum1,sum2, *p1,*p2; a = 15; b = 25; 4 p1= &a; p2= &b; sum1 = a+b; sum2 = *p1 + *p2; printf("sum1 = %d, sum2 = %d", sum1,sum2); Sample output: Example: illustrate the use of pointers in arithmetic operations. main() { int a,b,*p1,*p2,x,y,z; a = 12; b=4; p1 = &a; p2 = &b; x = *p1 * *p2 - 6; y = 4 * - *p2 / *p1 + 10; z = *p1 * *p2 - 3; printf("a = %d, b = %d\n", a,b); printf("x = %d, y = %d, z = %d", x,y,z); Sample output: 9.4 Pointers and arrays A special relationship exists between pointers and arrays. An array name without brackets is a pointer to the array’s first element. So, if a program declared an array data[], data (array’s name) is the address of the first array element and is equivalent to the expression &data[0] that means references the address of the array’s first element. data equivalent to &data[0] or a pointer to the array’s first element. The array’s name is, therefore a pointer to the array’s first element and therefore to the string if any. Element of an array are stored in sequential memory locations with the first element in the lowest address. Subsequent array elements, those with an index greater than 0 are stored at higher addresses. Also, if an array named list[] is a declared array, the expression *list is the array’s first 5 element, *(list + 1) is the array’s second element, and so on. Generally, the relationship is as follows: *(list) == list[0] //first element *(list + 1) == list[1] //second element *(list + 2) == list[2] //third element … … *(list + n) == list[n] //the nth element So, you can see the equivalence of array subscript notation and array pointer notation. Consider the following: int my_array[] = {1,23,17,4,-5,100}; Here we have an array containing 6 integers. We refer to each of these integers by means of a subscript to my_array, i.e. using my_array[0] through my_array[5]. But, we could alternatively access them via a pointer as follows: int *ptr; ptr = &my_array[0]; /* point the pointer at the first integer in an array */ And then we could print out our array either using the array notation or by dereferencing our pointer. Example: Accessing one dimensional array elements using array notation and the pointer //Array //Pointer main() { int my_array[] = {1,23,17,4,-5,100}; main() { int my_array[] = {1,23,17,4,-5,100}; int *ptr, i; ptr = &my_array[0]; //point the pointer to the first element of an array ptr = my_array; //same as above printf("\n Print array elements: \n"); for(i=0;i<6;i++) { printf(" my_array[%d] = %d\n", i, my_array[i]); } } Sample output: printf("\n Print array elements: \n"); for(i=0;i<6;i++) { printf(" ptr+%d = %d\n",i,*(ptr+i)); } } Sample output: 6 Now, change the line printf("ptr+%d = %d\n",i,*(ptr+i)); to printf("ptr + %d = %d\n",i, *ptr++); Run and observe the result. Then change it to: printf("ptr + %d = %d\n",i, *(++ptr)); Run again and observe the result. 9.5 Pointers and character strings In C, strings are treated like character arrays. Therefore they are declared and initialized as follows: char str[6] = “hello”; The compiler automatically inserts the null character ‘\0’ at the end of the string. C supports an alternative method to create strings using pointer variables of type char. Example: char *str = “hello”; This creates a string and then stores its address in the pointer variable str. The pointer str now points to the first character of the string “hello” as: h e l L 0 \0 str We can then print the content of the string str using either printf or puts function as follows: printf(“%s”,str); puts(str); Remember, although str is a pointer to the string, it is also the name of the string. Therefore, we do not have to use indirection operator (*) here. Like in one-dimensional arrays, we can use a pointer to access the individual characters in a string. Example: To access individual characters in a string using pointer. main() { char *str; str="hello"; while(*str != '\0') { printf("%c is stored at address %u\n",*str,str); *str++; } } Sample output: 7 9.6 Array of Pointers A multidimensional array can be expressed in terms of an array of pointers. The newly defined array will have one less dimension than the original multidimensional array. Each pointer will indicate the beginning of a separate (n-1) dimensional array. A two dimensional array can be defined as a one-dimensional array of pointers as: data-type *array[rows]; instead of data-type array[rows][cols]; Example: Suppose x is a two-dimensional integer array having 3 rows and 4 columns, we can define x as a one-dimensional array of pointers by writing int *x[3]; here, x[0] points to the beginning of the first row, x[1] points to the beginning of the second row, and so on. Note that the number of elements within each row is not explicitly specified. An individual array element, such as x[2][3], can be accessed by writing *(x[2] + 3); In this expression, x[2] is a pointer to the first element in row 2, and (x[2]+3) is a pointer to the fourth element (located at 3) within row 2. Therefore, *(x[2] + 3) refers to x[2][3]. Example: Program to read and display 2-dimensional array having 2 rows and 2 columns using array of pointers. #define m 2 #define n 2 main() { int i,j; int *x[m]; //Read array for(i=0; i<m; i++) { for(j=0; j<n; j++) { printf("\nx[%d]=",i); scanf("%d", x[i]+j)); } } 8 printf("\nDisplay array \n\n"); for(i=0; i<m; i++) { for(j=0; j<n; j++) { printf("%2d",*(x[i]+j)); } printf("\n"); } Sample output: One important use of pointers is in handling of a table of strings. Consider the following array of strings: char name[3][25]; this says that the name is a table containing three names, each with a maximum length of 25 characters (including null character). The total storage requirements for the name table are 75 bytes. We know that rarely the individual strings will be of equal lengths. Therefore, instead of making each row a fixed number of characters, we can make it a pointer to a string of varying length. Example: char *name[3] = { “New Zealand”, “Australia”, “India” }; In these declarations, name is an array of three pointers to characters, each pointer pointing to a particular name as: name[0] Æ New Zealand name[1]Æ Australia name[2]Æ India This declaration allocates only 28 bytes, sufficient to hold all the characters. To print out the names, we write as: for(i=0;i<3;i++) { printf(“%s\n”, name[i]) } 9 To access the jth character in the ith name, we write as: *(name[i]+j) 9.7 Pointers as arguments to functions All C functions have an identical template, composed of a unique name, a return type, argument(s), and a body. Function names are only identifiers that are treated as pointers without explicitly being declared. The return type of a function is understood to represent its value. A function body contains expressions, keywords, and statements that control the actions that are to be performed. Functions may optionally be declared to accept arguments. They can be passed as arguments by value and by reference (or by address). When an argument is passed by value, the data item is copied to the function. Thus, any alteration made to the data item within the function is not carried into the calling routine. When an argument is passed by reference, however (i.e. when a pointer is passed to a function), the address of a data item is passed to the function. Any change that is made to the data item (i.e. to the contents of the address) will be recognized in both the function and the calling routine. Example: Program to illustrate call by value and call by reference Void f(int p); main() { int x = 9; f(x); //f can't change x printf("\nx after calling f()=%d",x); } void f(int p) { p+=10; //p changed but x unchanged printf("p in f()= %d", p); } void f(int *p); void main() { int x = 9; f(&x); //call by reference printf("\nx after calling f()=%d",x); } void f(int *p) { *p+=10; // changes the value of x printf("p in f()= %d",*p); } Sample output: Sample output: Example: Program to illustrate a function returning more than one value at a time using call by reference. void sp(int num,int *s ,int *p); void main() { int sum,product,num; printf("Enter number:"); scanf("%d", &num); sp(num,&sum,&product); printf("\n Sum = %d, Product = %d ", sum,product); } 10 void sp(int num,int *s ,int *p) { *s = num + num; *p = num * num; } Since the addresses are passed, any changes made in values stored at the addresses contained in variables would make the change effective even in main. Thus the limitation of the return statement, which can return only one value from a function at a time, has been overcome, and it is now possible to return two values from the called function. 9.8 Functions returning pointers We have seen so far that a function can return a single value by its name or return multiple values through pointer parameters. Since pointers are data types in C, we can also force a function to return a pointer to the calling function. Example: int *f(int *i); main( ) { int *p,j; p=f(&j); printf(“%d”, *p); } int *f(int *i) { *i = 20; return(i); //address of i } Sample Output: 20 The function f receives the address of the variables j. Using the pointer variable i, the value of i is assigned to 20 and then returns the address of its location. The returned value is then assigned to the pointer variable p in the calling function. In this case, the address of j is returned and assigned to p and therefore the output will be the value of j which is 20. 9.9 Pointers and Structures As you may know, we can declare the form of a block of data containing different data types by means of a structure declaration. The beginning address of a structure can be accessed in the same manner as any other address, through the use of the address (&) operator. Thus if variable represents a structure-type variable, then &variable 11 represents the starting address of that variable. We can declare a pointer variable for a structure by writing struct tag *ptvar where, tag is the structure name and ptvar is the name of the pointer variable. We can then assign the beginning address of a structure variable to this pointer by writing ptvar=&variable Example: Consider the following structure declaration typedef struct employee { char fname[20]; /* last name */ char lname[20]; /* first name */ int age; /* age */ float rate; /* e.g. 12.75 per hour */ }pdata; pdata emp, *pemp; In this example, emp is a structure variable to type pdata, and pemp is a pointer variable whose object is a structure variable of type pdata. Thus the beginning address of pdata can be assigned to pemp by writing pemp= &emp; An individual structure member can be accessed in terms of its corresponding pointer variable by writing ptvar->member where ptvar refers to a structure-type pointer variable and the -> is comparable to the period(.) operator. Thus the expression ptvar->member is equivalent to writing variable.member Example: To read and print employee record using pointer #include<stdio.h> typedef struct Employee{ int number; char name[30]; int age; char position[30]; }emp1; void printEmployee(emp1 *e); void main() { emp1 emp, *pemp; pemp = &emp; 12 printf("Enter employee number:"); scanf("%d", &pemp->number); printf("Enter name:"); scanf("%s", &pemp->name); printf("Enter position:"); scanf("%s",&pemp->position); printf("Enter age:"); scanf("%d", &pemp->age); printEmployee(pemp); } void printEmployee(emp1 *e) { printf("\nDisplay employee information\n”); printf("\nEmployee number: %d",e->number); printf("\nName: %s", e->name); printf("\nAge: %d", e->age); printf("\nPosition: %s",e->position); } Sample output: 9.10 Dynamic memory allocation In our earlier programs, we defined the array such as int arr[10]; This is a conventional array definition. Now, as we look at pointer we can also define the array as a pointer variable. Thus we can define int *arr; rather than int arr[10]. A conventional array definition results in a fixed block of memory being reserved at the beginning of program execution, whereas this does not occur if the array is represented in terms of a pointer variable. Therefore, the use of a pointer variable to represent an array requires some type of initial memory assignment before the array elements are processed. This process of allocating memory at run time is known as dynamic memory allocation. The malloc library function is used to dynamically allocate memory for the array of pointer variable. Example: Suppose x is a one-dimensional, 10 element array of integers. It is possible to define x as a pointer variable rather than an array. Thus we can write 13 int *x; rather than int x[10]; or #define SIZE 10 int x[SIZE]; x is not automatically assigned a memory block when it is defined as a pointer variable, whereas a block of memory large enough to store 10 integer quantities will be reserved in advance when x is defined as an array. To assign sufficient memory for x, we can make use of the library function malloc, as follows: x= (int *) malloc(10*sizeof(int)) This function reserves a block of memory whose size (in bytes) is equivalent to 10 integer quantities. Example: Program to read and print one-dimensional array elements. The size of an array is specified at run-time. void readinput(int n, int *arr); void writeoutput(int n, int *arr); main() { int *arr, n; printf ("How many array elements to enter:"); scanf("%d", &n); //allocate memory arr = (int *) malloc(n * sizeof(int)); readinput(n, arr); writeoutput(n,arr); } void readinput(int n, int *arr) { int i; printf("\n Read the array elements:\n"); for(i=0;i<n;i++) { printf("arr[%d]= ", i); scanf("%d", arr+i); } } void writeoutput(int n, int *arr) { int i; printf("\n Display the array elements:\n"); for(i=0;i<n;i++) { printf("arr[%d]= %d\n", i, *(arr+i)); } } Sample output: 14 Quick exercise: 1. What is the output of the following program? a) void main() { int i=3; int *j; int **k; j=&i; k=&j; printf(“%d”,**k); } b) void main() { int a=5,b=10,c; int *p=&a,*q=&b; c=p-q; printf("%d",c); } c. void main() { int i=5,j; int *p,*q; p=&i; q=&j; j=5; printf("value of i : %d value of j : %d",*p,*q); } d. #include<stdio.h> struct field { int a; 15 char b; }; main() { struct field *pb, bit={5, 'A'}; pb= &bit; pb->a = 89; pb->b = ‘R’; printf("%c,%d", bit.b,bit.a); } ***************************************************************************** Please bring all the errors to my notice either by sending an email ([email protected]/ [email protected]) or in person. I will appreciate your kind gesture! ***************************************************************************** 16 10. Preprocessor The C preprocessor is a collection of special statements, called directives that are executed at the beginning of the compilation process. The preprocessor directives are shown here: #define #ifndef #endif #pragma #ifdef #else #line #if #elif #include #error #undef As you can see, all preprocessor directives begin with a # sign and do not end with a semicolon. In addition, each preprocessing directive must be on its own line. For example, this will not work: #include<stdio.h> #include<string.h> 10.1 #define We have already seen that the #define statement can be used to define symbolic constants within a C program. At the beginning of the compilation process, all symbolic constants are replaced by their equivalent text. Thus, symbolic constants provide a form of shorthand notation that can simplify the organization of a program. The #define statement can be used for more, however, than simply defining symbolic constants. In particular, it can be used to define macros; i.e. single identifiers that are equivalent to expressions, complete statements or groups of statements. Example: #include<stdio.h> #define sqr x * x void main() { int x=5; printf("%d", sqr); } This program contains the macro sqr, which represents the expression x * x. When the program is compiled, the expression x * x will replace the identifier sqr within the printf statement, so the printf statement will become printf("%d", x * x); #include<stdio.h> #define sqr x * x main() { int x; for(x=1; x<5; x++) { printf("% square = %d\n", x, sqr); } } 1 Sample output: A macro definition may include arguments, which are enclosed in parenthesis. When a macro is defined in this manner, its appearance within a program resembles a function call. Example: illustrates macros with arguments #include<stdio.h> #define sqr(x) x * x main() { int x; for(x=1; x<5; x++) { printf("%d square = %d\n", x, sqr(x)); } } There are possible side effects associated with the use of macros, particularly when calling arguments are involved. In the program below you have to put additional parenthesis within the macro definition as shown. Otherwise, each appearance of x will be replaced by i+1 without the parenthesis. Therefore, the result of the macro substitution will be i+1*i+1. Take for example i = 1, we have 1+1*1+1 =3, which is incorrect (suppose to be 4 (2*2)). Example: illustrate the side affects using macros when calling arguments #include<stdio.h> #define sqr(x) x * x main() { int i; for(i=0; i<5; i++) { printf("%d squared = %d\n", i+1, sqr(i+1)); } } #include<stdio.h> #define sqr(x) (x) * (x) void main() { int i; for(i=0; i<5; i++) { printf("%d squared = %d\n", i+1, sqr(i+1)); } } Sample output: Sample output: 2 Multiline macros can be defined by placing backward slash (\) at the end of each line except the last. This feature permits a single macro (i.e. a single identifier) to represent a compound statement. Example: Program to read and print array elements using macors. #include<stdio.h> #define read for(i=0;i<4;i++)\ {\ printf("a[%d]= ", i);\ scanf("%d", &a[i]);\ } #define print for(i=0;i<4;i++)\ {\ printf("a[%d]=%d",i,a[i]);\ } main() { int i; int a[4]; printf("Enter array:\n"); read printf(“\nPrinting array:\n”); print } Sample output: 10.2 #include An external file containing functions or macro definitions can be included as a part of a program so that we need not rewrite those functions or macro definitions. This is achieved by the preprocessor directive #include “filename” where filename is the name of the file containing the required definitions or functions. At this point, the preprocessor inserts the entire contents of filename into the source code of the program. When the file name is included within the double quotation marks, the search for the file is made first in the current directory and then in the standard dirctories. Alternatively this directive is can take the form: #include<filename> without double quotation marks. In this case, the file is searched only in the standard directories. If an included file is not found, an error is reported and compilation is terminated. 3 Example: //include.c #include<stdio.h> #include<conio.h> #include "fundef.h" main() { int a,b,s,p; a = readNum(); b = readNum(); s = sum(a,b); p = product(a,b); printf("sum of %d and %d = %d\n", a,b,s); printf("Product of %d and %d = %d",a,b,p); } //fundef.h saved in C:\TC\INCLUDE int readNum() { int n; scanf("%d",&n); return n; } int sum(int i, int j) { int r; r = i+j; return r; } int product(int i, int j) { int r; r = i*j; return r; } Sample output: 10.3 #error The #error directive is used to produce diagnostic messages during debugging. The general form is #error error_message When the #error directive is encountered, it displays the error message and terminates processing. Example: When the following program is compiled, the error messages should be generated as shown. 4 Then correct the error by defining myVal to 2 as the following program and when you rebuild, it should be OK. 10.4 Conditional Compilation Directives There are several directives that allow you to selectively compile portions of your program’s source code. This process is called conditional compilation. The most commonly used conditional compilation directives are #if,#else,#elif,and #endif. These directives allow you to conditionally include portions of code based upon the test condition. The general form of #if is: #if TC action; #endif If the TC is true, the action part is compiled. Otherwise, it is skipped. The #endif directive marks the end of an #if block. Example: simple #if #include<stdio.h> #define MAX 100 main() { #if MAX>99 printf(“compiled for array greater than 99.\n”); #endif } This program displays the message on the screen because MAX is greater than 99. This example illustrates an important point. The expression that follows that #if is evaluated at compile time. Therefore, it must contain only previously defined identifiers and constants - no variables may be used. 5 The #else directive works much like the else that is part of the C language. It establishes an alternative if #if fails. The previous example can be expanded as shown here: Example: #if/#else #include<stdio.h> #define MAX 10 main() { #if MAX>99 printf(“Compiled for array greater than 99.\n”); #else printf(“Compiled for small array.\n”); #endif } In this case, MAX is defined to be less than 99, so the #if portion of the code is not compiled. The #else alternative is compiled, however, and the message Compiled for small array. is displayed. The #elif directive means “else if” and establishes an if-else-if chain for multiple compilation options. If the test condition is true, that block of code is compiled and no other #elif test conditions are tested. Otherwise, the next block in the series is checked. The general form of #elif is: #if TC1 Action1 #elif TC2 Action2 #elif TC3 Action3 . . . #elif TCN ActionN #endif Example: #elif #define #define #define #define US 0 ENGLAND 1 FRANCE 2 ACTIVE_COUNTRY US #if ACTIVE_COUNTRY == US char currency[] = “dollars”; #elif ACTIVE_COUNTRY == ENGLAND char currency[] = “pound”; #else char currency[] = “franc”; #endif 6 #ifdef and #ifndef Another method of conditional compilation uses the directives #ifdef and #ifndef, which means “if defined” and “if not defined”, respectively. The general form of #ifdef is: #ifdef macro_name Action #endif If the macro_name has been previously defined in a #define statement, the block of code will be compiled. The general form of #ifndef is: #ifndef macro_name Action #endif If macro_name is currently undefined by a #define statement, the block of code is compiled. Both #ifdef and #ifndef may use an #else or #elif statement. Example: #ifdef and #ifndef #include<stdio.h> #define TED 10 main() { #ifdef TED printf(“Hi Ted\n”); #else printf(“Hi anyone\n”); #endif #ifndef RALPH printf( “Ralph is not defined\n”); #endif } 10.5 #undef The #undef directive removes a previously defined definition of the macro name that follows it. That is it “undefines” a macro. The general form for #undef is: #undef macro_name Example: #undef #define LEN 100 #define WIDTH 100 char array[LEN][ WIDTH]; #undef LEN #undef WIDTH /*at this point both LEN and WIDTH are undefined*/ 7 Both LEN and WIDTH are defined until the #undef statements are encountered. Example: Sample using #define, #ifdef, #ifndef, #undef, #else and #endif #include<stdio.h> #define chap10 10 #define preprocessor 10.2 main() { #ifdef chap10 printf("Chap10 is defined.\n"); #else printf("Chap10 is not defined.\n"); #endif #ifndef preprocessor printf("Preprocessor is not defined.\n"); #else printf("Preprocessor is defined.\n"); #endif #ifdef myRevision printf("MyRevision is defined.\n"); #else printf("MyRevision is not defined.\n"); #endif #undef preprocessor #ifndef preprocessor printf("preprocessor is not defined.\n"); #else printf("preprocessor is defined.\n"); #endif } Sample output: 10.6 Difference between macros and functions When calling a function the compiler enters a call-sequence (which takes time) and allocates a new stack frame for that function (which takes text stack space) so that the function's body can be executed. After it's done it enters a returning-sequence phase (which takes time). A macro does not need anything of the above, because it's preprocessor's job to expand a macro, it's only about text replacement, not about compiler stuff or code-generating issues. So time and space is not wasted in doing what a function would need in order to be executed. Therefore macros are generally used as part of small 8 functions to save the time of calling functions. That makes functions and macros completely different even if the result of using both is, in some cases, the same. One must know it's suitable to use one or the other. For instance: you can not point a pointer to a macro, but you can use pointers to functions. One important thing you must understand while using Macros is that TypeChecking is not done at any point of time. Quick exercise: 1. Write a program to find the area of a rectangle by defining a macro area, which represents length * breadth. 2. Write a macro called max that utilizes the conditional operator (?:) to determine the maximum of a and b, where a and b are integer quantities. ***************************************************************************** Please bring all the errors to my notice either by sending an email ([email protected]/ [email protected]) or in person. I will appreciate your kind gesture! ***************************************************************************** 9 11. File Handling in C 11.1 What is a File? Until now, we have been using the functions such as scanf and printf to read and write data. These are console oriented I/O functions, which always use the terminal (keyboard and screen) as the target place. This works fine as long as the data is small. However, many real-life problems involve large volumes of data and in such situations, the consoleoriented I/O operations pose two major problems. 1. It becomes cumbersome and time consuming to handle large volumes of data through terminals. 2. The entire data is lost when either the program is terminated or the computer is turned off. It is therefore necessary to have a more flexible approach where data can be stored on the disks and read whenever necessary, without destroying the data. This method employs the concept of files to store data. A file is a place on the disk where group of related data is stored. C supports a number of functions that have the ability to perform basic file operations, which include: Naming a file Opening a file Reading data from a file Writing data to a file Closing a file There are two distinct ways to perform file operations in C: 1. Low-Level I/O operation (it uses UNIX system calls) 2. High-Level I/O operation (it uses functions in C’s standard I/O library) Table: File I/O functions Function Name Operation fopen() - creates a new file for use - opens an existing file for use fclose() - closes a file which has been opened for use getc() - reads a character from a file putc() - writes a character to a file fprintf() - writes a set of data values to a file fscanf() - reads a set of data values from a file getw() - reads an integer from a file putw() - writes an integer to a file fseek() - sets the position to a desired point in the file ftell() - gives the current position in a file (in terms of bytes from the start ) rewind() - sets the position to the beginning of the file 1 11.2 Opening a File First things first: we have to open a file to be able to do anything else with it. For this, we use fopen. This function takes two arguments - the first one is the path to your file, including the filename. So if your file sits in the same directory as your C source file, you can simply enter the filename in here. The second argument determines how the file is opened by your program. The general format for declaring and opening a file: FILE *fp; fp = fopen(“filename”, “mode”); The first statement declares the variable fp of type FILE to point to a file located on the computer. FILE is a structure that is defined in the I/O library (in stdio.h). The second statement opens the file with mode specified. Finally, fopen returns a FILE pointer if the file was opened successfully, else it returns NULL. Note that both the filename and modes are specified as strings. They should be enclosed in double quotation marks. Depending on the mode specified, one of the following actions may be performed. 1. When the mode is “writing”, a file with the specified name is created, if the file does not exist. The contents are deleted if the file already exists. Example: FILE *fp; fp = fopen(“stdRecord”, “w”); 2. When the mode is “appending”, the file is opened with the current contents safely. A file with the specified name is created if the file does not exist. Example: FILE *fp; fp = fopen(“stdRecord”, “a”); 3. When the mode is “reading”, and if it exists, then the file is opened with the current contents safely, otherwise an error occurs. Example: FILE *fp; fp = fopen(“stdRecord”, “r”); The Table below shows the possible ways to open a file by various modes. Table: Modes of a file 2 11.3 Closing a File When you've finished with a file, its best if you closed it - seems logical enough! Simply pass it a FILE pointer, but be warned, don't pass a NULL pointer (it points to nothing), or your program might crash. Example: fclose(fp); 11.4 Opening Files for Reading Only Firstly, a text file is a file that you can open and read its contents visually - for example, C source files, .txt files, HTML etc - anything that looks "neat" in Notepad. A binary file is a file that is written in machine code - usually seen as a mass of weird characters in Notepad! Examples are bitmaps, executables etc. In this chapter, we're going to be looking at standard text files. If you wanted to open a binary file, simply put a b at the end of the second argument of fopen. To open a text file for reading only, pass "r" as the second argument of fopen, as demonstrated in this example: #include <stdio.h> void main() { FILE *fp; /* declare a FILE pointer */ fp = fopen("D:\hello.txt", "r"); /* open a text file for reading */ if(fp==NULL) { printf("Error: can't open file.\n"); } else { printf("File opened. Now closing it...\n"); fclose(fp); } } The file named “hello.txt” has been created earlier in Notepad and saved in a drive D: Notice that the function call is assigned to fp. If the file was successfully opened, fp would point to that file, else it'll point to nothing (a pointer that points to nothing is called a NULL POINTER). A check is performed to see if file is a NULL pointer: if(file==NULL). If so, display an error message and quit the program. 3 If file is not a NULL pointer, display a success message and close the file. 11.5 Creating Files for Writing Only To create a text file for writing only, pass "w" into fopen as the second argument. Example: #include <stdio.h> void main() { FILE *fp; /* declare a FILE pointer */ fp = fopen("D:\writing.txt", "w");/* create a text file for writing */ if(fp==NULL){ printf("Error: can't create file.\n"); } else { printf("File created. Now closing it...\n"); fclose(fp); } } Now, check the drive ‘D’ you will see a text file called "writing" created. Warning: Creating a file that already exists wipes all the data from that file! 11.6 Input/output operations on files Once a file is opened, reading out of or writing to it is accomplished using the standard I/O routines that are listed in the table above. 11.6.1 Using getc and putc functions The simplest file I/O functions are getc and putc. These are analogous to getchar and putchar functions and handle one character at a time. Example: Assume that a file is opened with mode “w” and the file pointer fp. Then the statement putc(c,fp); writes the character contained in the character variable c to the file associated with FILE pointer fp. Similarly, getc is used to read a character from a file that has been opened in read mode. Example: The statement c = getc(fp); would read a character from the file whose file pointer is fp. 4 The file pointer moves by one character position for every operation of getc or putc. The getc will return an end-of-file marker EOF, when an end of the file has been reached. Therefore, the reading should be terminated when EOF is encountered. Example: Program to read data from the keyboard and write it to a file called “studentRecord”. Then read the same data from the file and display it on the screen. #include<stdio.h> void main() { FILE *fp; char c; //create the file for writing fp = fopen("D:\studentRecord.txt","w"); if(fp == NULL) { printf("Error: Can't create file.\n"); } else{ printf("File created. Data Input\n"); while((c =getchar()) != EOF) putc(c,fp); printf("\nClosing file.\n"); fclose(fp); } //open the file for reading fp = fopen("D:\studentRecord.txt","r"); if(fp==NULL) { printf("Error: Can't open file.\n"); } else{ printf("\nFile opened. Data output\n"); while((c = getc(fp))!=EOF) { printf("%c",c); } printf("\nClosing file.\n"); fclose(fp); } } For this program, we enter the input data via the keyboard and the program writes it, character by character, to the file studentRecord. The end of the data is indicated by entering an EOF character, which is control+z in the reference system. The file is closed at this signal. The end of the data can also be indicated by entering any special characters such as $. while((c =getchar()) != ‘$’) 5 The file studentRecord is again reopened for reading. The program then reads its content character by character, and displays it on the screen. Reading is terminated when getc encounters the end-of-file marker EOF. Testing for the end-of-file condition is important. Any attempt to read past the end of file might either cause the program to terminate with an error or result in an infinite loop situation. Sample output: 11.6.2 Using getw and putw functions The getw and putw are integer-oriented functions are used to read and write integer values. These functions are useful when we deal with only integer data. The general form of getw and putw are: putw(integer, fp); getw(fp); 11.6.3 Using fprintf and fscanf functions These functions behave exactly like printf() and scanf() functions except that they operate with files. The first argument of these functions is a file pointer which specifies the file to be used. The general form of fprintf is: fprintf(fp,“control string”,list); fscanf(fp,“control string”,list); where fp is a file pointer associated with a file that has been opened for writing. The control string contains output specifications for the items in the list. The list may include variables, constants and strings. The general form of fscanf is: fscanf(fp,“control string”,list); This statement would cause the reading of the items in the list from the file specified by fp, according to the specifications contained in the control string. 6 Example: Program to read a string and an integer from the keyboard and write to a disk file called string. The program then reads the file and displays the information on the screen. main() { FILE *fp1; int num; char s[30]; fp1 = fopen("D:\string.txt","w"); printf("Enter a string and a number:"); //Read from keyboard fscanf(stdin,"%s %d",s,&num); fprintf(fp1,"%s %d",s,num); //write to file printf("\nClosing file.\n\n"); fclose(fp1); fp1 = fopen("D:\string.txt","r"); if(fp1 == NULL) { printf("Error: can't open file.\n"); } printf("File opened successfully. Now printing contents.\n"); fscanf(fp1,"%s %d",s,&num); fprintf(stdout,"%s %d",s,num); printf("\n\nClosing file.\n\n"); fclose(fp1); } Sample output: 11.6.4 Using feof() The feof function is used to test for an end of file condition. It takes a FILE pointer as its only argument and returns non zero integer value if all of the data from the specified file has been read, and returns zero otherwise. If fp is a pointer to the file that has just been opened for reading, then the statement if(feof(fp)) printf(“End of data.\n”); would display the message “End of data.” on reaching the end of file condition. 11.6.5 Random access to files So far we have discussed file functions that are useful for reading and writing data sequentially. There are occasions, however, when we are interested in accessing only a 7 particular part of a file and not in reading the other parts. This can be achieved with the help of the functions fseek, ftell, and rewind available in the I/O library. ftell() ftell function takes a file pointer and returns a number of type long that corresponds to the current position. It takes the following form: n =ftell(fp); where fp is a file pointer, and n would give the relative offset(in bytes)of the current position. This means that n bytes have already been read (or written). rewind() The rewind() function resets the position to the beginning of the file. That is, it “rewinds” the file. It takes the form: rewind(fp); n=ftell(fp); would assign 0 to n because the file position has been set to the start of the file by rewind. This function helps us in reading a file more than once, without having to close and open the file. fseek() The fseek()function is used to move the file position to a desired location within the file. It takes the following form: fseek(fileptr, offset, position); where fileptr is a pointer to the file concerned, offset is a number or variable of type long, and the position is an integer number. The offset specifies the number of positions (bytes) to be moved from the location specified by position. The position can take one of the following three values: Value 0 1 2 Meaning Beginning of file Current position End of file The offset may be positive, meaning move forwards, or negative, meaning move backwards. ***************************************************************************** Please bring all the errors to my notice either by sending an email ([email protected]/ [email protected]) or in person. I will appreciate your kind gesture! ***************************************************************************** 8
© Copyright 2024