0 valutazioniIl 0% ha trovato utile questo documento (0 voti)
33 visualizzazioni77 pagine
Variables are data objects which keep values in the memory. They are declared by using the following syntax. A variable must be declared before it is used.
Variables are data objects which keep values in the memory. They are declared by using the following syntax. A variable must be declared before it is used.
Variables are data objects which keep values in the memory. They are declared by using the following syntax. A variable must be declared before it is used.
1. Variable declaration Variables are data objects which keep values in the memory. A variable is declared by using the following syntax. dataType variableNameList; o Each variable must have a data type and have an unique name, i.e., you can not define different variables with the same name. o A variable must be declared before it is used. o Variables can only be declared at the beginning of any block. o In a single declaration statement, there can be more than one variable name which are separated by commas. But you can only define the variables of the same data type in a single declaration statement. o When you define a variable, its value is garbage (its value can not be predicted), but there are exceptions. But it is also possible to initialize the value of a variable when you define it. Note that initializer must be constant value. o For printf() and scanf() functions, the type of variable should match up with the corresponding conversion specifier, otherwise an undefined behavior occurs. o x 'x' "x" x: it is the name of a variable. 'x': it is a character x. "x": it is a string x. o 9 '9' "9" 9: it is the integer constant 9. '9': it is a character 9, and it has the value of 57 in ASCII code. "9": it is a string 9.
2. Rules for variable names These rules are also valid for identifier names, e.g., variable names, function names, symbolic constant names, etc. 1. They must consist of letters and digits (The underscore character is counted as a letter). The names of variables do not need to be meaningful. 2. The names of variables must begin letters. It is also possible to begin with the underscore character, but it is not recommended since library routines often use such names. 3. Do not forget that C is a case sensitive, i.e., x and X are different. 4. At least 31 characters of an internal name are significant (it depends on a compiler). 5. Keywords like int, main, float, return, long, unsigned, const, if, etc are reserved and can not be used as identifier names. Here are some valid identifier names: My_Name8 number_ _number CMPE fdjkfhdg34ffs x9875424 And some invalid identifier names are listed as follows: 8Name_4 4563 number+ data\% return 3. Constants They are values defined as constants, you can not change their values in the execution of the program. We can define constants by two different ways. 0. By using reserved word const. It must be defined in the declaration part, note that a memory location is allocated for the constant. const type name var name = constant value; const double PI = 3.14; 1. By using define. Actually it is a substitution and no memory place is allocated. #define PI 3.14 If you want to define PI as long double use #define PI 3.14L Note that we can use define for other purposes (look at subject macros). 4. Enumeration constants It is for defining a list of constant integer values, remember that character constant values are actually small integer values. It is alternative to multiple #define's. enum Month{JAN = 1, FEB, MAR}; enum BOOLEAN{no, yes}; enum my_numbers{three = 3, one, FOUR = 2, five}; enum specialCharacters{TAB = '\t', NEWLINE = '\n', BACKSLASH = '\\'}; o They behave like integers. o Unless it is specified, the values of enumeration constants begins with 0. The other unspecified ones continues from the last specified one. o Names in different enumerations must be distinct. o Values do not need to be distinct in the same or different enumeration. enum boolean{FALSE,TRUE}; int main (){ int a; a = FALSE; } enum boolean{FALSE,TRUE}; int main (){ enum boolean b; b = 9; /* it gives warning */ b = TRUE; } int main(){ enum boolean{FALSE,TRUE}; int a; enum boolean b; a = FALSE; b = 9; /* it gives warning */ b = TRUE; } 5. Assignment and type conversions When a narrower type is assigned to a wider one, the value is assigned without losing information. When a wider type is assigned to a narrower one, we may lose some information. int a; float b; char c; a=3.14; /* a = 3 */ b=8; /* b = 8.0 */ c='0'; /* c = '0' */ b=9.2; /* b = 9.2 */ a=b; /* a = 9 */ b=a; /* b = 9.0 */ a=c; /* a = 48, since ASCII code of '0' is 48 */ b=c; /* b = 48.0 */ c=49; /* c = '1', since ASCII code of '1' is 49 */ c=50.2; /* c = '2' */ 2. Introduction To C 2.1. Hello World Example 2.2. Summation Of Two Integers 2.3. Subtraction Multiplication Division Mod
2.1. Hello World Example A program that writes hello world on the screen: FILE:introduction_to_c__hello_world_example__hello_world.c
anykey(); return 0; } Exercise:Modify above program so it sums 3 integers 2.3. Subtraction Multiplication Division Mod A program that reads 2 numbers, then calculates and prints sum subtraction multiplication division mod of 2 numbers
SAVE (Ctrl- S)COMPIL E (Ctrl-F7) UNDO (Ctrl- Z)REDO (Ctrl-Y) { beautif y(); }LOAD DEFAULT CODE
1 2 3 4 5 6 7 8 9 10
#include<stdio.h>
int main(){
anykey(); return 0; } 3. Data Types 3.1. Integers 3.2. Floating Point Numbers 3.3. Characters
3.1. Integers We have already studied integer data type (int). For integer type modifiers (short, long, signed, unsigned) please refer to "Advanced Topics in C"section. 3.2. Floating Point Numbers Floating point numbers are the real numbers, i.e., the numbers with fractional parts (e.g.: 1.54). Just like integers being input and output using %d, floats are input and output using %f. Example: A program that reads two floats from the user and calculates the sum: FILE:data_types__floating_point_numbers__sum2float.c
printf("%d\n",Div); anykey(); return 0; } Another method for declaring a real number is using double, which can store real numbers with higher precision. %lf is used for reading and writing doubles. Example: A program that reads two doubles from the user and calculates the sum: FILE:data_types__floating_point_numbers__sum2double.c
printf("%c:%d\n",ch,ch); anykey(); CODE 12 13 return 0; } Example: A program that reads a character variable from the user, then prints the character read and its ASCII code (integer value corresponding to that character) on screen: FILE:data_types__characters__char.c
anykey(); return 0; } 4. Variables And Constants 4.1. Variable Declaration 4.2. Rules For Variable Names 4.3. Constants 4.4. Assignment And Type Conversions
4.1. Variable Declaration Below is an example of various variable declarations. Please note that you are only responsible for int, float, double, char data types. Other types are used just for reference.
These rules are also valid for identifier names, e.g., variable names, function names, symbolic constant names, etc. 1. They must consist of letters and digits (The underscore character '_' is considered as a letter). 2. The names of variables must begin with letters. It is also possible to begin with the underscore character, but it is not recommended since library routines often use such names. 3. Do not forget that C is a case sensitive language, i.e., x and X are different. 4. At least first 31 characters of an internal name are significant (it depends on a compiler). 5. Keywords like int, main, float, return, long, unsigned, const, if, etc. are reserved and can not be used as identifier names. The language does not require that variable names are meaningful, but it is STRONGLY recommended that you use meaningful names. Here are some valid identifier names: My_Name8 number_ _number CMPE fdjkfhdg34ffs x9875424 And some invalid identifier names are listed as follows: 8Name_4 4563 number+ data\% return
4.3. Constants They are identifiers defined as constants. You are not allowed to change their values during the execution of the program. For example: #define PI 3.14 Note that, by convention, only CAPITAL LETTERS and _ character are used within constant names.
Example: A program which reads the radius of a circle and then calculates circumference and area (utilizing a defined PI): FILE:variables_and_constants__constants__circle.c
printf("circ is: %.2f\nArea is: %.2f\n",circ,area);
anykey(); return 0; } For other constant types such as use of const directive or enum data type, please refer to "advanced C topics" section 4.4. Assignment And Type Conversions When a narrower type is assigned to a wider one, the value is assigned without losing information. When a wider type is assigned to a narrower one, you may lose some information.
int main(){ int a; float b; char c; a=3.14; /* a = 3 */ b=8; /* b = 8.0 */ c='0'; /* c = '0' */ printf("a:%d b:%f c:%c\n",a,b,c); anykey(); b=9.2; /* b = 9.2 */ printf("b:%f\n",b); anykey(); a=b; /* a = 9 */ printf("a:%d\n",a); anykey(); b=a; /* b = 9.0 */ printf("b:%f\n",b); anykey(); a=c; /* a = 48, since ASCII code of '0' is 48 */ printf("a:%d\n",a); anykey(); b=c; /* b = 48.0 */ printf("b:%f\n",b); anykey(); c=49; /* c = '1', since ASCII code of '1' is 49 */ printf("c:%c\n",c); anykey(); c=50.2; /* c = '2' */ printf("c:%c\n",c); anykey(); anykey(); return 0; } 5. Operators 5.1. Assignment Operator 5.2. Arithmetic Operators 5.3. Increment And Decrement Operators
5.1. Assignment Operator
x = a + y; x += y; // short notation for x = x + y; (does the same thing) x *= y + 1; // short notation for x = x * (y + 1); (does the same thing)
5.2. Arithmetic Operators
+, , *, / Can be applied to all data types (including character data type). % modulus operator. Can be applied only to integer class. / integer division (truncates any fraction part) if both operands are integers. When an operator has operands of different types, the narrower operand is converted to the wider one.
int main(){ int a; char b='A'; a='9'-'2'; /* a=7 */ printf("a:%d\n",a); anykey(); a='Z'-b; /* a=25 */ printf("a:%d\n",a); anykey(); b='c'+4; /* b='g' */ printf("b:%c\n",b); anykey(); return 0; } Casting: Explicit type conversion can be enforced. (type name) conversion Suppose you have two integers and you want to compute their average and the ratio of them. FILE:operators__arithmetic_operators__typeCasting.c
ratio = x/y; /* ratio = 1.0 */ printf("ratio:%f\n",ratio); anykey(); ratio = (float)(x/y); /* ratio = 1.0 */ printf("ratio:%f\n",ratio); anykey(); ratio = (float)x/y; /* ratio = 1.25, the value of x will not change */ printf("ratio:%f\n",ratio); anykey(); ratio = (x+0.0)/y; /* ratio = 1.25 */ printf("ratio:%f\n",ratio); anykey(); return 0; }
5.3. Increment And Decrement Operators
++ and -- are the increment and the decrement operators respectively. ++ adds 1 to its operand and - - subtracts 1 from its operand. They are used either prefix or postfix operators. n++; (First use the value of n, then increment the value of n by 1) n--; (First use the value of n, then decrement the value of n by 1) ++n; (First increment the value of n by 1, then use the value of n) --n; (First decrement the value of n by 1, then use the value of n)
int n = 5,x; ++n; /* n = 6 */ printf("n:%d\n",n); anykey(); x=++n; /* n = 7, x = 7 */ printf("x:%d n:%d\n",x,n); anykey(); x=--n; /* n = 6, x = 6 */ printf("x:%d n:%d\n",x,n); anykey(); return 0; 18 }
6. Control Structures 6.1. If Statement 6.2. If Else 6.3. Not Operator 6.4. Nested If 6.5. Switch
6.1. If Statement So far, a program consists of a number of statements that are executed in sequence (sequential statements), i.e., they are executed once in all conditions.
What if we want to execute some portion of the code based on a condition?
Below is a program, which reads the grade of the student. Then, if the grade of the student is greater than or equal to 60, it prints "You passed"
If the grade is not greater than or equal to 60, program does not print anything:
int main(){ int grade; printf("Enter your grade:\n"); scanf("%d",&grade);
anykey(); return 0; }
Primary comparison operators are: < :(less than) > :(greater than) <= :(less than or equal to: ) >= :(greater than or equal to: ) != :(not equal to: ) == :(equal to: important! writing = instead of == is a common mistake)
6.2. If Else Now suppose that, we also want to inform the student if (s)he failed (if grade < 60). Then, we use the optional else.
int main(){ int grade; printf("Enter your grade:\n"); scanf("%d",&grade);
anykey(); return 0; }
A program that reads 3 integers from the user. Then, it calculates and prints
the average (as a float) the maximum the minimum the median (the one in the middle) of these 3 integers: FILE:control_structures__if_else__avg_max_min_med.c
anykey(); return 0; } Book Solution: Help 6.3. Not Operator ! is used as a logical not operator
For example, if(!(x==3)){ printf("x is not equal to 3") } IMPORTANT: do not forget parenthesis! 6.4. Nested If The if-else structure is actually a single statement. Therefore, you can use it as one of the statements in the statement block. We use nested if's to encode multiway decisions. Suppose we want to classify the students into three groups according to their grades (above 60, between 50 and 60, and below 50) and display a message accordingly. FILE:control_structures__nested_if__nested_if.c
Exercise: How do you match else's with if's? if (n > 0) if (n > 0){ if (a > b) if (a > b) n = a; n = a; else } n = b; else else n = b; if (a < 0) b = 1; else if (b == 0) b = n;
Write a program that assigns the letter grade of the score as follows: 90-100 A, 80-89 B, 70-79 C, 60-69 D, and 0-59 F. FILE:control_structures__nested_if__letter_grade.c
Example: Write a program that computes and displays the real roots of the quadratic equation (ax 2 +bx+c=0) whose equations are taken from the user. The real roots are computes as the following equation: x 1,2 = -b b 2 -4ac 2a Let = b 2 -4ac. If <0, there are no real roots. If =0, there is only one real root. If >0, there are two real roots.
Example: Write a program that converts the uppercase letter to its lowercase and converts the lowercase letter to its uppercase acccording to the case of the letter. You must display a message, if the character is not a letter: FILE:control_structures__nested_if__upper_lower.c
Example: Take two decimal numbers and one character from the user (in the from of no1 character no2) and design a simple calculator. If the character is +, -, *, or /, your program will be perform the corresponding operation and display the results, otherwise your program will give a warning message to the user: FILE:control_structures__switch__calculator.c
anykey(); return 0; } Switch statement is a special case of if statement where there are multiple cases with == checks. ANYTHING CAN BE DONE WITH SWITCH CAN BE DONE WITH IF, however switch statement can shrink code dramatically for certain situations.
Enter at first suitable "case", leave at first "break": Check following code for switch-break behaviour: FILE:control_structures__switch__break.c
int main(){ int num; printf("Enter the number: "); scanf("%d",&num);
switch (num){ case 1: printf("one "); break; case 2: printf("two" ); case 3: printf("three "); case 4: printf("four "); break; case 5: printf("five "); case 6: printf("six "); default: printf("else"); }
anykey(); return 0; 21 } 7. Repetition Statements 7.1. While 7.2. For 7.3. Nested Loops 7.4. Break 7.5. Continue So far, we have seen sequential and control statements. But suppose the following problems: 1. Writing a program which prints the line numbers at the beginning of each line (suppose we have 100 lines). 2. Computing the average of the exam grades of 1000 students. 3. Taking a text from the user and converting its lowercase letters to their corresponding uppercases until a new line character is pressed. 4. Computing the factorial of the number which is given by the user. In cases 1 and 2, it is possible to write the programs by using sequential statements, however this is not practical. For cases 3 and 4, you cannot write the necessary codes by only using sequential statements since you do not know in advance how many times statements are to be executed, e.g., you cannot know when the user enters a new line character before the execution of the program. So we need structures to execute the statements repeatedly. A loop is a group of instructions that the compiler executes repeatedly while some conditions are satisfied. 7.1. While Suppose printing 100 lines with their line numbers: FILE:repetition_statements__while__100_lines.c
anykey(); return 0; } Compute the average of n numbers entered by the user. You must take the numbers until a negative value is entered: FILE:repetition_statements__while__average_until_negative.c
anykey(); return 0; } Example: Suppose the user enters the sequence of characters. Write a program that counts the lowercase letters in this sequence until * is met: FILE:repetition_statements__while__count_lowercase.c
Example (read integer char-by-char): You want to take a single integer from the user. Using %d for reading is not allowed: only %c is allowed. Input ends when a non-number character is entered. FILE:repetition_statements__while__read_int_char_by_char.c
anykey(); return 0; } Example: Compute the average of n numbers entered by the user where n is also specified by the user at the beginning of the program: FILE:repetition_statements__for__average_n.c
anykey(); return 0; } Example: Write a program that controls whether the number entered by the user is perfect or not. Perfect number is the positive number whose sum of its positive divisors except itself is equal to itself. For example 6 and 28 are perfect numbers (6 = 1 + 2 + 3): FILE:repetition_statements__for__is_perfect.c
7.3. Nested Loops We can have two or more loops inside each other (like nested if' s or switch' s). Example: Draw the isosceles triangle using *, where line number is also specified by the user. FILE:repetition_statements__nested_loops__triangle.c
anykey(); return 0; } Example: Write a program which reads the input as a sequence of characters terminated by '! '. Your program should detect the longest sequence of the lowercase letter repeated. Display which letter it is and the number of its occurrences.
SAVE (Ctrl- S)COMPIL E (Ctrl-F7) UNDO (Ctrl- Z)REDO (Ctrl-Y) { beautify
1 2 3 4 5 6 7 8 9 10
#include<stdio.h>
int main(){
anykey(); return 0; } (); }LOAD DEFAULT CODE
7.4. Break It provides an early exit from for and while just as switch. It causes the innermost enclosing loop or switch to be exited immediately. Example: Suppose the effect of the gravity on a free falling object. Write a program that displays the height of the object falling from a tower on the screen for every second until it hits the ground. You must also display a message when the object hits the ground (use break to exit the loop when you detect crash). The height of the tower h0 is specified by the user and the height h at time t is: h=h 0 -1/2gt 2 : FILE:repetition_statements__while__free_fall.c
7.5. Continue It causes the next iteration of the enclosing for, while, do-while to begin. It cannot be used with switch' s. For while and do-while loops, the test part is executed immediately. In for loops, the control passes to the modification step.
Suppose you have the following loops, and the input of the program is: 5 -4 3 2 -5 8 9 1.
While loop version: FILE:repetition_statements__continue__while.c
Why are functions necessary? 1. Real-world problems are much larger than the ones we examined so far. Thus we need to break up the problem into the smaller subproblems. Given a problem, we start its abstract formulation first and work down to more detailed subproblems. 2. Functions enables us to reuse the codes written before. For example there are standard functions defined in stdio.h (printf,scanf), in math.h (sqrt, pow, sin, cos), and we use them for our purposes. Or consider computing the permutation of two numbers, P(n,k)=n!/(n-k)!. In this example we must compute the factorial twice, i.e., we must write its code twice. Instead of that, just define a function for this purpose and use if it is necessary. So we have compact codes. We want to write a function: int square(int num) which calculates square of a given value: FILE:functions__square.c
anykey(); return 0; } Example: Write functions that compute the factorial f(n)=n!, permutation, P(n,k)=n!/(n-k)!, and the combinatorial, C(n,k)=n!/(n-k)! k!, of two numbers n and k, where n>0, k>0, and n k. FILE:functions__permutation_combination.c
8.1. Boolean Functions C does not support boolean functions directly, however, they can be simulated via integer functions.
Example:
1. Write a function: int isLower(char ch) Which returns 1 if ch is a lowercase letter, 0 otherwise. 2. Write another function: char toUpper(char ch) Which assumes that the given ch is a lowercase character, and returns its uppercase equivalent (e.g. if ch is 'b', your function must return 'B') 3. Write main function which reads a character from user, checks whether it is lowercase via isLower function you wrote above: if(isLower(ch)==1){ then if it is, prints its uppercase equivalent via toUpper function: printf("%c",toUpper(ch)); FILE:functions__boolean_functions__isLower_toUpper.c
8.2. Void Functions Not all functions must return a value. Consider the following function printing 2x2 square on screen: void printSquare() FILE:functions__void_functions__2x2_square.c Load Previous Saves/Versions:0-1-2-4-8-16-32-64-128-256- 512 Empty
anykey(); return 0; } 9. Pointers And Pointer Arithmetic 9.1. Pointer Operators A pointer is a variable that is capable to hold a memory address in it. Suppose the declaration of the integer pointer p: int *p; p keeps an address in it. At this address, an integer is stored since it is an integer pointer. In case of float pointer a, float *a; a keeps an address at which a floating number is held. Note that a pointer is a variable too, thus it is kept in some memory place, i.e., it has some memory address, which is not the same with its value. Every pointer points to a specific data type except void pointers. Pointer to void is used to hold any type of pointer. 9.1. Pointer Operators
1. & (address operator): It is a unary operator, and it gives the address of its operand. It can be applied to objects in memory, it cannot be applied to expressions, constants or register variables. Suppose the following statements: 2. int *p, c p = &c; In the first line we declare two variables, one is an integer, the other is an integer pointer. In the second line, we assign the address of c as the value of variable p. 3. * (indirection(dereferencing) operator): It is also a unary operator and can be applied to pointers. By this operator, we can access the variable whose address is kept in the pointer. You should only access to addresses which was previously allocated, otherwise you may have some problem. For example: 4. int *p; *p = 3; You define an integer pointer p, which has garbage value in it, so in its address there can be any address. And when you try to access to the address, which is the value of p, you may probably access an unallocated address. Example:
SAVE (Ctrl- S)COMPIL E (Ctrl-F7) UNDO (Ctrl- Z)REDO (Ctrl-Y) { beautif y(); }LOAD DEFAULT CODE
1 2 3 4 5 6 7 8 9 10 11
#include<stdio.h>
int main(){ int a = 3, b = 2, *ptr, *x, *y;
anykey(); return 0; }
Book Solution: Help 10. Pointers And Function Arguments Suppose we have a function, and we want to change two values in it and return them to the caller part. But only a single value can be returned and there is no direct way to alter a parameter in C programming language. The way to obtain this effect is to pass pointers to the functions. Suppose the example of swaping two values which are sent as the parameters.
This is an example where parameters are sent via call-by-value, thus does not work: FILE:pointers_and_function_arguments__swap_false.c
anykey(); return 0; } This is an example where parameters are sent via call-by-reference, thus works: FILE:pointers_and_function_arguments__swap_true.c
Example: Consider the program, which finds the real roots of a given quadratic equation. At this time, we want to rewrite it by using a function: int solve(float a,float b,float c,float *root1, float *root2) Your function will return the number of real roots (0 if no real roots, 1 if roots are equal, 2 if 2 distinct real roots)
int main(){ float avg = 0,i; int no; for (i = 0; i < N; i++){ scanf("%d",&no); avg += no; } avg /= N; printf("average:%f\n",avg); anykey(); return 0; }
We want to compute the variance of the numbers x i , where variance is
var=
N (x i -avg) 2
i=1 N-1 For this time, we need to keep x i values since first we need to compute the average value, then use this value in the variance computation. One way is to keep each number in distinc variables. For example if N=100, then we need to declare 100 variables. But it is not feasible. The next alternative way is to group these variables under the same name, where each of them has an unique index. An array is a group of memory locations. These locations are related by the fact that they have all the same name and same type. To refer (access) to a particular location, i.e., an element within the array, specify the name of the array and the position number (index) of the variable in square brackets.
anykey(); return 0; } CODE We must declare arrays before using them. int X[100]; float Y[90], Array[10]; float Y[90]; tells the compiler to reserve 90 elements for float array Y, i.e., to allocate 90 memory locations, each of which is capable of storing a float value. Arrays (their elements) occupy space in the memory, so you must specify the number of allocations, e.g., 90 for the array Y. You need constant expressions for these numbers. For example, the following definition is valid: #define b 3 const int mySize = 2; int y[a*b+2]; We can use its elements like variables, such as: float A[10]; A[3] = 2; /* assign 2 to the third element of the array A */ A[4] = A[3] * 2; /* get the value of the third element of the array A, multiply it by 2, then assign the result into the fourth element of the array A */ A[3]++; /* increment the value of the third element by 1 */ The index of the first element of the array is zero, i.e., arrays begin with 0 th element. And the index of the last element is N-1, where N is the size of the array. Indices can only be either an integer or an integer expression. int k[5], i; for (i = 0; i < 5; i++) k[i] = i * 2; k[2*2 - 1] = 3; k[k[4] / k[1]] = 2; k[1.5] = 3; /* error occurs */ Elements of the array can be considered as variables, however array name is not a variable. Suppose we define double NewArray[5];, we have variables NewArray[0], NewArray[1], etc. But there is no variable with the name of NewArray. Do not try to access the elements extending the size of an array. Suppose you define an array as int k[5];, then you should not try to access k[-45], k[-1], k[5], k[22], etc. Note that that is permitted, but you may have some problems if these are not allocated memory cells. It is also possible to initialize arrays at the declarations, give the values of elements in a comma separated list. int n[4] = {1,2,3,4}; int n[4] = {1,2,3,4,5}; /* too many values (greater than the size of the array), it gives compile time error */ int n[4] = {2,3}; /* fewer initializers, remaining elements are initialized by zero */ int n[] = {1,2,5,3,7,7,0}; /* it works, the compiler will create an array whose size equals to the number of the initializers, in this case with size 7 */ int n[]; /* it gives compile time error*/ Arrays are static structures, which means you set the size of the array in your code and cannot change its size anymore. In summary you should either specify the array size or initialize the array.
Example: Suppose you have 100 elements with integer data type. You want to control whether these numbers are symmetric or not. Consider elements are given in a sequence by the user.
anykey(); return 0; } Example: User enters a sequence of characters. You want to count the lowercase and the uppercase letters in this sequence. The sequence is finished when % is entered. You should ignore all other characters.
anykey(); return 0; } Arrays have one important drawback: They cannot be created or used dynamically. For example, consider the user enters a sequence of decimal numbers but we do not know the size of this sequence at the beginning of the program (suppose size is also given by the user or the numbers are taken until the user enters a sentinel value). We can solve this problem by defining very large arrays and hope the user not to enter the size such that it extends the limit. Suppose the following examples: FILE:arrays__size_limit.c
anykey(); return 0; } If user enters the size smaller than or equal to 100000, the above code works. If he enters the size 100500, the indices exceeds the ranges, so you may have some problems. Now suppose the user enters 5 as the size of the arry, then 99995 locations are allocated unnecessarily. Not that defining arrays with very large sizes is inefficient. So we need dynamic structures. For dynamic arrays, please refer to Advanced C Topics -> relationship between arrays and pointers dynamic memory allocation with malloc calloc and realloc
Example: Suppose you want to write a program that takes two polynomials from the user. Perform addition and multiplication operations on them: FILE:arrays__polynom_addition_multiplication.c
1. When an array name is passed to a function, what is passed is the address of the initial element of an array. 2. When a pointer is passed, the address value is passed. 3. Arrays and pointers can be used interchangebly in defining parameters of functions. We do not need to specify the size of the array in the parameter list unless it is a multidimensional array. 4. If an address parameter is passed to a function, you can modify the values pointed by this address and the modifications can be seen after the termination of the function. If you try to change the value of the parameter, it won' t be seen by the caller part. 5. Functions can also return to pointers, but not arrays! . Example: You have integers with size n. You want to change all elements which are equal to the given integer value with another given integer. Write a function that performs this operation, and returns to the number of the changed numbers FILE:passing_arrays_to_functions__change_array_values.c
void printArray(int arr[], int size){ int i; for(i=0;i<size;i++){ printf("%d ",arr[i]); } printf("n"); }
void swap(int *a, int *b){ int temp; temp = *a; *a = *b; *b = temp; }
int main(){ int array[MAX_SIZE], size, i; printf("Enter the size of the array:n"); scanf("%d",&size); if(size<=0 || size > MAX_SIZE){ printf("Error: invalid size. Size must be between 0 and %dn",MAX_SIZE); return -1; } srand(NULL);//initialize random seed for (i = 0; i < size; i++){ array[i] = rand()%10; } printf("array before sort:n"); printArray(array,size);
sort_array(array,size);
printf("array after sort:n"); printArray(array,size);
anykey(); return 0; }
12.2. Binary Search Example: Suppose we have a sorted array with size N. Write a function that finds a given number in this array, and returns the index of its location. If the number is not found, your function should return -1. Perform search by using binary search algorithm:
void printArray(int arr[], int size){ int i; for(i=0;i<size;i++){ printf("%d ",arr[i]); } printf("n"); }
void swap(int *a, int *b){ int temp; temp = *a; *a = *b; *b = temp; }
void sort_array(int arr[], int size){ int i, j; for (i = 0; i < size; i++) for (j = i + 1; j < size; j++) if (arr[i] > arr[j]) swap(&arr[i],&arr[j]); }
int main(){ int array[MAX_SIZE], size, i, searchedNumber,searchedNumberIndex; printf("Enter the size of the array:n"); scanf("%d",&size);
if(size<=0 || size > MAX_SIZE){ printf("Error: invalid size. Size must be between 1 and %dn",MAX_SIZE); anykey(); return -1; }
for (i = 0; i < size; i++){ array[i] = rand()%10; }
1. When an array name is passed to a function, what is passed is the address of the initial element of an array. 2. When a pointer is passed, the address value is passed. 3. Arrays and pointers can be used interchangebly in defining parameters of functions. We do not need to specify the size of the array in the parameter list unless it is a multidimensional array. 4. If an address parameter is passed to a function, you can modify the values pointed by this address and the modifications can be seen after the termination of the function. If you try to change the value of the parameter, it won' t be seen by the caller part. 5. Functions can also return to pointers, but not arrays! . Example: You have integers with size n. You want to change all elements which are equal to the given integer value with another given integer. Write a function that performs this operation, and returns to the number of the changed numbers FILE:passing_arrays_to_functions__change_array_values.c
void printArray(int arr[], int size){ int i; for(i=0;i<size;i++){ printf("%d ",arr[i]); } printf("n"); }
void swap(int *a, int *b){ int temp; temp = *a; *a = *b; *b = temp; }
int main(){ int array[MAX_SIZE], size, i; printf("Enter the size of the array:n"); scanf("%d",&size); if(size<=0 || size > MAX_SIZE){ printf("Error: invalid size. Size must be between 0 and %dn",MAX_SIZE); return -1; } srand(NULL);//initialize random seed for (i = 0; i < size; i++){ array[i] = rand()%10; 37 38 39 40 41 42 43 44 45 46 47 48 49 } printf("array before sort:n"); printArray(array,size);
sort_array(array,size);
printf("array after sort:n"); printArray(array,size);
anykey(); return 0; }
12.2. Binary Search Example: Suppose we have a sorted array with size N. Write a function that finds a given number in this array, and returns the index of its location. If the number is not found, your function should return -1. Perform search by using binary search algorithm:
void printArray(int arr[], int size){ int i; for(i=0;i<size;i++){ printf("%d ",arr[i]); } printf("n"); }
void swap(int *a, int *b){ int temp; temp = *a; *a = *b; *b = temp; }
void sort_array(int arr[], int size){ int i, j; for (i = 0; i < size; i++) for (j = i + 1; j < size; j++) if (arr[i] > arr[j]) swap(&arr[i],&arr[j]); }
if(searchedNumberIndex==-1){ printf("value not foundn"); } else{ printf("value found at index %dn",searchedNumberIndex); }
anykey(); return 0; } 14. Multidimensional Arrays We can define multidimensional arrays. In the two dimension, multidimensional arrays provide us to think of the memory locations as a table. Actually it is not possible to keep a table format for memory locations and two dimensional array is an one dimensioanl array, whose elements are sorted. In the declaration, we specify all dimensions of the multidimensional array. Suppose the following two dimensional array declarations: int a[3][4]; /* You can think of array a with 3 rows and 4 columns, but actually 12 consecutive locations are allocated for the elements of a */ int b[3][5] = {{0,1,2,3,4},{3}}; /* It is also possible to initialize the multidimensional arrays, all uninitialized locations are set to zero as in the single dimensional arrays */ int c[][5] = {{1,2,3,3,2},{3,4,4,3,2}}; /* First dimension is set to 2 in this case */ int d[][] = {{1,2},{3,4},{5,6}}; /* It is illegal, only the first dimension can be free, i.e., unspecified */ int e[3][4][5]; /* It is a three dimensional array, actually it consists of 60 consecutive int-sized locations*/ If a two dimensional array is to be passed to a function, what is passed is a pointer to an array of rows. The parameter declaration in the function must include the size of dimensions except that of the first one. Suppose the following function declarations and function calls. void func1(int a[][]){} /* it is invalid, you need to specify at least the second dimension*/ void func2(int a[3][4]){} /* valid */ void func3(int a[][4]){} /* valid */ ------------------------------------------------------- int a[3][7], b[5][4], c[3][4]; func3(a); /* will not cause a syntax error, */ /* but will do something completely different */ func3(b); /* valid */ func3(c); /* valid */ Example: Consider a block of 10 apartment houses, each of which contains 6 apartment flats. First, we want to keep the number of people in each flat, and then we want to compute the average number of people that live in each apartment house.
anykey(); return 0; } Example: Consider we have 5 classes, each of which has 30 students. These 30 students takes 10 course (all students in different classes take the same courses). 1. Write a function that takes grades of each student from the user. 2. Write a function that computes the average of 10 courses. 3. Write a function that assigns letter grades to the given grades of the students (90 - 100: A, 80 - 89: B, 70 - 79: C, 60 - 69: D, 0 - 59: F, and assume all grades are given between 0 and 100). FILE:multidimensional_arrays__grades.c
anykey(); return 0; } Remember arrays have a major drawback: They cannot be created and used dynamically. Please refer to advanced c topics section for dynamic multidimensional arrays.
Example: We have two two-dimensional matrices whose dimensions are 2x4. Write a function for computing the sum of these two matrices. Write another function to compute the transpose of a give matrix:
anykey(); return 0; } 15. Structures 15.1. Valid Operators 15.2. Array Of Structures And Structure Pointers 15.3. Typedef 15.4. Structure And Functions A structure is a collection of one or more variables, whose data types can be different, under a single name. They are derived data types. Example: Suppose you want to keep lines in your program. To this end, you want to create a line by taking its two endpoints. You also want to perform the swapping operations on some points and you want to calculate the length of a given line. FILE:structures__point_line.c
It is also possible to nest the structures as you can see above. Consider also the following examples: struct point{ int x; int y; }; struct line{ struct point p1; struct point p2; }; struct circle{ struct point center; float radius; };
15.1. Valid Operators
1. You can access the members of a structure. To access a member: structure_name.member_name For example: struct point{ float x; float y; }; struct point p, *p_ptr, p_arr[10];
p_ptr=&p; p.x = 4; (*p_ptr).y=5; p_ptr->y=5; // this is the same as above statement. *p_ptr.y=5; // illegal. You must use one of the above two
p_arr[2].x=12; p_arr[0].y=4.5; 2. It is valid to copy the structure variables of the same data type. The following copying operations are all valid. 3. struct point{ 4. int X, Y; 5. }; 6. struct line{ 7. struct point P1, P2; 8. }; 9. 10. struct line L, K; 11. struct point p1; 12. 13. L.P1.X = 3; 14. L.P1.Y = 4; 15. p1.X = 5; 16. p1.Y = 7; 17. L.P2 = p1; 18. K = L; L.P2 = K.P1; 19. Taking the address of a structure variable is valid
15.2. Array Of Structures And Structure Pointers As you recall from the point example above, struct pointer and array of structures is valid 15.3. Typedef p>It creates new data types. For example: typedef int length; makes the name of length a synonym for int. And both declarations of a variable with the integer data type are valid. int len1; length len2; But typedef is usually necessary for complicated types. struct polynom{ int degree; float coeff[100]; };
typedef struct polynom poly; /* from now on for declaring a polynom variable, you can both use: struct polynom p; or poly p; */
15.4. Structure And Functions
1. You pass the entire structures or members (call by value). 2. You pass the address of the structure (call by reference). 3. Your function can return a structure or pointer to structure. 4. Structure arrays are possible Example: You are going to write a program which reads name, surname and age of a number of employees. Your program sorts employees in ascending order according to their age and prints them. 1. Define struct employee consisting of name (string), surname (string) and age (int) 2. Use typedef to define type Employee as struct employee 3. Write functions: 1. Employee readEmployee(): reads an employee from the user and returns it 2. void printEmployee(Employee e): prints the given employee on screen 3. void sortEmployees(Employee employees[], int numEmployees): sorts given employees 4. void swapEmployees(Employee *a, Employee *b): swaps employees (used within sort function) 5. main function: defines Employee employees[50] array, reads number of employees, reads employees to array one by one via utilizing readEmployee function, calls sortEmployees function and prints them one by one via printEmployee function. 6. after finishing this code, write a second version of readEmployee: void readEmployee(Employee *eptr) which takes an employee pointer and reads its parameters from the user (as (*eptr).age, you can use eptr->age) FILE:structures__structure_and_functions__employees.c
anykey(); return 0; } 16. Advanced Topics In C 16.1. Additional Variable Types 16.2. Macro Substitution 16.3. Additional Constants 16.4. Bitwise Operators 16.5. If Integers As Booleans 16.6. If Advanced Not Operator 16.7. For Infinite Loop 16.8. For Double Counter 16.9. Do While 16.10. Function Scopes 16.11. Pointer Addition Subtraction 16.12. Pointers And Dynamic Arrays Malloc Calloc Realloc 16.13. Advanced String Functions 16.14. Array Of Pointers 16.15. Pointers To Pointers 16.16. When Do You Use Multidimensional Arrays Array Of Pointers And Pointers Of Pointers 16.17. File Operations
16.1. Additional Variable Types for further reference: http://en.wikipedia.org/wiki/Scanf FILE:advanced_c_topics__additional_variable_types.c
short s; int i; long l; unsigned int ui; double d; long double lod;
printf("Enter a short integer between -32,768 -> +32,767\n"); scanf("%d",&s); printf("short: %d\n",s);
printf("Enter an integer between -2,147,483,648 -> +2,147,483,647\n"); scanf("%d",&i); printf("int: %d\n",i); printf("int octal: %o\n",i); printf("int hexadecimal: %x\n",i);
printf("Enter a long integer between -2,147,483,648 -> +2,147,483,647\n"); scanf("%ld",&l); printf("long: %ld\n",l);
printf("Enter an unsigned integer between 0 -> +4,294,967,295\n"); scanf("%u",&ui); printf("unsigned int: %u\n",ui);
printf("Enter a double (float with bigger range) between 1.7*10^-308 to 1.7*10^308 or -1.7*10^-308 to - 1.7*10^308 (cannot give exact 0 similar to float)\n"); scanf("%lf",&d); printf("double: %lf\n",d);
printf("Enter a long double (float with even bigger range than double) 3.4*10^-4932 to 3.4*10^4932 or - 3.4*10^-4932 to -3.4*10^4932 (cannot give exact 0 similar to float and double)\n"); scanf("%Lf",&lod); printf("long double: %Lf\n",lod);
anykey(); return 0; }
16.2. Macro Substitution Remember #define PI 3.14 in the compile time all PI' s are substituted with 3.14. We can use it to replace a token by an arbitrary sequence of characters. We call them macro and the general format is as follows: #define name replacement_text where name has the form of the identifier name. It is also possible to define macros with arguments. To compute the square of a given number, you can define a macro: #define square(x) (x)*(x) In this example parentheses are important to prevent the misinterpretations. In your program you use this macro as: a = square(3); b = square(a + 3 * b); Or you can define a maximum operation, which finds the maximum of given two numbers as: #define max(A,B) (((A) > (B)) ? (A) : (B)) In your program, it can be used as z=max(p+q,r+s); Do not forget that it is just a text substitution, which is performed at the compile time. 16.3. Additional Constants
int main(){ int a, b, c; a = 10; /* a = 1010*/ b = 86; /* b = 1010110*/
c = a & b; /*c = 0000010 = 2*/ printf("c:%d\n",c); c = a | b; /*c = 1011110 = 94*/ printf("c:%d\n",c); c = a ^ b; /*c = 1011100 = 92*/ printf("c:%d\n",c); c = b << 1; /*c = 10101100 = 172, it is same as b*2*/ printf("c:%d\n",c); c = b << 2; /*c = 101011000 = 344, it is same as b*2^2*/ printf("c:%d\n",c); c = b >> 1; /*c = 101011 = 43, it is same as b/2*/ printf("c:%d\n",c); 25 26 27 28 c = b >> 2; /*c = 10101 = 21, it is same as b/2^2*/ printf("c:%d\n",c); c = ~b; /*c = -87*/ printf("c:%d\n",c);
anykey(); return 0; }
16.5. If Integers As Booleans A boolean variable is a variable which takes logic values, namely true or false. However, C language does not have boolean variable support.
C language uses integers as boolean variables. An integer with value 0 is assumed to be false, and an integer with any value except 0 is assumed to be true
check the following code: FILE:control_structures__integers_as_booleans__boolfun.c
int main(){ int x; if(1){ printf("This text will always be printed (1)n"); } if(0){ printf("This text will never be printed (0)n"); } if(5){ printf("This text will always be printed (5)n"); } if(-45){ printf("This text will always be printed (- 45)n"); } x=0; if(x){ printf("This text will not be printed (x=0)n"); } x=1; if(x){ printf("This text will be printed (x=1)n"); } printf("Enter x:n"); 32 33 34 35 36 scanf("%d",&x);
if(x){ printf("This text will be printed if x is NOT 0, x:%dn",x); }
anykey(); return 0; }
In other words, if(x!=0){ printf("x is not zeron"); } or if(x){ printf("x is not zeron"); } are exactly the same. 16.6. If Advanced Not Operator Not operator can be used with the boolean mechanism: if(!x){ printf("x is zero"); } Some additional tricks: Guess what the following program will print: FILE:control_structures__not_operator__fun.c
16.7. For Infinite Loop In the for loop, all expressions are optional, but semicolon must be used. But the following loop causes an infinite loop which means the loop is executed forever (avoid infinite loops): FILE:repetition_statements__for__infinite_loop.c
for (i = 1, j = 100; i <= 100; i++, j--){ printf("i:%d j:%dn",i,j); }
anykey(); return 0; }
16.9. Do While While and for loops test the termination condition at the top, whereas do-while tests it at the bottom after a pass through the loop body. The body of the do-while loop is always executed at least once (break statement works for do-while also). Example: Write a program that computes the area of the square. At the end of each computation, your program should ask the user whether he wants to continue or not, and exits if the user presses q/Q key. Otherwise it continues to process. FILE:repetition_statements__do_while__square_area.c
A variable has a scope such that this variable is only defined in this scope. There are three different scopes: 1. Block Scope: Any block contain variable declarations, which must be defined at the beginning of a block. You can use these variables only in the block, where they are defined. Some examples: 2. Function Scope: The variables defined at the beginning of a function has function scope. These variables are called local variables. They can be used anywhere in the function where they appear, and cnnot be used outside the function body. 3. File Scope: The variables declared outside any function has file scope. They can be used in all functions from the point at which the variable is declared until the end of file. These are called global variables. But unless it is necessary, avoid using global variables in your program. 4. double G; /* G is the global variable */ 5. int function1(){ 6. int a; /* a is the local variable */ 7. if (a < 0){ 8. float b; /* b has block scope, and can be used only in this if */ 9. } 10. for (a = 1; a < 100; a++){ 11. char c; /* c has block scope, and can be used only in this for */ 12. } } If a variable is used in a code, it is searched as the following order: 1) In its block, 2) Among the local variables, 3) Among the global variables. Note that variables with block or function scope have garbage value when they are declared, unless they are initialized. On the other hand, global variables are initialized with zero in the absence of initializations. Example: What is the output of the program?
first(float a, float b, int ch){ printf("first #1 a = %f, b = %f, c = %f, ch = %dn",(float)a,b,c,ch); a = b + ch; c--; return (a + b--); } float second(int a, float c){ int ch = 2; printf("second #1 a = %f, b = %d, c = %f, ch = %dn",(float)a,b,c,ch); b = first(b,c,ch); printf("second #2 a = %f, b = %d, c = %f, ch = %dn",(float)a,b,c,ch); return (b * c); } main(){ float b = 5.99, ch = 1.2; printf("main #1 a = %f, b = %f, c = %f, ch = %dn",(float)a,b,c,(int)ch); a = first(ch,c,b); printf("main #2 a = %f, b = %f, c = %f, ch = %dn",(float)a,b,c,(int)ch); ch = second(c,b); printf("main #3 a = %f, b = %f, c = %f, ch = %dn",(float)a,b,c,(int)ch); return 0; }
/* Output is: main #1 a = 5.00, b = 5.99, c = 10.00, ch = 1 first #1 a = 1.20, b = 10.00, c = 10.00, ch = 5 main #2 a = 25.00, b = 5.99, c = 9.00, ch = 1 second #1 a = 9.00, b = 3, c = 5.99, ch = 2 first #1 a = 3.00, b = 5.99, c = 9.00, ch = 2 second #2 a = 9.00, b = 13, c = 5.99, ch = 2 main #3 a = 25.00, b = 5.99, c = 8.00, ch = 77 */
16.11. Pointer Addition Subtraction We can also use addition and subtraction operator with pointers.
SAVE (Ctrl- S)COMPI LE (Ctrl-F7) UNDO (Ctrl- Z)REDO (Ctrl-Y) { beautif y(); }LOAD DEFAULT CODE
1 2 3 4 5 6 7 8 9 10 11
#include<stdio.h>
int main(){ int a = 3, *x, *y;
anykey(); return 0; } Book Solution: Help 16.12. Pointers And Dynamic Arrays Malloc Calloc Realloc
When we declare int a[10];, a block consists of 10 consecutive places, each is capable of storing an integer, is allocated. After this declaration, we cannot change the size of the allocated block, i.e. we can neither expand the block nor shrink it. Our aim is to allocate a block in the memory such that its size can be changed in the program execution. To this end, let us first examine the relationship between pointers and arrays and their notations. Suppose the following declarations: int myArray[10]; int *myPointer; We know that myArray is the name of the 10 individual elements, it is NOT a variable. Actually myArray (the name of an array) is a synonym for the address of the initial element of an array, &a[0]. We know that myPointer is a variable that is capable of storing an address in it. Thus, if we want to store the address of the first element of myArray into myPointer, the following two statements are equivalent: myPointer = &myArray[0]; myPointer = myArray; So you can use the notations of pointers and arrays interchangebly. The following usages are equivalent: myArray[i] *(myArray+i) myArray+i &myArray[i] myPointer[i] *(myPointer+i) myPointer+i &myPointer[i] Please remember their differences again. A pointer is a variable, i.e., it is kept in some memory locations. Thus myPointer=myArray or myPointer++ are legal statements. But array name is not a variable and myArray=myPointer or myArray++ are ILLEGAL statements. And also remember that we should not access the memory locations that are not allocated before. This rule is valid for both arrays and pointers. Exercise: Trace the following code and observe the consequence of each statement.
int main(){ int a[] = {2, 20, 3, 1, -1, -6}, *p, t; p = &a[0]; /* In p is the address of the initial element of a */ printf("p:%d a:%d &a[0]:%d a:%d\n",p,a,&a[0],a); t = *p; /* t becomes 2 */ printf("t:%d\n",t); t = *(p + 1); /* t becomes 20 */ printf("t:%d\n",t); p = p + 3; /* In p is the address of the third element of a*/ printf("p:%d\n",p); t = *p; /* t becomes 1 */ printf("t:%d\n",t); t = a[2]; /* t becomes 3 */ printf("t:%d\n",t); t = *(a + 2); /* t becomes 3 */ printf("t:%d\n",t); p = a; /* In p is the address of the initial element of a */ printf("p:%d\n",p); p++; /* Increments the value of p */ printf("p:%d\n",p); t = *p; /* t becomes 20 */ printf("t:%d\n",t); t = p[2]; /* t becomes 1 */ printf("t:%d\n",t); t = p[-1]; /* t becomes 2 (t can be 3 at other compilers, which convert negative indexes to its positive counterpart) */ printf("t:%d\n",t); a = p; /* illegal, since a is not the variable, comment out this line for successful compile*/ a++; /* illegal, since a is not the variable, comment out this line for successful compile*/ anykey(); return 0; } 2 3 2 4 2 5 2 6 2 7 2 8 2 9 3 0 3 1 3 2 3 3 3 4 So we have seen that we can use arrays and pointers interchangebly. Remember our problem, we want to change the size of the allocated block during the execution of our program. In the declaration of an array, an appropriate memory block is allocated implicitly and we cannot change its size. It is possible to change the size of an allocated block only we perform our allocations explicitly. There are three types of allocation functions defined in stdlib.h. 1. malloc: It allocates a block of bytes from memory. The size of the allocated block is given as the argument to the function. On success, it returns the beginning address of the newly allocated block. It returns NULL on error, e.g., if there is not enough memory. 2. int *a; a = (int *)malloc(8 * sizeof(int)); In this example, the size of the block is 8*sizeof(int), which means we allocate a block in which we can keep 8 integers. Note that the size of the integer or any data type varies from computer to computer. sizeof(int) returns the size of the specified argument in bytes, in our example it returns how much byte is necessary to keep an integer in the computer which our program runs. The result of this function may be different for different computers. There are other malloc examples: double *numbers, char *letters; numbers = (double *)malloc(348 * sizeof(double)); letters = (char *)malloc(100 * sizeof(char)); 3. calloc: It allocates a memory block like malloc, the difference is that calloc sets the memory to zero. The syntax of calloc is illustrated in the following example: 4. long *elements; elements = (long *)calloc(348, sizeof(long)); 5. realloc: It adjusts the size of the allocated block to its new size, copying the contents to a new location, if necessary. On success, it returns the address of the beginning of the reallocated block, which might be different of the original block. On failure, realloc function returns NULL. vector = (long *)realloc(vector, 4 * sizeof(long)); It adjusts the size of vector to 4 and returns the beginning address of a newly allocated block. Realloc is applied to the blocks either previously obtained by malloc/calloc/realloc or set to NULL. Otherwise undefined behaviors may occur. Example: Write a program that takes the decimal numbers from the user and computes their average. The number of decimal numbers is specified by the user at the beginning of the program. In this example, we do not know the size of the numbers when we write our program. Therefore we allocate space for the numbers in the execution, which requires explicit allocation.
int main(){ int size, i; float *elements, avg; printf("Enter the amount of the numbers:"); scanf("%d",&size); if (size <= 0){ printf("The size must be positive\n"); exit(1); } elements = (float *)malloc(size * sizeof(float)); if (elements == NULL){ printf("Not enough memory space\n"); exit(1); } for (i = 0; i < size; i++) scanf("%f",&elements[i]); avg = 0.0; for (i = 0; i < size; i++) avg += elements[i]; avg /= size; free(elements); anykey(); return 0; } We know that variables are deallocated, i.e., freed, when we exit their scopes. In this example variables size, i, elements adn avg are deallocated at the end of the program. Since we allocate the memory block explicitly, we should also perform the deallocation of this block explicitly. free function deallocates a memory block allocated by a previous allocation function, i.e., malloc, calloc or realloc. If you try to free the memory block that was not allocated before, undefined behavior occurs. Example: Write a program that takes the decimal numbers from the user until a negative value is entered and computes their average. In this example, we do not know the size of the necessary block neither before the executin nor during the execution. We can only know the exact size when a negative value is entered. Therefore in this example, we assume the size of the block is 0 at the beginning of the program, then we expand the block incremently. So we use realloc function.
int main(){ int counter = 0, i; float *elements, avg, temp; elements = NULL; /* This is necessary to use the realloc function in a safe way later */ scanf("%f",&temp); while (temp >= 0){ counter++; elements = (float *)realloc(elements,counter * sizeof(float)); if (elements == NULL){ printf("Error in the allocation\n"); exit(1); } elements[counter - 1] = temp; scanf("%f",&temp); } if (counter == 0) printf("There is no number\n"); else{ for (i = 0; i < counter; i++) avg += elements[i]; avg / = counter; } free(elements);
anykey(); return 0; }
Do not forget that, crashes are related overflowing an allocated chunk, using realloc function without initializing its address argument or freeing the same pointer twice. Also accessing the unallocated memory place may result in crashes. Consider the following statements and their validity. #include <stdlib.h> int main(){ int *a = NULL, *b, *c, *d, *e; a = (int *)realloc(a, 3*sizeof(int)); /* (1) */ b = (int *)realloc(b, 3*sizeof(int)); /* (2) */ c = (int *)realloc(a, 3*sizeof(int)); /* (3) */ d = (int *)malloc(3*sizeof(int)); /* (4) */ d = (int *)realloc(d, 8*sizeof(int)); /* (5) */ d = (int *)realloc(d, 1*sizeof(int)); /* (6) */ free(c); /* (7) */ free(a); /* (8) */ free(a); /* (9) */ free(e); /* (10) */ d = (int *)malloc(50*sizeof(int)); /* (11) */ free(d); /* (12) */ d = (int *)malloc(50*sizeof(int)); /* (13) */ free(d); /* (14) */ return 0; } Let us examine each case. (1): it is safe, since a was initialized by NULL in the declaration (2): it results in crashes. Variable b was neither initialized by NULL nor allocated previously (3): it is safe since variable a was allocated previosly, in (1). Be careful of this statement, the variable a keeps the address, which indicates the block to be expanded. On the other hand, the variable c just keeps the return value of the realloc function. (4): it is safe, d points to an allocated block of 3 integers after this statement. (5): it makes the size of the allocated block indicated by d eight. And it is safe since variable d was allocated by malloc in (4) previously. (6): it makes the size of the allocated block indicated by d one. And it is safe since variable d was allocated by realloc in (5) previously. (7): it is safe, since c was allocated before, in (3). (8): it is safe, since a was allocated before, in (1). (9): it is not safe, since a was freed in (8), and has not been allocated again. So do not try to deallocate the unallocated memory locations. (10): it is not safe, since variable e was not allocated before. (11): it is safe, it is just an allocation. But the drawback of this allocation is that: we allocate some space and we keep the starting address of this space in the variable d. When we allocate this variable again, it begins to keep the address of the newly allocated space, and we cannot access the space allocated previously any more. (12): it is safe, since d keeps the adress of the allocated memory location, where allocation was done in (11). (13): it is safe, it is just an allocation. (14): it is safe, since we allocate the variable d in (13). In general, a pointer can be initialized as: 1. by using NULL. 2. int *a; long *b = NULL; a = NULL; 3. by using the expressions involving the addresses of previously defined data of the appropriate type. 4. double *a, b[100], c; 5. a = &c; a = b + 2; 6. by using allocation functions, malloc, realloc or calloc. The valid pointer operations are summarized as follows: 1. Assignment of addresses or pointers of the same type. 2. Adding or subtracting a pointer and an integer. 3. Subtracting two pointers of the same data type. 4. float *a, *b, c[10], t; 5. a = c; b = c + 3; t = b - a; /* in this example, t becomes 3 */ 6. Comparing two pointers. Suppose we have pointers p and q. p<q gives true if p keeps an address smaller than that of kept in q. In the above example a<b gives true and a>b gives false. It is also possible to use <=,>=,!=,== with pointers.
16.13. Advanced String Functions char *strtok(char *S1, char *S2): scans S1 for the first token not contained in S2. In the firts call, it returns a poinetr to the first character of the first token in S1 and writes a null character into S1 immediately following the returned token. Be careful in using this function, since the string S1 is changed after calling this function. FILE:advanced_c_topics__strtok.c
void f3( char* s1, char* s2 ){ while (*s1) s1++; while (*s1++ = *s2++); }
int main(){
anykey(); return 0; }
16.14. Array Of Pointers
Suppose there are 100 students in the class and we want to keep their names. So we can define a two-dimensional character array with dimensions 100 MAX, where MAX is the maximum length of a name possible. So the declaration is as follows providing a constant MAX was defined before. char names[100][MAX]; In this case there is a waste of space since the length of the names vary. So instead of keeping each name in an array with size MAX, we can allocate a memory block with an appropriate size and keeps the starting address of this block in a pointer. Thus we have an array with size 100 and in each of its elements an address is kept. The declaration of this array is as follows: char *names[100]; This array can be considered as a two-dimensional array whose first dimension is fixed and second dimension is free. And for the second dimension MEMORY ALLOCATION must be done! Suppose we take the names from the user and store them in names array. FILE:advanced_c_topics__array_of_pointers__read_names.c
// we must deallocate the memory: for(i=0; i<NUM_NAMES;i++){ free(names[i]); }
anykey(); return 0; }
Note that we can also use calloc or realloc function in the memory allocation. names[4] = (char *)calloc(40, sizeof(char)); names[57] = (char *)realloc(names[57],50 * sizeof(char)); Example: Suppose we want to keep our payments for each month. 1. Write a function that takes the payments from the user. For each month, the number of payments is read first. 2. Write a function that computes and returns the total payment for each month. FILE:advanced_c_topics__array_of_pointers__payments.c
int *readPayments(int *payments[NUM_MONTHS]){ int *numberOfPayments = (int*)malloc(NUM_MONTHS * sizeof(int)), i, j, temp; for (i = 0; i < NUM_MONTHS; i++){ printf("Enter the number of payments of month %d: ",i+1); scanf("%d",&temp); if (temp < 0){ printf("The minimum number of payment can be zero\n"); temp = 0; } numberOfPayments[i] = temp; if (numberOfPayments[i] != 0){
payments[i] = (int *)malloc(numberOfPayments[i] * sizeof(int)); for (j = 0; j < numberOfPayments[i]; j++){ printf("Enter payment %d of month %d: ",j+1,i+1); scanf("%ld",&payments[i][j]); } } } return numberOfPayments; } int *computeTotalPayments(int *payments[NUM_MONTHS], int *no){ int *total = (int *)malloc(NUM_MONTHS * sizeof(int)); int i, j; for (i = 0; i < NUM_MONTHS; i++){ total[i] = 0; for (j = 0; j < no[i]; j++) total[i] += payments[i][j]; } return total; 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 }
int main(){ int *payments[NUM_MONTHS],*numberOfPayments,*totalPayments,i; numberOfPayments = readPayments(payments); totalPayments = computeTotalPayments(payments,numberOfPayments);
printf("number of payments each month:\n"); printArray(numberOfPayments,12);
printf("total payments each month:\n"); printArray(totalPayments,12);
anykey(); return 0; } Note that it is also possible to define pointers to arrays. In this case, the second dimension is fixed and the first dimension is free. The declaration, allocation and deallocation of pointers to arrays with type long are follows: long (*powers)[10]; powers = (*(long)[10])malloc(8 * sizeof(long[10])); powers = (*(long)[10])realloc(powers,4 * sizeof(long[10])); free(powers);
16.15. Pointers To Pointers
Suppose we want to write matrix multiplication, remember we defined a matrix as a two- dimensional array last week. But we do not want to restrict ourselves for fixed dimensions, i.e., we want the first and the second dimensions to be free. Thus we should allocate memory blocks for both dimensions explicitly. In the same manner the deallocation of both dimensions is our responsibility. Suppose the following declaration: char **myPointer; myPointer is a variable that keeps a single value in it. This value is an address value, let us call this address M1. When we access the memory location with address M1, we expect to get another address value, say M2. And when we access the memory location with address M2, we expect to obtain a value with a character data type. Note that if we want to create a two-dimensional structure in a function and to return it at the end of the function, we can only use pointers to pointers as in the allocation, multiplication, etc. functions in the following examples. Example: Perform the following operations on matrices with varied row and column size. 1. Allocate necessary space for a matrix with the specified dimensions. 2. Deallocate the given matrix 3. Print the contents of the matrix in a tabular format. 4. Add two matrices and return the resultant matrix. 5. Multiply two matrices return the resultant matrix. 6. Find the transpose of a given matrix. 7. Generate random elements for a given matrix. Use rand function to generate the integers between 0 and 99, and assign these values to a matrix. 8. Use all these functions in a main function. 9. Run these codes and see the results as an exercise.
int **allocate_matrix(int row, int column){ int **temp, i; temp = (int **)malloc(row * sizeof(int *)); for (i = 0; i < row; i++) temp[i] = (int *)malloc(column * sizeof(int)); return temp; } void free_matrix(int **M, int row){ int i; for (i = 0; i < row; i++) free(M[i]); free(M); } void print_matrix(int **M, int row, int column){ int i, j; for (i = 0; i < row; i++){ for (j = 0; j < column; j++) printf("%d\t",M[i][j]); printf("\n"); } printf("\n"); } 4 1 5 1 6 1 7 1 8 1 9 2 0 2 1 2 2 2 3 2 4 2 5 2 6 2 7 2 8 2 9 3 0 3 1 3 2 3 3 3 4 3 5 3 6 3 7 3 8 3 9 4 0 4 1 4 int **matrix_addition(int **M1, int **M2, int row, int column){ int i, j, **temp = allocate_matrix(row,column); for (i = 0; i < row; i++) for (j = 0; j < column; j++) temp[i][j] = M1[i][j] + M2[i][j]; return temp; } int **matrix_multiplication(int **M1, int **M2, int row1, int column1, int column2){ int **temp = allocate_matrix(row1,column2), i, j, k; for (i = 0; i < row1; i++) for (j = 0; j < column2; j++){ temp[i][j] = 0.0; for (k = 0; k < column1; k++) temp[i][j] += M1[i][k] * M2[k][j]; } return temp; } int **matrix_transpose(int **M, int row, int column){ int i, j; int **temp = allocate_matrix(column,row); for (i = 0; i < row; i++) for (j = 0; j < column; j++) temp[j][i] = M[i][j]; return temp; } void random_matrix(int **M, int row, int column){ int i, j; for (i = 0; i < row; i++) for (j = 0; j < column; j++) M[i][j] = rand() % 100; } int main(){ int **M1, **M2, **result;
16.16. When Do You Use Multidimensional Arrays Array Of Pointers And Pointers Of Pointers
1. Suppose the following problem. We want to keep the distances between the cities of Turkey. We know there are 81 cities, therefore we can keep them in a static structure, i.e., by using two-dimensional array. float distances[81][81]; In this declaration, allocation is done implicitly and after exiting the scope of this array deallocation will be performed implicitly too. You do not need to allocate and deallocate a memory block for this array. 2. Suppose we want to keep the distances from a city to its towns for each city in Turkey. We know that there are 81 cities in Turkey, it is fixed at this moment. On the other hand different cities may have different number of towns, thus the number of towns is not fixed. In this example we can keep 81 address information in an array, where each of address indicates the starting point of a memory block for the corresponding city. In a memory block, we keep the distances from the city to its towns. float *distances[81]; In this declaration, the allocation of the first dimension is done implicitly. However if we need, we must allocate the space for the second dimension explicitly by using any allocation functions. In the same manner when we exit from the scope of distances, the first dimension will be allocated automatically but we must free the second dimension. /* code for allocation */ for (i = 0; i < 81; i++){ printf("Enter the number of towns of city %d: ", i); scanf("%d",&n); distance[i] = (float *)malloc(n * sizeof(float)); } /* code for deallocation */ for (i = 0; i < 81; i++) free(distance[i]); 3. Suppose user enters some city numbers. And we only want to keep the distances between these cities and their towns. In this case we know neither the number of cities nor the number of towns, i.e., both dimensions are free. float **distances; In this declaration only a pointer that is capable of keeping an address is defined. We keep an address in this variable. This address will be an address of an address of a floating point number. We must allocate spaces for each dimension and free them explicitly. 4. /* code for allocation */ 5. printf("Enter the number of cities:"); 6. scanf("%d",&no); 7. distance = (float **)malloc(no * sizeof(float)); 8. for (i = 0; i < no; i++){ 9. printf("Enter the number of towns of city %d:",i); 10. scanf("%d",&noTown); 11. distance[i] = (float *)malloc(noTown * sizeof(float)); 12. } 13. 14. /* code for deallocation */ 15. for (i = 0; i < no; i++) 16. free(distance[i]); free(distance);
16.17. File Operations
So far, we all read data from the standard input and write data on the standard output. These operations are automatically defined for a program by the operating system. Moreover, your program can also access a file that has already connected to the program. There is a structure that contains information about the file (location of a buffer, current position in the buffer, read/write flags, etc.). We have a file pointer points to this structure. The declaration of a file is given as follows: FILE *id; The FILE * data type is defined in stdio.h. Before you access a file, you should connect to it by fopenfunction. You must specify the filename (with its path) and the mode of the operation in this function. Mode can be write(w), read(r), append(a), etc. For example if you open the file with read mode and try to write some data into it, you may have problem. The following example connects the file with name data.txt for the write operation. If there is some error, e.g. if a file with the specified name is not found, fopen function returns to NULL. FILE *fid; fid = fopen("data.txt","w"); Some functions defined on the files are given as follows. You can find detailed information or more functions in the stdio.h library or any reference book. Note that before performing these operations, you must connect the corresponding file, i.e., you must open it. 1. fprintf: to print the formatted data into a file which has already opened. fprintf(fid,"%dHello world\n%f\t%s\n",12,data,str); 2. fscanf: to read data from the opened file. fscanf(fid,"%d%c",&myInt, &myChar); 3. fgets: to read characters from stream into the string s. It stops when it reads either n-1 characters or a newline character, whichever comes first. On success, it returns the string pointed to s. On error or end-of-file, it returns NULL. char *fgets(char *s, int n, FILE *stream); 4. fputs: It copies the null terminated string s to the given output stream. It does not append a newline character and the terminating null character is not copied. On success, it returns the last character written. On error, it returns EOF. 5. EOF: It is a constant indicating that end-of-file has been reached on a file. We should break the connection between the file pointer and its external name, freeing the file pointer by using fclose function. fclose(fid); There are limitations on the number of files that may open in a program. So close them if they are no longer needed. After the program is terminated, fclose is called automatically for each open file. Do not try to close a file that was not opened. Otherwise you may have some problems. Example: In the following example, data entered by the user is written in a file called info.dat. Then this information (in info.dat) is read. For this purpose, write a function that reads and writes data from/into a file.
void writeIntoFile(char *filename){ FILE *id; int n, i; double data; id = fopen(filename,"w"); printf("Enter the number of data to be entered:"); scanf("%d",&n); for (i = 0; i < n; i++){ scanf("%lf",&data); fprintf(id,"%lf\n",&data); } fclose(id); }
int main(){ double *data; int no, i; writeIntoFile("info.dat"); data = readFromFileIntoAnArray("info.dat",&no); for (i = 0; i < no; i++) printf("%lf\n",data[i]); free(data);