Table of Contents for
Intermediate C Programming

Version ebook / Retour

Cover image for bash Cookbook, 2nd Edition Intermediate C Programming by Yung-Hsiang Lu Published by CRC Press, 2015
  1. Front Cover
  2. Contents (1/2)
  3. Contents (2/2)
  4. List of Figures (1/2)
  5. List of Figures (2/2)
  6. List of Tables
  7. Foreword
  8. Preface
  9. Author, Reviewers, and Artist
  10. Rules in Software Development
  11. Source Code
  12. I. Computer Storage: Memory and File
  13. 1. Program Execution (1/2)
  14. 1. Program Execution (2/2)
  15. 2. Stack Memory (1/5)
  16. 2. Stack Memory (2/5)
  17. 2. Stack Memory (3/5)
  18. 2. Stack Memory (4/5)
  19. 2. Stack Memory (5/5)
  20. 3. Prevent, Detect, and Remove Bugs (1/2)
  21. 3. Prevent, Detect, and Remove Bugs (2/2)
  22. 4. Pointers (1/6)
  23. 4. Pointers (2/6)
  24. 4. Pointers (3/6)
  25. 4. Pointers (4/6)
  26. 4. Pointers (5/6)
  27. 4. Pointers (6/6)
  28. 5. Writing and Testing Programs (1/4)
  29. 5. Writing and Testing Programs (2/4)
  30. 5. Writing and Testing Programs (3/4)
  31. 5. Writing and Testing Programs (4/4)
  32. 6. Strings (1/3)
  33. 6. Strings (2/3)
  34. 6. Strings (3/3)
  35. 7. Programming Problems and Debugging (1/4)
  36. 7. Programming Problems and Debugging (2/4)
  37. 7. Programming Problems and Debugging (3/4)
  38. 7. Programming Problems and Debugging (4/4)
  39. 8. Heap Memory (1/3)
  40. 8. Heap Memory (2/3)
  41. 8. Heap Memory (3/3)
  42. 9. Programming Problems Using Heap Memory (1/4)
  43. 9. Programming Problems Using Heap Memory (2/4)
  44. 9. Programming Problems Using Heap Memory (3/4)
  45. 9. Programming Problems Using Heap Memory (4/4)
  46. 10. Reading and Writing Files (1/3)
  47. 10. Reading and Writing Files (2/3)
  48. 10. Reading and Writing Files (3/3)
  49. 11. Programming Problems Using File (1/2)
  50. 11. Programming Problems Using File (2/2)
  51. II. Recursion
  52. 12. Recursion (1/4)
  53. 12. Recursion (2/4)
  54. 12. Recursion (3/4)
  55. 12. Recursion (4/4)
  56. 13. Recursive C Functions (1/4)
  57. 13. Recursive C Functions (2/4)
  58. 13. Recursive C Functions (3/4)
  59. 13. Recursive C Functions (4/4)
  60. 14. Integer Partition (1/5)
  61. 14. Integer Partition (2/5)
  62. 14. Integer Partition (3/5)
  63. 14. Integer Partition (4/5)
  64. 14. Integer Partition (5/5)
  65. 15. Programming Problems Using Recursion (1/5)
  66. 15. Programming Problems Using Recursion (2/5)
  67. 15. Programming Problems Using Recursion (3/5)
  68. 15. Programming Problems Using Recursion (4/5)
  69. 15. Programming Problems Using Recursion (5/5)
  70. III. Structure
  71. 16. Programmer-Defined Data Types (1/6)
  72. 16. Programmer-Defined Data Types (2/6)
  73. 16. Programmer-Defined Data Types (3/6)
  74. 16. Programmer-Defined Data Types (4/6)
  75. 16. Programmer-Defined Data Types (5/6)
  76. 16. Programmer-Defined Data Types (6/6)
  77. 17. Programming Problems Using Structure (1/4)
  78. 17. Programming Problems Using Structure (2/4)
  79. 17. Programming Problems Using Structure (3/4)
  80. 17. Programming Problems Using Structure (4/4)
  81. 18. Linked Lists (1/3)
  82. 18. Linked Lists (2/3)
  83. 18. Linked Lists (3/3)
  84. 19. Programming Problems Using Linked List (1/2)
  85. 19. Programming Problems Using Linked List (2/2)
  86. 20. Binary Search Trees (1/4)
  87. 20. Binary Search Trees (2/4)
  88. 20. Binary Search Trees (3/4)
  89. 20. Binary Search Trees (4/4)
  90. 21. Parallel Programming Using Threads (1/5)
  91. 21. Parallel Programming Using Threads (2/5)
  92. 21. Parallel Programming Using Threads (3/5)
  93. 21. Parallel Programming Using Threads (4/5)
  94. 21. Parallel Programming Using Threads (5/5)
  95. IV. Applications
  96. 22. Finding the Exit of a Maze (1/5)
  97. 22. Finding the Exit of a Maze (2/5)
  98. 22. Finding the Exit of a Maze (3/5)
  99. 22. Finding the Exit of a Maze (4/5)
  100. 22. Finding the Exit of a Maze (5/5)
  101. 23. Image Processing (1/3)
  102. 23. Image Processing (2/3)
  103. 23. Image Processing (3/3)
  104. 24. Huffman Compression (1/10)
  105. 24. Huffman Compression (2/10)
  106. 24. Huffman Compression (3/10)
  107. 24. Huffman Compression (4/10)
  108. 24. Huffman Compression (5/10)
  109. 24. Huffman Compression (6/10)
  110. 24. Huffman Compression (7/10)
  111. 24. Huffman Compression (8/10)
  112. 24. Huffman Compression (9/10)
  113. 24. Huffman Compression (10/10)
  114. A. Linux
  115. B. Version Control
  116. C. Integrated Development Environments (IDE) (1/3)
  117. C. Integrated Development Environments (IDE) (2/3)
  118. C. Integrated Development Environments (IDE) (3/3)
Stack Memory 19
are strongly discouraged, global constants are acceptable and commonly used because they
cannot change.
2.3.4 Value Address
So far, all our functions’ return types have been void, i.e., the functions have all returned
nothing. Functions can return values. Consider this example:
int f1 ( in t k , in t m )1
{2
return (k + m) ;3
}4
5
void f2 ( void)6
{7
int u;8
u = f1 (7 , 2) ;9
// RL A10
}11
The local variable u is inside f2 so it is in f2’s frame. The value of u is undefined because
it has not yet been assigned to anything. Remember that C does not initialize variables,
so uninitialized variables could store any values (i.e., garbage). The frame for f2 contains
the variable u whose value is undefined yet.
Frame Symbol Address Value
f2 u 100 garbage
The address of u is stored in the call stack before f1 is called. This address is called the
value address because it is the address where the return value of function f1 will be stored.
Thus, when the frame for f1 is constructed, one more row is added for the value address,
and its value is the address of u.
Frame Symbol Address Value
f1
m 104 2
k 103 7
Value Address 102 100
Return Location 101 line 10
f2 u 100 garbage
When function f1 executes, it adds the values of k and m, producing the value 9. The
number 9 is then written to (i.e., replaces) the original garbage value at address 100. After
f1 finishes, and its frame has been popped, the call stack will be as follows:
Frame Symbol Address Value
f2 u 100 9
This rule can be incorporated into the previous rules of the call stack.
If a function returns a value, the value is written to a local variable in the caller’s
frame. This variable’s address (called the value address) is stored in the call stack.
If a function has local variables, then the local variables are stored above the argu-
ments.
If a function has arguments, then the arguments are stored above the return location.
20 Intermediate C Programming
The arguments and the return location together form the frame of the called function.
When a function is called, the line number after this call is pushed onto the call stack.
This line number is the “return location” (RL). This is the place from which the
program will continue after the called function finishes (i.e., returns).
If the same function is called from multiple lines, then each call has a corresponding
return location (the line after each function call).
When a function finishes, the program continues from the line number stored at the
top of the call stack. The top of the call stack is then popped.
Note that the caller (f2) is not obliged to store the return value of the callee (f1), and
line 9 in the example above can be written as:
f1 (7, 2) ;9
In this case, function f1 is called but the returned value is discarded. Since there is no need
to store the return value, the value address is not pushed onto the call stack.
The keyword return can be used for two different purposes:
If void is in front of the function’s name, the function does not return any value. The
word return stops the function and the program continues from the return location
in the caller.
If the function is not void, the word return assigns a value to the variable given by
the value address in the call stack.
Please remember that if a function executes a return statement, anything after the
return is ignored and will not be executed. Executing a return statement stops the func-
tion, and its frame is popped from the call stack. The program then continues from the
return location.
2.3.5 Arrays
The following example creates an array of five elements. Each element contains one
integer, which will be uninitialized.
int arr [5];1
Symbol Address Value
arr[4] 104 garbage
arr[3] 103 garbage
arr[2] 102 garbage
arr[1] 101 garbage
arr[0] 100 garbage
If an array has five elements, the valid indexes are 0, 1, 2, 3, and 4. The first index is
0, not 1; the last index is 4, not 5. The array is said to be “zero indexed”. In general, if an
array has n elements, the valid indexes are 0, 1, 2, ..., n 1. Please remember that n is
not a valid index. This is a common mistake among students.
Programmers have no control over addresses and this is still true for arrays. The ad-
dresses of an array’s elements are, however, always contiguous. Suppose i < j < k and all
of them are valid indexes for an array called arr. Then the address of arr[j] is between
the addresses of arr[i] and arr[k]. If an array’s elements are not initialized (like in the
example above), then the values are garbage.
The following example illustrates C’s facility to initialize arrays:
int arr [5] = { -31 , 52 , 65 , 49 , -18};1
Stack Memory 21
Symbol Address Value
arr[4] 104 18
arr[3] 103 49
arr[2] 102 65
arr[1] 101 52
arr[0] 100 31
It is possible to initialize all the elements to zero in this way:
int arr [5] = {0};1
It is possible to create an array without giving the size:
int arr [] = { -31 , 52 , 65 , 49 , -18};1
In this case, the compiler automatically calculates the size as 5.
2.3.6 Retrieving Addresses
It is possible to get a variable’s address by adding an & in front of it. This address can be
printed with the printf function by using the “%p” format specifier. The following example
prints the addresses of both a and c.
// address .c1
#in clude < stdio .h >2
#in clude < stdlib .h >3
int main ( i n t argc , char * * argv )4
{5
int a = 5;6
int c = 17;7
printf (" as address is %p , c s address is %p\ n" , &a , & c );8
return EXIT _SUCCES S ;9
}10
Below is a sample output from this program:
a’s address is 0x7fff2261aea8, c’s address is 0x7fff2261aeac
The output will probably be different when the program is run again:
a’s address is 0x7fffb8dad0b8, c’s address is 0x7fffb8dad0bc
As you can see, the addresses change. If you execute the same program, you will likely
see different addresses.
2.4 Visibility
Every time a function is called, a new frame is pushed to the call stack. A function
can see only its own frame. Consider these two examples:
22 Intermediate C Programming
int f1 ( in t k , in t m )1
{2
return (k + m) ;3
}4
5
void f2 ( void)6
{7
int a = 5;8
int b = 6;9
int u;10
u = f1 ( a + 3, b - 4) ;11
// some additiona l code12
}13
int f1 ( in t a , in t b )1
{2
return (a + b) ;3
}4
5
void f2 ( void)6
{7
int a = 5;8
int b = 6;9
int u;10
u = f1( a + 3, b - 4) ;11
// some additiona l code12
}13
These two programs are identical. Renaming the arguments of f1 from k and m to a and
b has no effect. What about the call stack? This is the call stack when f1 is called in the
first example:
Frame Symbol Address Value
f1
m 106 2
k 105 8
Value Address 104 102
Return Location 103 line 14
f2
u 102 garbage
b 101 6
a 100 5
The call stack in the second example is the same, except that the arguments in frame f1
have different symbols. Note that the addresses are the same. The second example highlights
the fact that the a and b in f1 refer to different address–value pairs than the a and b in f2.
This is the call stack:
Frame Symbol Address Value
f1
b 106 2
a 105 8
Value Address 104 102
Return Location 103 line 14
f2
u 102 garbage
b 101 6
a 100 5
The a in f1’s frame has nothing to do with the a in f2’s frame. Renaming a to k makes
no difference to the behavior of the program. The same rule applies to b. Remember that
computers do not know about symbols. Computers only use addresses and values. Symbols
are only useful for any humans that are reading the code, and are discarded when a program
is compiled into machine-readable format.
This can be a source of confusion among students. It may seem intuitive that the a in
f1’s frame and the a in f2’s frame are related. In fact, they occupy different locations in
the call stack and are unrelated. The following example offers a further explanation:
int f1 ( in t a , in t b )1
{2
a = a + 9;3
Stack Memory 23
b = b * 2;4
return (a + b) ;5
}6
7
void f2 ( void)8
{9
int a = 5;10
int b = 6;11
int u;12
u = f1( a + 3, b - 4) ;13
// some additiona l code14
}15
The following table shows the call stack when the program has entered f1 but has not
yet executed line 3:
Frame Symbol Address Value
f1
b 106 2
a 105 8
Value Address 104 102
Return Location 103 line 14
f2
u 102 garbage
b 101 6
a 100 5
After line 3 has been executed, the call stack will appear as in the table below. Note
that function f1 only modifies the variable a that is in its frame, since a function can only
see arguments and variables in its own frame.
Frame Symbol Address Value
f1
b 106 2
a 105 8 17
Value Address 104 102
Return Location 103 line 14
f2
u 102 garbage
b 101 6
a 100 5
The following table shows the call stack after the program has executed line 4:
Frame Symbol Address Value
f1
b 106 2 4
a 105 8 17
Value Address 104 102
Return Location 103 line 14
f2
u 102 garbage
b 101 6
a 100 5
Function f1 returns a + b, which is 17 + 4 = 21. The value 21 is written to the value
at address 102 (i.e., the value address). After f1 returns, the call stack is as follows: