C Programming and Numerical Analysis 2nd Edition
C Programming and Numerical Analysis 2nd Edition
Engineering
This series publishes short books in mechanical engineering (ME), the engineering branch
that combines engineering, physics and mathematics principles with materials science to
design, analyze, manufacture, and maintain mechanical systems. It involves the production
and usage of heat and mechanical power for the design, production and operation of
machines and tools. This series publishes within all areas of ME and follows the ASME
technical division categories.
Seiichi Nomura
C Programming
and Numerical Analysis
An Introduction
Second Edition
Seiichi Nomura
Mechanical and Aerospace Engineering
The University of Texas at Arlington
Arlington, TX, USA
This work is subject to copyright. All rights are solely and exclusively licensed by the Publisher, whether the whole
or part of the material is concerned, specifically the rights of translation, reprinting, reuse of illustrations, recitation,
broadcasting, reproduction on microfilms or in any other physical way, and transmission or information storage
and retrieval, electronic adaptation, computer software, or by similar or dissimilar methodology now known or
hereafter developed.
The use of general descriptive names, registered names, trademarks, service marks, etc. in this publication does
not imply, even in the absence of a specific statement, that such names are exempt from the relevant protective
laws and regulations and therefore free for general use.
The publisher, the authors and the editors are safe to assume that the advice and information in this book are
believed to be true and accurate at the date of publication. Neither the publisher nor the authors or the editors give
a warranty, expressed or implied, with respect to the material contained herein or for any errors or omissions that
may have been made. The publisher remains neutral with regard to jurisdictional claims in published maps and
institutional affiliations.
This Springer imprint is published by the registered company Springer Nature Switzerland AG
The registered company address is: Gewerbestrasse 11, 6330 Cham, Switzerland
This book is intended for STEM students who are eager to learn the principles of computer
programming and apply these techniques to the numerical analysis challenges that arise
in engineering and science. By focusing on practical applications, the book aims to bridge
the gap between theoretical knowledge and real-world problem-solving.
The objectives of this book, as implied by its title, are twofold:
While no prior programming knowledge is required, it’s helpful for readers to have
a basic understanding of sophomore-level calculus and linear algebra. This background
will make it easier for students to grasp the mathematical concepts discussed and better
understand how to apply them through programming.
This book uses C as the main programming language. There’s an ongoing debate in
academic circles about which programming language is best for college courses. Histori-
cally, FORTRAN was the dominant programming language for scientific and engineering
computations until the 1990s. However, its popularity declined with the rise of modern
languages such as PASCAL and C. Today, MATLAB is often taught as an introduc-
tory computer tool for STEM students. Python is becoming more popular as a versatile,
v
vi Preface
general-purpose language and is increasingly seen as a good starting point for students.
Among the many modern programming languages, using C for scientific and engineering
computations is still beneficial. C covers almost all the basic concepts and syntax found in
modern programming languages, except for object-oriented programming, which is used
in languages such as C++ and Java.
Those who learn C as their first language often find it easy to pick up other pro-
gramming languages and tools, including MATLAB. However, the reverse is not true. A
key advantage of C is that it is a compiled language, making it preferable to interpreted
languages for scientific and engineering tasks.
While there are many excellent textbooks on C and numerical analysis, finding one that
effectively combines both topics is difficult. This book is not meant to be a comprehen-
sive reference to C or numerical analysis. Instead, it focuses on the C features essential
for numerical analysis, intentionally leaving out features not directly related to this pur-
pose. This book does not cover C++, as the benefits of object-oriented programming in
numerical analysis are minimal.
By the end of this book, readers will be able to solve various engineering and science
problems by writing their own C programs.
The book is structured into two parts. In Part I, comprehensive coverage of the gen-
eral syntax of the C language is provided. The compiler utilized throughout is gcc,
which is freely available on nearly all platforms. Given that gcc is native to the UNIX
environment, a concise introduction to the UNIX operating system is included.
Part II focuses on key topics in numerical analysis, accompanied by detailed expla-
nations and listings of corresponding C programs. The subjects covered include solving
single equations, numerical differentiation, numerical integration, solving a set of simul-
taneous equations, and solving differential equations.
Appendix A introduces gnuplot, a visualization application essential for plotting
program output, as the C language itself lacks graphical capabilities.
Appendix B offers a brief tutorial on Octave/MATLAB, designed for those acquainted
with C seeking a quick mastery of Octave/MATLAB.
In Appendix C, a concise tutorial on FORTRAN is provided for those already familiar
with C, enabling a rapid understanding of programs written in FORTRAN (FORTRAN
77).
This book is derived from the course notes in a sophomore-level numerical analysis
and programming course taught at the Department of Mechanical and Aerospace Engi-
neering at the University of Texas at Arlington. I extend my gratitude to the students who
participated in this course for their invaluable feedback. I also wish to express apprecia-
tion to Paul Petralia of Springer Nature for his encouragement and understanding and Dr.
Clovis L. Tondo of T&T TechWorks, Inc. for his support. The programs and tools featured
in this book are accessible at no cost via the internet, a testament to the commendable
vision of the GNU project and the Free Software Foundation (FSF).
Preface vii
The first edition of this book was published in 2018. The second edition corrects minor
errors, enhances content clarity, and includes an expanded section on memory manage-
ment. I would like to thank Dr. Dieter Merkle and Bagavathi Murugesan of Springer
Nature for their support.
ix
x Contents
This section assumes that the platform for C programming is a Unix server and involves
running gcc [1] from a remotely connected Windows computer.2
Before the arrival of Windows 10, the primary method for connecting to a remote Unix
server was through putty, a lightweight Windows terminal program supporting ssh con-
nections.
Today, the functionality of ssh is integrated into the Windows system and accessible
directly from the command line. To utilize ssh, simply enter “ssh abc1234@server.
myschool.edu” into the Windows search box at the bottom as shown in Fig. 1.1 where
1 gcc first compiles the source file into an object file (*.o), then, links the object file to necessary
libraries, and finally produces an executable file (a.out).
2 If you are using a Linux system, simply open a shell window.
abc1234 is your username and server.myschool.edu is the host name of your server.
Once connected, enter your password at the prompt which is not echoed. When successfully
logged in, you see a screen similar to Fig. 1.2.3
Navigating through a Unix shell is different from Windows/Mac environments where
users are accustomed to interacting with graphical user interfaces (GUIs) by simply clicking
icons to access applications. However, to use gcc, you must use the character based interface
(CUI) to compile and run your program in a UNIX shell, on a command line (DOS) window
(Windows) or in Terminal App (Mac).
In the following sections of this book, we use a Unix shell connected to a remote server
from a terminal on either a PC or a Mac.
If you have never used a UNIX system before, you may want to play with some of the
essential UNIX commands. Try the following:
3 It is also possible to have a similar setup at home by running your own Linux server or installing a
PC/Mac version of gcc.
1.1 A Cycle of C Programming 5
$ nano MyProgram.c
The symbol, $, is the system prompt so do not type it. Enter the following text into nano.
Note that all the input in UNIX is case-sensitive.
#include <stdio.h>
int main()
{
printf("Hello, World!\n");
return 0;
}
3. After you finish entering the text, save the file (Control-O5 ) by entering MyProgram.c6
as the file name to be saved and press Control-X to exit from nano. This will save the
file you just created permanently under MyProgram.c.
4. The file you created with nano is a text file that is not understood by the computer. It is
necessary to translate this text file into a code which can be run on the computer. This
translation process is called compiling and the software to do this translation is called a
compiler. We use gcc for this purpose.
At the system prompt ($), run a C compiler (gcc) to generate an executable file
(a.out7 ).
$ gcc MyProgram.c
If everything works, the system will create an executable binary file whose default name
is a.out.
5. Run the executable file.
4 nano is a simple editor that comes with all the installation of UNIX. It is a clone of another simple
text editor, pico.
5 Hold down the control key and press O.
6 The file name is case sensitive.
7 a.out is an abbreviation for .assembler output.
1.2 UNIX Command Primer 7
$ ./a.out8
$ nano MyProgram.c
$ ./a.out
In a perfect world, you could compose a C program, compile it and run a.out and you are
done with it. This scenario may work for a program of less than 10 lines but as the size of
the program grows or the program depends on other modules, it is necessary to manipulate
and organize files on the UNIX system. Even though this is not an introductory book of
the UNIX operating system, a minimum amount of knowledge about the UNIX operating
system is needed. The following are some of the UNIX commands that are used often. Try
each command yourself from the system prompt and find out what it does. It won’t damage
the machine.
• ls (Directory listing.)
• ls -l (Directory listing in long format.)
• ls -lt .| more (Directory listing, one screen at one time, long format, chronological
order.)
• dir (alias for ls)
• ls . (Lists the current directory.)
• cd .. (Moves to the directory one level up.)
• pwd (Shows the present working directory.)
• cd / (Moves to the top directory.)
• cd (Returns to the home directory.)
• mkdir MyNewFolder (Creates a new directory.)
• nano myfile.txt (Creates a new file.)
• cp program1.c program2.c (Copies program1.c to program2.c.)
8 ”./" represents the current directory. If the current directory is included in the PATH environmental
variable, ”./" is not necessary.
8 1 First Steps to Run a C Program
To quickly move while entering/editing a command line and in nano sessions, master the
following shortcuts. ˆf means holding down the control key and pressing the f key.
Arguably, the most important book on the C language is a book known as “K&R” written
by Kernighan and Ritchie [2] who themselves developed the C language. It is concise yet
well-written and is highly recommended for reading.
The following program is absolutely the smallest C program that can be written:
int main()
{
return 0;
}
You can compile and execute this program using the following commands:
$ gcc MyProgram.c
$ ./a.out
where MyProgram.c is the name of the saved file. Despite its simplicity, this program is
fully functional. When you run ./a.out, the program simply exits and returns you to the
system prompt.
Here is a line-by-line analysis of the program above. Refer to Sect. 1.3.1 for related
concepts:
The first line, int main(), declares a function named main that returns an integer
value (int) upon exit (Item 4). This function takes no arguments as indicated by the empty
content (Item 5). The program consists solely of the main() function, which is executed
first (Item 7). The content of the main() function is enclosed by { and } (Item 6). The
function executes the return 0 statement and exits, returning a value of 0 to the operating
system. As C is a free-form language, the end of each statement has to be clearly marked.
A semicolon ; is placed at the end of each statement. Hence, return 0;.
The following program is a celebrated code that appeared first in the K&R book in the
Getting Started section and later adapted in just about every introductory book for C as the
first C program that prints “Hello, World!” followed by an extra blank line.
1:#include <stdio.h>
2:int main()
3: {
4: printf("Hello, World!\n");
5: return 0;
6: }
Each line in the program above is now parsed. The first line, .#include <stdio.h>,
might be a bit confusing, so let’s skip it for now and move on to the subsequent lines.
If you compile the program and execute a.out, you will see that the program prints
Hello, World! followed by a new line on the screen. Thus, you can infer that the special
characters \n represent a newline. Since there is no specific character that represents a blank
line, you can use \n to print a newline.
Next, note that the part printf is followed by a pair of parentheses, indicating that
it is a function in C (Item 5). It is clear that this function, printf(), prints the string
Hello, World! and then terminates. Since it is a function in C, it must be defined and
declared before it is used. However, no such definition is found above the main() line.
The first line, .#include <stdio.h>, actually refers to a file that contains the definition
of printf(), which is preloaded before anything else. The file stdio.h is a header
file (hence the extension h) available in the C library that comes with gcc. As the name
indicates (stdio = STanDard Input and Output), this header file contains the definitions
of many functions that handle input and output (I/O) operations.
Finally, a function must specify the type of the value it returns, such as int, float,
double, etc. (Item 4). In this case, the function int main() is declared to return an
integer value upon exit. Indeed, the last statement return 0; returns 0 when the execution
is complete, and 0 is an integer.
Here is how gcc parses this program line by line:
Line 1 Before anything else, it loads the header file .<stdio.h>, which contains the
definitions of all the functions that deal with I/O from the system area.
Line 2 This is the start of a function called main(). This function returns an integer value
(int) upon exit. Since this function has no parameters, the parentheses are empty.
Line 3 The { character indicates the beginning of the content of the function main().
Line 4 This line calls the function printf(), which is defined in.<stdio.h>, and prints
the string Hello, World! followed by a newline. The semicolon (;) marks the
end of this statement.
1.3 Overview of C Programming 11
Line 5 This is the last statement of the function main(). It returns the value 0 to the
operating system and exits.
Line 6 The } character indicates the end of the content of the function main().
$ nano hello.c
$ gcc hello.c
$ ./a.out
Hello, World!
1:#include <stdio.h>
2:#include <math.h>
3: /* This is a comment */
4:int main()
5: {
6: float x, y;
7: x = 6.28;
8: y = sin(x);
9: printf("Sine of %f is %f.\n", x, y);
10: return 0;
11: }
This program computes the value of .sin x where .x = 6.28 radians. The program can be
compiled as:
There are several new concepts in this program that need to be explained. The second line
preloads another header file, .math.h, as this program computes the sine of a number. In the
fourth line, two variables, x and y, are declared. The float keyword indicates that the two
variables represent floating-point numbers (real numbers with decimal points). The fifth line
assigns the number 6.28 to the variable x. The equal sign (=) here is not the mathematical
equality you are accustomed to. In C and most other programming languages, an equal sign
(=) is used for assignment, meaning the value on the right of = is assigned to the variable
on the left. In the eighth line, the printf() function prints a list of variables (x and y)
with formatting specified by the double quotation marks (“...”). The way formatting works
is that printf() prints everything literally within the quotation marks except for special
codes starting with the percentage sign (%). Here, %f represents a floating-point number,
which is replaced by the actual value of the variable. As there are two %f’s, the first %f is
replaced by the value of x, and the second %f is replaced by the value of y. The details of
the new concepts shown here will be explained in Chap. 2.
10 “-l” is used to link a library, and “m” stands for the math library.
11 .<math.h> contains only the function declarations (prototypes) for mathematical functions. It is
necessary to link the math library libm.a locally using the -lm option.
13 A comment can also start with //. This is for one-line comment originated in C++.
1.4 Exercise 13
1.4 Exercise
It is not necessary to know all the C syntax to work on the following problems. Each problem
includes a template that you can modify. Begin with the template code, make adjustments,
and understand the purpose of each statement. It is crucial that you write the code yourself
(rather than copying and pasting) and execute it.
1. Write a C program to print three blank lines followed by “Hello, World!”. Use the
following code as a template.
#include <stdio.h>
int main()
{
printf("\nHello, World!\n\n");
return 0;
}
#include <stdio.h>
int main()
{
int a, b; /* to declare that a and b are integer variables */
printf("Enter two integer numbers separated by space = ");
scanf("%d %d", &a, &b); /* This is the way to read two integer
numbers and assign them to a and b. */
printf("The sum of the two numbers is %d.\n", a + b);
/* %d is for integer format. */
return 0;
}
3. Write a program to read a real number, .x, and outputs its sine, i.e. .sin(x). You need to
use .<math.h> and the -lm compile option. Use the following template program that
computes .e x .
#include <stdio.h>
#include <math.h>
14 1 First Steps to Run a C Program
int main()
{
float x;
printf("Enter a number ="); scanf("%f", &x);
printf("x = %f exp(x) = %f\n",x, exp(x));
return 0;
}
4. The following code contains syntax errors. Correct the errors and compile it.
#include <stdio.h>
int main()
{
print(’Hello World!\n’)
return o;
}
Components of C Language
2
In this chapter, we introduce and explain the essential components of the C language. While
the syntax covered is not exhaustive, by the end of this chapter, you should be able to write
a simple C program capable of solving various problems in engineering and science.
Every variable used in C must have a type that specifies the kind of value it represents. The
four variable types are listed in Table 2.1.
In Table 2.1, the third column displays the format specifiers for each data type, which are
used in the printf() and scanf() functions.
• int represents an integer value. The range of int depends on the hardware and the
version of the compiler. In most modern systems, int ranges from .−2147483647 to
2147483647.
• float represents a floating-point number. This type is suitable for most non-scientific
floating-point calculations (single precision). For scientific and engineering computa-
tions, double should be used.
• double is an extension of float. This data type can handle larger floating-point
numbers at the cost of increased memory usage (though not significantly).
• char represents a single ASCII character. This data type is essentially a subset of int
with a range limited to 0 to 255. Characters represented by char must be enclosed in
single quotation marks (’).
For example, in the expression a * b, where a is of type int and b is of type float, a
is automatically converted to float, and the result is also of type float.
There are instances where two variables are both of type int, yet the result of the
operation is desired to be of type float. For example,
#include <stdio.h>
int main()
{
int a, b;
a = 3; b = 5;
printf("%f\n", a/b);
return 0;
}
$ gcc prog.c
2.c: In function ’main’:
2.c:6:10: warning: format ’%f’ expects argument of type ’double’,
but argument 2 has type ’int’ [-Wformat=]
printf("%f\n", a/b);
ˆ
$ ./a.out
-0.000000
2.1 Variables and Data Types 17
It prints 0 with a warning, even though the expected result is 0.6. To perform this operation
as intended,1 a cast operator (an operator that allows the temporary change of a variable’s
type) must be used as
#include <stdio.h>
int main()
{
int a, b;
a = 3; b = 5;
printf("%f\n", (float)a/b);
return 0;
}
$ gcc prog.c
$ ./a.out
0.600000
The (float)a/b part forces both variables to be of float type and returns 0.6 as
intended.
/*
Print a character
*/
#include <stdio.h>
int main()
{
char a = ’h’;
printf("%c\n",a);
return 0;
}
Note that the variable a is declared as char and initialized with the value ‘h’ on the
same line.
2. This program prints an integer 10.
/*
Print an integer
*/
#include <stdio.h>
int main()
{
int a = 10;
printf("%d\n",a);
return 0;
}
Note that the variable a is declared as int and initialized with the value 10 on the same
line.
3. This program prints a floating number 10.5.
Note that the variable, a, is declared as float and initialized with the value 10.5 on
the same line.
2.2 Input/Output
Almost all C programs include at least one output statement. Without it, the program will
not display any output on the screen, making it impossible to determine if the program ran
successfully or not.
The most commonly used input/output functions in C are printf() and scanf(),
both of which are defined in the header file stdio.h. Use printf() (“Print with Format”)
to output data to the console, and scanf() (“Scan with Format”) to input data from the
keyboard.
2.2 Input/Output 19
• printf()
The syntax of the printf() function is
printf("format",argument(s));
where format specifies the formatting of the output, which you can control, and
argument(s) is a list of variables to be printed.
The printf() function outputs the value(s) of variables specified in argument(s) to
the standard output (screen) according to the formatting commands defined by format.
Examples:
printf("Hello, World!\n");
printf("Two integers are %d and %d.\n",a,b);
printf("Two floating numbers are %f and %f.\n",a,b);
printf("Three floating numbers are %f, %f and %f.\n",a,b,c);
A string of characters surrounded by the double quotes (") is printed as is. However, the
percentage sign (%) followed by a format specifier is automatically replaced by the value
of a variable followed. Use %d for an integer, %f for a floating number and %c for a
character.
The backslash (\) is called the escape character and escapes the following letter. \n
represents the next line (inserting a blank line), \t represents a tab character and \a
rings the bell. If you want to print the double quotation mark ("), use \". To print the
backslash (\) itself, use \\.
• scanf()
The scanf() function is the counterpart of printf(); it reads the value(s) of vari-
able(s) from the standard input (keyboard) according to a specified format.
The format specification in scanf() (% ...) follows the same conventions as printf().
However, each variable name must be prefixed with an & (ampersand) symbol.
The reason why an & is required for scanf() but not for printf() will be explained
in Sect. 2.9 (pointers).
Unlike in printf(), the format specifiers %f and %lf are distinguished in scanf().
Use %f for reading a single-precision floating-point number, and %lf for reading a
double-precision floating-point number.
20 2 Components of C Language
#include <stdio.h>
int main()
{
int a, b;
printf("Enter two integers separated by a comma = ");
scanf("%d, %d",&a, &b);
printf("a = %d b = %d\n", a, b);
return 0;
}
$ gcc prog.c
$ ./a.out
Enter two integers separated by a comma = 12, 29
a=12 b=29
This program expects two values to be entered from the keyboard, separated by a comma
(,), as specified by "%d, %d" in the scanf() function. Ensure you type the comma
(,) immediately after the first number. The second number can be entered with any
number of spaces preceding it.
#include <stdio.h>
int main()
{
int a; float b;
printf("Enter an integer and a real number separated by a space = ");
scanf("%d %f",&a, &b);
printf("a = %d b = %f\n", a, b);
return 0;
}
$ gcc prog.c
$ ./a.out
Enter an integer and a real number separated by a space = 21 6.5
a=21 b=6.500000
2.3 Operators Between Variables 21
In this program, two numbers must be entered separated by at least one space. The number
of spaces between the numbers can vary arbitrarily.
• getchar() and putchar()
For character-wise input/output, getchar() and putchar() are available.
getchar() and putchar() are simpler, lower-level functions compared to scanf()
and printf(). getchar() reads a single character from standard input (usually the
keyboard), while putchar() writes a single character to standard output (usually the
screen). They work on individual characters, making them suitable for character-by-
character input/output operations. In contrast, scanf() and printf() are formatted
I/O functions. Below is an example code to use getchar() and putchar().
#include <stdio.h>
int main()
{
char ch;
printf("Enter a character: ");
ch = getchar();
printf("You entered: ");
putchar(ch);
/* Print a newline for formatting*/
putchar(’\n’);
return 0;
}
$ gcc program.c
$ ./a.out
Enter a character: A
You entered: A
There are three types of operators used with variables: (1) relational operators, (2) logical
operators, and (3) increment/decrement operators.
22 2 Components of C Language
The statement above indicates that if the two variables, a and b, have the same value,
“a and b are equal.” is printed; otherwise, “a and b are not equal.”
is printed. It’s important to distinguish between the single equal sign (=), used for assign-
ment, and the double equal signs (==), used for checking logical equality. In C, as in
many other programming languages, the double equal sign (==) is used to represent
equality, similar to the single equal sign in standard mathematical equations.
2. a = 10;
a = a + 1;
In C, as in almost all other programming languages, the single equal sign (=) represents
an assignment operation, where the value on the right-hand side of the equal sign is
assigned to the variable on the left-hand side. The statement a = a + 1 may seem
strange and wrong in mathematical terms, but it is perfectly valid in C. Here, a + 1 is
evaluated to yield 11, and this resulting value is then assigned to a. As a result, the value
of a is incremented by 1.
Logical operators in C are primarily used within the if statement. These operators are
essential for making decisions based on multiple conditions as shown in Table 2.3.
Examples The following examples are self-explanatory.
int a = 20;
if (!(a == 10)) printf("a is not equal to 10.");
if (a == 10)
{
printf("The value of a is 10.\n");
return 0;
}
else ....
2 The naming of C++ (another compiler) reflects this concept, signifying an “incremental” improve-
ment over the C language.
24 2 Components of C Language
i = 100;
i++;
is the same as
i = 100;
i = i + 1;
i = i + 1;
i+ = 1;
i++;
++i;
2.4 Control Statements 25
Without control statements, a program can only execute code sequentially from top to bottom
once, which is often not very useful. Control statements enable a program to branch to
different parts and repeat operations as many times as needed, making the program more
flexible and powerful.
The following are the five types of control statements that can control the flow of the
program:
• if .... else
• for ( ; ; )
• while
• do while
• switch
2.4.1 if Statement
#include <stdio.h>
int main()
{
int i;
printf("Enter an integer = ");
scanf("%d", &i);
if (i > 1 && i < 100)
printf("The number is between 1 and 100.\n");
else
printf("The number is not in that range.\n");
return 0;
}
$ gcc prog.c
$ ./a.out
Enter an integer = 45
The number is between 1 and 100.
$ ./a.out
Enter an integer = 104
The number is not in that range.
In fact, the program above functions correctly without the else clause. If the condition in
if (…) is not satisfied, control simply proceeds to the next statement.
A for loop is used when the number of iterations to be executed is known in advance. The
for loop consists of three parts, each separated by a semicolon (;). The first part initializes
a counter variable, the second part tests the iteration counter variable, and if this test fails,
the loop terminates. The third part defines an action on the counter variable. A block of code
can follow the for statement to execute multiple statements for each iteration.
Examples
#include <stdio.h>
int main()
{
int i;
for (i = 0; i < 10; i++) printf("i = %d\n",i);
return 0;
}
$ gcc prog.c
$ ./a.out
i = 0
i = 1
2.4 Control Statements 27
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
. S = 1 + 2 + 3 + 4 + 5 + 6 + . . . + 100.
#include <stdio.h>
int main()
{
int i, sum = 0;
for (i = 0; i <= 100; i++) sum = sum + i;
printf("Sum = %d\n", sum);
return 0;
}
$ gcc prog.c
$ ./a.out
Sum = 5050
In this program, i serves as the iteration variable, while sum acts as a placeholder for the
partial summation of .1 + 2 + 3 + . . .. The parameters in the for loop specify that the
iteration variable i is initialized to 0. As long asi remains less than or equal to 100, the
statement sum = sum + i is repeatedly executed. After each execution of sum =
sum + i, the iteration variable i is incremented by 1. This newly incremented value
of i is then added to the previous value of sum, thereby updating sum. Therefore, sum
now represents .1 + 2 + 3 + . . . + i.
In general, the following pattern can be used to compute a mathematical summation:
sum = 0.0;
for (i = 0; i <= 100; i++) sum = sum + f(i);
which corresponds to
28 2 Components of C Language
100
. f (i) = f (0) + f (1) + f (2) + . . . + f (100).
i=0
∞
(−1)i+1
. . (2.2)
i
i=1
#include <stdio.h>
#include <math.h>
int main()
{
int i, n;
float sum = 0.0;
printf("Enter # of iterations = ");
scanf("%d", &n);
1
. = 1 − x + x2 − x3 + x4 − x5 + . . .
1+x
one can obtain
x2 x3 x4 x5 x6
. ln (1 + x) = x − + − + − + ...
2 3 4 5 6
Substituting .x = 1 on both sides yields
1 1 1 1
. ln 2=1− + − + − ...
2 3 4 5
.
2.4 Control Statements 29
$ ./a.out
Enter # of iterations = 1000
Approximation of log(2) = 0.693646.
Exact value of log(2) = 0.693147.
$ ./a.out
Enter # of iterations = 10000
Approximation of log(2)= 0.693191.
Exact value of log(2)= 0.693147.
$ ./a.out
Enter # of iterations = 10000000
Approximation of log(2)= 0.693137.
Exact value of log(2)= 0.693147.
As seen from the output above, the convergence of the series is rather slow. Also, note
that for modern computers, performing 10,000,000 iterations poses no issue whatsoever.
4. We want to find one of the roots of a cubic equation defined by
. x 3 + x − 1 = 0, (2.3)
which lies between 0 and 1 using an iterative method. By rearranging Eq. (2.3), we obtain
1
. x= . (2.4)
1 + x2
Although Eqs. (2.3) and (2.4) are mathematically equivalent, Eq. (2.3) cannot be used as
a valid C statement, whereas Eq. (2.4) can be implemented as a valid C statement where
1
the evaluated value of . 1+x 2 is assigned to . x. By starting with an appropriate initial guess
for .x, Eq. (2.4) can be iterated until convergence is attained. The following program uses
. x = 1 as the initial guess.
30 2 Components of C Language
#include <stdio.h>
int main()
{
int i, n;
float x = 1.0;
printf("Enter # of iterations = ");
scanf("%d", &n);
for (i = 1; i < n; i++) x = 1/(1 + x*x);
printf("Iteration # = %d, x = %f.\n", i, x);
return 0;
}
$ gcc prog.c
$ ./a.out
Enter # of iterations = 30
Iteration # = 30, x = 0.682327.
$ ./a.out
Enter # of iterations = 31
Iteration # = 31, x = 0.682328.
$ ./a.out
Enter # of iterations = 32
Iteration # = 32, x = 0.682328.
It is observed that convergence was attained after 31 iterations. Although the convergence
rate of this iteration method is slow, it demonstrates that solving this cubic equation does
not require any advanced mathematical techniques.
A while statement executes the subsequent statement(s) as long as the specified condition
remains true. The following program outputs 10.
#include <stdio.h>
int main()
{
int i = 0;
while (i < 10) i++;
printf("%d\n",i);
return 0;
}
2.4 Control Statements 31
$ gcc prog.c
$ ./a.out
10
It may seem that the program should output 9 instead of 10. However, when the condition
i<10 is tested with i=9, i is incremented to 10, and this value is retained thereafter.
.
For multiple statements, it is necessary to use a block with curly brackets ({ and }).
#include <stdio.h>
int main()
{
int i = 0;
while (i < 10)
{
i++;
printf("i = %d\n",i);
}
printf("%d\n",i);
return 0;
}
$ gcc prog.c
$ ./a.out
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
i = 10
10
32 2 Components of C Language
The do while loop is similar to the while loop, except that the test occurs at the end
of the loop body. This guarantees that the loop is executed at least once before continuing.
Such a structure is frequently used in scenarios where data needs be read and validated. The
loop re-reads the data if the initial input is unacceptable.
The following program keeps prompting until the user enters 0 or 1.
#include <stdio.h>
int main()
{
int yesno;
do
{
printf("Enter 1 for yes, 0 for no :");
scanf("%d", &yesno);
} while (yesno ! = 1 && yesno ! = 0);
return 0;
}
$ gcc prog.c
$ ./a.out
Enter 1 for yes, 0 for no :10
Enter 1 for yes, 0 for no :2
Enter 1 for yes, 0 for no :-1
Enter 1 for yes, 0 for no :0
A switch statement enables branching to different tasks based on the value of the variable
provided.
Although the same outcome can be achieved using multiple if statements, employing a
switch statement enhances the clarity and flow of the program.
The following program checks whether the input value is either 1 or 2, and outputs “a
is neither 1 nor 2.” if it is not.
2.5 Miscellaneous Remarks 33
#include <stdio.h>
int main()
{
int i;
printf("Enter an integer="); scanf("%d", &i);
switch(i)
{
case 1: printf("a is 1\n");break;
case 2: printf("a is 2\n");break;
default: printf("a is neither 1 nor 2\n");break;
}
return 0;
}
$ gcc prog.c
$ ./a.out
Enter an integer=12
a is neither 1 nor 2
$ ./a.out
Enter an integer=2
a is 2
It is important to use a break statement to exit the block after each executed case. Note
that a colon (:) must be used after case instead of a semicolon (;).
Several key aspects of C and UNIX, while not warranting separate sections, are still important
to highlight. These are summarized below:
#include <stdio.h>
int main()
{
int i ;
for (i = 1; i > 0; i++) printf("loop");
return 0;
}
34 2 Components of C Language
#include <stdio.h>
int main()
{
float a = 3.14;
printf("%f\n",a);
printf("%10f\n",a);
printf("%20f\n",a);
printf("%30f\n",a);
printf("%10.3f\n",a);
printf("%10.4f\n",a);
printf("%10.5f\n",a);
printf("%10.6f\n",a);
return 0;
}
4 Hold down the Control key and press C. Also if the screen is suddenly frozen and does not accept
any keyboard input, try pressing Control-Q. This is usually caused by accidentally pressing Control-S
which pauses output).
2.5 Miscellaneous Remarks 35
$ gcc prog.c
$ ./a.out
3.140000
3.140000
3.140000
3.140000
3.140
3.1400
3.14000
3.140000
The format, %10.6f specifies that 10 spaces are reserved from the beginning of the
line, with the floating-point number printed with 6 decimal places, right-justified. This
formatting option is purely cosmetic and does not alter the actual value of the variable.
• What is a = b = 20?
A statement such as a = b = 20 may appear unusual but is a valid C statement.
Consider running the following code:
#include <stdio.h>
int main()
{
float a, b;
printf("%f\n", a = 20.0);
b = a = 30.0;
printf("%d\n", a == 20.0);
return 0;
}
In C, a statement such as b = 20 not only assigns the value 20 to b, but also the
expression b = 20 itself also evaluates to 20.
The output of the program is:
$ gcc prog.c
$ ./a.out
20.000000
0
In the statement a = b = 20, the evaluation proceeds from right to left. Thus, b =
20 is executed first, which assigns 20 to b and also results in the value 20 being assigned
to a. This is a convenient way to assign the value 20 to both a and b on the same line.
36 2 Components of C Language
Similarly, in the statement b = a = 30.0, a is assigned 30.0, and then this value is
assigned to b. As a result, a == 20.0 evaluates to false, and the printf() function
returns 0 (false).
• In gcc, you can use the -o5 option (also called a flag) to specify the name of the
executable file instead of the default name a.out.
This generates an executable binary, MyProgram, instead of the default file, a.out,
in the same directory. For the Windows version of gcc, the name of the executable is
MyProgram.exe.
• You can define a symbolic constant by the define preprocessor:
#include <stdio.h>
#define PI 3.141592 /* Defines pi. */
int main()
{
float a;
printf("Enter radius = ");
scanf("%f", &a);
printf("The area of circle is = %f.\n", a*a*PI);
return 0;
}
$ gcc prog.c
$ ./a.out
Enter radius = 2.0
The area of circle is = 12.566368.
5 o for output.
2.5 Miscellaneous Remarks 37
2.5.1 Exercise
1. Write a program that interactively reads temperature in Celsius and convert it to Fahren-
heit. Note
5
.C = (F − 32) × .
9
Expected output:
$ ./a.out
Enter temperature in C = 29
It is 84.2 degrees in Fahrenheit.
2. Write a C program that determines whether an integer entered via the keyboard is a
multiple of 3 or not. Use the modulus operator, .a%b, which returns the remainder when
.a is divided by .b.
3. Write a C program that interactively reads the three coefficients of a quadratic equation
and compute the two roots. The program must alert if there is no real root. The quadratic
equation is given by
.ax + bx + c = 0,
2
1
. = 1 − x2 + x4 − x6 + x8 − . . .
1 + x2
Integrating both sides, we obtain
38 2 Components of C Language
(−1)n+1
an =
. , n = 1, 2, 3 . . .
2n − 1
2.6 Functions
2.6.1 Definition of Functions in C
For functions that do not return a value, the return type void is used.
Examples
1. The following program demonstrates the use of the void return type. The user-defined
function, write(),7 prints "Hello World!" and does not return any value upon
exit. Therefore, it is declared as void.
#include <stdio.h>
void write()
{
printf("Hello, World!\n");
}
x3 x5 x7 x9
. arctan x=x− + − + − ...
3 5 7 9
Substitute .x = 1 into the series, we get
π 1 1 1 1
. arctan 1= = 1 − + − + − ...
4 3 5 7 9
.
7 Surprisingly, C does not have a built-in function named write().
2.6 Functions 39
int main()
{
write();
return 0;
}
$ gcc prog.c
$ ./a.out
Hello, World!
Since the function write() does not accept any parameters, it must be called without
any arguments in the main function.
2. The following program defines a function, cube(), which returns the cube of a param-
eter, x.
#include <stdio.h>
float cube(float x)
{
return x*x*x;
}
int main()
{
float x;
printf("Enter x = "); scanf("%f",&x);
printf("The cube of x is %f.\n", cube(x));
return 0;
}
$ gcc prog.c
$ ./a.out
Enter x = 3
The cube of x is 27.000000.
Upon exit, cube(x) returns .x 3 with x as the argument passed from the main function.
40 2 Components of C Language
3. The following program computes .x y (.x to the power of . y)8 using its own function,
power(), instead of the pow()9 function available in .<math.h>.
#include <stdio.h>
#include <math.h>
float power(float x, float y)
{
return exp(y * log(x));
}
int main()
{
float x,y;
printf("Enter x and y separated by space = ");
scanf("%f %f", &x, &y);
if (x < 0)
{
printf("x must be positive !!\n");
return 0;
}
printf("%f to power of exponent %f is %f.\n", x, y, power(x,y));
return 0;
}
The program exits if .x is negative. Otherwise, it computes .x y and prints its value.
Variables used within a function are local, i.e. they do not retain the values outside the
function. In the following program, the variable name, sum, is used for both f() and
main() yet sum used within f() does not propagate outside the function f().
#include <stdio.h>
int f(int n)
{
int i,sum = 0;
for (i = 1; i <= n; i++) sum = sum + i;
return sum;
}
int main()
{
int i, sum = 0;
for (i = 1; i <= 10; i++) sum = sum + i*i;
printf("%d %d\n", sum, f(10));
return 0;
}
$ gcc prog.c
$ ./a.out
385 55
The printed value of sum is the sum defined in main() even though a variable of the
same name is returned in the function, f().
C functions can be used and defined recursively, meaning that a C function can call itself
within its own definition10 Using recursive algorithms, programs can be written compactly
and efficiently.
Examples
1. Fibonacci numbers
The Fibonacci numbers [3], .an , are defined as follows:
an = an−1 + an−2 , a1 = 1, a2 = 1.
. (2.5)
#include <stdio.h>
int fibo(int n)
{
if (n == 1) return 1;
if (n == 2) return 1;
return fibo(n-1) + fibo(n-2);
}
int main()
{
int i;
printf("Enter n = "); scanf("%d", &i);
printf("%d\n", fibo(i));
return 0;
}
$ gcc prog.c
$ ./a.out
Enter n = 12
144
The program simply translates Eq. (2.5) into the definition of fibo().12
√ n 1 √ n
1
2 1+ 5 − 2 1− 5
.an = √ .
5
.
2.6 Functions 43
1 + 2 + 3 + ... + n
.
#include <stdio.h>
int sum_of_integers(int n)
{
if (n == 0) return 0;
return n + sum_of_integers(n - 1);
}
int main()
{
int n;
printf("Enter n = ");
scanf("%d", &n);
printf("1 + 2 + .... + %d = %d \n", n, sum_of_integers(n));
return 0;
}
$ gcc prog.c
$ ./a.out
Enter n = 100
1+2+....+100 =5050
Random numbers can be generated using the rand() function from the stdlib.h library.
This function is commonly employed in numerical simulations to model various experiments
that would otherwise be difficult or impractical to perform. The following program prints
random numbers 10 times.
44 2 Components of C Language
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i;
for (i = 0; i < 10; i++) printf("%d\n", rand());
printf("\nMAX = %d\n", RAND_MAX);
return 0;
}
$ gcc prog.c
$ ./a.out
1804289383
846930886
1681692777
1714636915
1957747793
424238335
719885386
1649760492
596516649
1189641421
MAX = 2147483647
The function, rand(), returns an integer between 0 and RAND_MAX (system depen-
dent)13 which is defined in the header file, stdlib.h.14
If floating random numbers between 0 and 1.0 are desired instead of between 0 and
RAND_MAX, the following program is used:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i;
for (i = 0; i < 10; i++) printf("%f\n", 1.0 * rand() / RAND_MAX);
return 0;
}
13 2147483647 for most systems. It is the maximum integer value handled by the system.
14 stdlib = standard library.
2.6 Functions 45
$ gcc prog.c
$ ./a.out
0.840188
0.394383
0.783099
0.798440
0.911647
0.197551
0.335223
0.768230
0.277775
0.553970
Note the %f format and the factor of 1.0. The value of rand()/RAND_MAX alone
returns 0 as both rand() and RAND_MAX are integers and the result is a truncated integer.
However, the same numbers are output again and again each time the program is run. They
are all predictive and not true random numbers. In order to generate a different sequence of
random numbers each time the program is run, srand()15 available in stdlib.h must
be used in conjunction with rand(). The srand() function sets its argument as the seed
for a new sequence of pseudo-random integers to be returned by rand(). These sequences
are repeatable by calling srand() with the same seed value. If no seed value is provided,
the rand() function is automatically seeded with a value of 1.
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i;
printf("Enter a seed integer = ");
scanf("%d", &i);
srand(i);
printf("%d\n", rand());
return 0;
}
$ gcc prog.c
$ ./a.out
Enter a seed integer = 10
1215069295
In the program above, srand(i) takes a seed number i and generates a random number
based on the value of i. The issue with this approach is that if the same seed number is
given, the rand() function returns the same value.
To generate a different seed every time, the time() function defined in time.h can
be used. The function time() with the argument NULL returns the elapsed time since
00:00:00 GMT, January 1, 1970,16 measured in seconds.
#include <stdio.h>
#include <time.h>
int main()
{
int i;
printf("%d\n", time(NULL));
return 0;
}
$ gcc prog.c
$ ./a.out
1729224502
At the time of writing, 1,729.224.502 s have passed since 1/1/1960. As this value keeps
increasing, it can be used as a seed number for srand(). The following program generates
a different random number every time it is called.17
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
srand(time(NULL));
printf("%d\n", rand());
return 0;
}
$ gcc prog.c;
$ ./a.out
1729224502
(Wait a few seconds.)
$ ./a.out
1729224510
1 1
1 π
. 1 − x 2d x = 1 − x 2 x + sin−1 (x) = .
0 2 0 4
.
48 2 Components of C Language
consider a pair of random numbers, .x, and . y, each ranging between 0 and 1. These numbers
can be identified as a point, .(x, y), inside the square defined by .{(x, y), 0 ≤ x ≤ 1, 0 ≤
y ≤ 1} as shown in Fig. 2.2. If .(x, y) satisfies .x 2 + y 2 < 1, the point, .(x, y), is inside the
shaded area. Otherwise,.(x, y) is outside the shaded area. Generate a pair of random numbers
between 0 and 1 for . N iterations. For each iteration, if .x 2 + y 2 < 1 is satisfied, increment
the counter .i by 1. Otherwise, continue to the next iteration. At the end of . N iterations, the
ratio .i/N should approximate the ratio of the shaded area to the area of the square. As . N
increases, this ratio is expected to converge to .π/4. This process can be implemented by the
following C program:
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#define PI 3.141592
int main()
{
float x, y;
int i, count = 0;
int n;
printf("Enter iteration number = ");scanf("%d", &n);
srand(time(NULL));
for (i = 0; i < n; i++)
{
x = 1.0*rand()/RAND_MAX;
y = 1.0*rand()/RAND_MAX;
if (x*x + y*y < 1.0) count = count + 1;
}
printf("True value = %f\n", PI/4);
printf("Appx value = %f\n", 1.0*count/n);
return 0;
}
As observed in the output above, the convergence achieved by the Monte Carlo method
is, at best, mediocre for one-dimensional integrals. Therefore, it should be employed only as
a last resort for such cases. However, the Monte Carlo method provides a quick and efficient
way to approximate integrals in two or more dimensions.
2.6.5 Exercise
1 1 1
. 1+ + + ... + .
1! 2! 11!
3. Similar to the example above for the quarter area of the circle, use the Monte Carlo
method to numerically integrate the following integral as shown in Fig. 2.3:
1 1
. d x.
0 1 + x2
Vary the number of iterations (10, 100, 1,000) and estimate the appropriate number of
iterations needed to achieve good accuracy. Note that the exact value of the integral is
.π/4, which can also be used to approximate the value of .π.
4. Using the Monte Carlo method, estimate the volume of the unit sphere defined by
. x 2 + y 2 + z 2 ≤ 1.
Note that the above volume is equivalent to 1/8 of the total sphere.
5. Using the Taylor series expansion for .cos(x) expressed as
x2 x4 x6
. cos (x) = 1 − + − + ... (2.7)
2! 4! 6!
create your own implementation of .cos(x) and demonstrate your program by making a
table similar to the following:
.x mycos(x) cos(x)
0.0 1.000 1.000
0.1 1.101 1.105
0.2 1.308 1.221
… … …
1.0 1.69 1.781
where mycos(x) is the result from your program and .cos (x) is the math function from
math.h.
Note:
Template:
#include <stdio.h>
#include <math.h>
int factorial(int n)
{
(your code here)
}
float mycos(float x)
{
float sum = 0; int i;
for (i = 0; i <= 10; i++) sum = sum + (your code here);
return sum;
}
int main()
{
float ...;
int i = 1; /* counter */
2.7 Arrays
In C programming, vectors and matrices in linear algebra can be implemented using arrays.
An array in C allows the representation of multiple elements under a single variable name.
This section introduces the fundamental concepts of arrays. Arrays are closely related to
pointers in C, which will be discussed in greater detail in Sect. 2.9.
An array is a variable that can represent multiple elements, such as numbers and characters.
Arrays can be declared similar to other variables as follows:
#include <stdio.h>
int main()
{
float a[3];
a[0] = 1.0; a[1] = 2.0; a[2] = 5.0;
(.......)
return 0;
}
52 2 Components of C Language
The program above defines an array, a, which represents three elements. The values,
1.0, 2.0 and 5.0, are assigned to the first, second and third elements of a, respectively. Note
that the index in arrays begins at .0 rather than .1 and ends at .n − 1, where .n is the number
of elements. This can cause some confusion when arrays are used to represent matrices or
vectors in linear algebra, as indices in linear algebra typically begin at .1. Therefore, care
must be taken when manipulating vector or matrix indices.
You can also initialize arrays at the time of declaration as follows:
#include <stdio.h>
int main()
{
float a[3] = {1.0, 2.0, 3.0};
(......)
return 0;
}
or simply,
#include <stdio.h>
int main()
{
float a[] = {1.0, 2.0, 3.0};
(......)
return 0;
}
i.e. if an array is initialized at the time of declaration, its dimension can be omitted, as the
number of elements is automatically determined.
The following program computes the sum of all the elements in an array.
#include <stdio.h>
#define N 5
int main()
{
int i;
float a[N] = {2.0, -15.0, 12.0, -5.4, 1.9};
float sum = 0.0;
for (i = 0; i < N; i++) sum = sum + a[i];
printf("The sum is = %f.\n", sum);
return 0;
}
2.7 Arrays 53
$ gcc prog.c
$ ./a.out
The sum is = -4.500000.
Arrays can be nested, i.e. they can take more than 1 indices. Nested arrays (multi-dimensional
arrays) can represent matrices in linear algebra. For example, the components of a .2 × 5
matrix, a, can be represented in C as follows:
#include <stdio.h>
#define COL 5
#define ROW 2
int main()
{
int i,j;
float mat[ROW][COL] = {{1.0 ,2.0 ,3.0, 4.0 ,5.0},{6.0, 7.0,
8.0, 9.0, 10.0}};
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COL; j++) printf("%f ", mat[i][j]); printf("\n");
}
return 0;
}
$ gcc prog.c
$ ./a.out
1.000000 2.000000 3.000000 4.000000 5.000000
6.000000 7.000000 8.000000 9.000000 10.000000
2.7.3 Examples
1
N
sx2 ≡
. (xi − X̄ )2 . (2.8)
N −1
i=1
Note the factor,. N − 1, instead of. N in Eq. (2.8). This adjustment is a mathematical neces-
sity to account for sample variability. The standard deviation, .sx , having the dimension
as .xi , is defined as
1
N
sx ≡
. (xi − X̄ )2 .
N −1
i=1
The following program computes the average and the standard deviation of 10 data
points.
#include <stdio.h>
#include <math.h>
#define N 10
int main()
{
float a[N] = {0.974742, 0.0982212, 0.578671, 0.717988, 0.881543,
0.0771773, 0.910513,0.576627, 0.506879, 0.629856};
float sum = 0, average, var = 0, sd;
int i;
for (i = 0; i < N; i++) sum = sum + a[i];
average = sum/N;
for (i = 0; i < N; i++) var = var + pow(a[i] - average, 2);
sd = sqrt((var)/(N - 1.0));
printf("Average = %f S.D.= %f\n", average, sd);
return 0;
}
The goal is to find the best fit line that represents the data points in the format:
. y = ax + b, (2.9)
where .a is the slope and .b is the y-intercept. The parameters .a and .b are chosen to
minimize the error. The error is defined as the difference between the measured and
predicted values. Since this difference can be either positive or negative, it is squared to
ensure it is positive. Thus, the total error,21 . E 2 , is the sum of the squared differences at
each point and is defined as:
N
. E2 ≡ (axi + b − yi )2 . (2.10)
i=1
To minimize . E 2 , partially differentiate . E 2 with respect to both .a and .b, and set the
derivatives to zero22 :
∂ E2 ∂ E2
= 0,
. = 0.
∂a ∂b
This yields a set of two simultaneous equations for .a and .b as
N
2
. (axi + b − yi )xi = 0,
i=1
N
2 (axi + b − yi )(+1) = 0,
i=1
N
N
N
.( xi2 )a + ( xi )b = xi yi ,
i=1 i=1 i=1
N
N
N
( xi )a + ( 1)b = yi .
i=1 i=1 i=1
#include <stdio.h>
#define N 10
int main()
{
float x[N] = {1,2,3,4,5,6,7,8,9,10},
y[N]={-3, 2.9, 0.5, 3.0, 2.6, 5.2, 4.9, 4.5, 6.1, 7.2};
float xysum = 0.0, xsum = 0.0, ysum = 0.0, x2sum = 0.0;
float a, b, det;
int i;
for (i = 0; i< N; i++)
2.7 Arrays 57
{
xsum = xsum + x[i];
ysum = ysum + y[i];
xysum = xysum + x[i]*y[i];
x2sum = x2sum + x[i]*x[i];
}
det = x2sum * N - xsum * xsum;
a = (xysum * N - xsum * ysum)/det;
b = (x2sum * ysum - xysum * xsum)/det;
printf("The regression line is %f x + %f.\n", a, b);
return 0;
}
$ gcc prog.c
$ ./a.out
The regression line is 0.863636 x + -1.359998.
2.7.4 Exercise
1. Birthday Paradox
What is the likelihood that two individuals in a group of . N people share the same
birthday?23 Use the Monte Carlo method to estimate this probability.
Suggested Approach:
(a) Prepare an integer array, a[N], to hold the birthday dates for N individuals.
(b) Generate a random number between 1 and 365 for each element of a[].24
(c) Compare a[0] with the other elements in a[]. If a match is found, exit the loop,
increment the counter by 1, and proceed to step (b) (i.e., start the next round of the
simulation).
23 This problem is known as the birthday paradox [5] and can be exactly solved using probability
theory. Mathematically, the probability is given by . p(N ) = 1 − 365! . For . N = 23, this
365 N (365−N )!
probability exceeds 50%.
24 Use the modulus operator % for this purpose. For example, 300%7 returns the remainder of 300
divided by 7, which is 6.
58 2 Components of C Language
(d) Compare a[1] with the remaining elements in a[]. If a match is found, exit the
loop, increment the counter by 1, and proceed to step (b) (i.e., start the next round of
the simulation).
(e) Continue this process for all elements in a[].
(f) After completing .n simulations, compute the probability as counter/n.
ExitLoop:;
.....
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
int main()
{
int i, a[10000];
float sum = 0, avg, std;
srand(time(NULL));
3. If the experimental data, as shown in Fig. 2.5, exhibit a trend suggesting a curved relation-
ship, it is more appropriate to model the data using a second-order polynomial expressed
as
. y = ax + b,
2
(2.11)
rather than the linear model of Eq. (2.9).
2.8 File Handling 59
Following the linear regression analysis, derive the expressions for .a and .b, and calculate
their values for the following given data:
x[N]={1,2,3,4,5,6,7,8,9,10};
y[N]={4.19, 8.24, 13.53, 22.12, 32.30, 44.42, 59.08,76.35, 96.29, 117.28};
In this section, we discuss how C programs can interact with external files, covering the
necessary functions and methods to read from and write to files.
UNIX shells (such as csh, tcsh, and bash on most UNIX platforms) and the DOS command
prompt support I/O redirection.25 Instead of entering data from the keyboard and displaying
the output on the screen, it is possible to redirect input and output to or from other devices,
such as files, printers, etc. Table 2.5 summarizes the available options.
When the command a.out is entered alone from the keyboard, all the output is displayed
on the screen. However, if the command .a.out > result.dat is used, the output is
redirected to an external file named result.dat, and nothing is shown on the screen. The
contents of result.dat can be viewed using the more command.
$ gcc prog.c
$ ./a.out > result.dat
$ more result.dat
25 Note that this feature is dependent on the operating system and is not a property of the C language.
60 2 Components of C Language
If the program requires input from an existing external file and the output needs to be
saved to another external file, both input and output redirections can be specified on the
same command line as follows:
$ gcc prog.c
$ ./a.out < data.dat > result.dat
$ more result.dat
The file data.dat contains the necessary input data for the program.
The following command, when executed in a DOS window, creates a file named
filelist.dat that lists all the files in the current directory.26
I/O redirection is dependent on the operating system (e.g., UNIX and DOS) and is only
available when the C program is run from a command line. It is not possible to use I/O
redirection when the C program is executed within a graphical user interface (GUI).
To write to or read from an external file within a C program, the file must first be opened
and then closed after completing the necessary operations.
To open and close a file, the functions fopen() and fclose() must be used in
conjunction with the special keyword FILE (note the uppercase),27 as shown in the following
syntax. The file variable fp is a pointer (discussed in Sect. 2.9).
26 In the Windows system, the name prn cannot be used for an external file, as it is reserved for a
printer device.
27 This is a FILE pointer that keeps track of the file’s memory location.
2.8 File Handling 61
#include <stdio.h>
int main()
{
FILE *fp;
fp = fopen("filename","w");
/*
Write something to fp.
*/
fclose(fp);
return 0;
}
#include <stdio.h>
int main()
{
FILE *fp;
fp = fopen("data.dat","w");
fprintf(fp, "Hello, World!\n");
fclose(fp);
return 0;
}
The following program reads three floating-point numbers separated by spaces from an
external file named data.dat and prints their values to the screen.
#include <stdio.h>
int main()
{
FILE *fp; float a,b,c;
fp = fopen("data.dat", "r");
fscanf(fp,"%f %f %f", &a, &b, &c);
printf("%f %f %f", a, b, c);
fclose(fp);
return 0;
}
62 2 Components of C Language
#include <stdio.h>
int main()
{
FILE *fp1, *fp2;
float a, b, c;
fp1 = fopen("data1.dat","w");
fp2 = fopen("data2.dat","w");
fprintf(fp1,"This is the first file.\n");
fprintf(fp2,"This is the second file.\n");
fclose(fp1); fclose(fp2);
return 0;
}
2.9 Pointers
The concept of pointers in C is often considered one of the most challenging topics for
learners. While pointers are a fundamental feature in both C and C++, they are not present
in many other programming languages.28 When pointers are extensively used, your code
will exhibit characteristics typical of C programming.
Pointers are primarily necessary in specific scenarios such as (1) working on arrays, (2)
handling matrices and vectors in linear algebra and (3) implementing functions that require
variables to be passed by reference, which will be discussed in this section.
However, understanding pointers requires learning only two new operators: & (address-
of) and * (dereference).
Pointers are closely tied to the underlying hardware of the computer executing a C program.
When a C compiler processes source code, it maps each defined variable to a specific location
in RAM that stores the variable’s value. For example, consider the following program:
#include <stdio.h>
int main()
{
float a = 20.0, b = 50.0;
float *pa, *pb;
28 Languages such as Java, Python, and JavaScript avoid direct pointers for safety.
2.9 Pointers 63
.. . . 101 .. . .
a 102 20.0
.. . . .. . . .. . .
b 150 50.0
.. . . 151 .. . .
.. . . .. . . .. . .
pa 200 102
.. . . .. . . .. . .
pb 220 150
pa = &a; pb = &b;
return 0;
}
When the compiler processes the program, it generates a memory map similar to the
one shown in Table 2.6. Note that this representation is based on a hypothetical machine
for illustrative purposes. In this hypothetical machine, the variable a is mapped to memory
location 102 in RAM, which holds the value 20.0. Similarly, the variable b is mapped to
memory location 150, which holds the value 50.0.
To determine the memory address of a variable, you can use the & (ampersand) operator,
also known as the address-of or referencing operator. For instance, in the example above, a
represents the value 20.0, while &a represents the memory address 102.
Run the following program:
#include <stdio.h>
int main()
{
int a = 10;
printf("Address of a = %d.\n", &a);
return 0;
}
The compiler generates a warning, and the output from a.out is incorrect, displaying
a negative value.
64 2 Components of C Language
$ gcc prog.c
prog.c: In function ‘main’:
prog.c:5:10: warning: format ‘%d’ expects argument of type ‘int’,
but argument 2 has type ‘int *’ [-Wformat=]
printf("Address of a=%d.\n", &a);
ˆ
$ ./a.out
Address of a = -1074704200.
This issue arises because &a holds the address of a memory location, which may exceed
the maximum integer value that the compiler can handle (2147483647).
To display a memory address correctly in hexadecimal format, use the %p format specifier
instead of %d.29 Update the printf() function in the previous code by replacing %d with
%p. The output will be machine-specific but will appear similar to the following:
$ gcc prog.c
$ ./a.out
Address of a = 0xbfa1e9a8.
#include <stdio.h>
int main()
#include <stdio.h>
{
int main()
int a[] = {100, 2, -56};
{
printf("%p\n", &a[0]);
double a[] = {100, 2, -56};
printf("%p\n", &a[1]);
printf("%p\n", &a[0]);
printf("%p\n", &a[2]);
printf("%p\n", &a[1]);
return 0;
printf("%p\n", &a[2]);
}
return 0;
}
The output from the two programs may vary depending on the machine. An example of
the output is shown below:
$ gcc prog1.c
$ ./a.out $ gcc prog2.c
0xbfc76660 $ ./a.out
0xbfc76664 0xbf988c50
0xbfc76668 0xbf988c58
0xbf988c60
29 In hexadecimal format, numbers are represented using the digits 0-9 and the letters a-f.
2.9 Pointers 65
In the first (left) program, the array a[] is declared as an integer array, while in the
second (right) program, a[] is declared as a double-precision array.
In the output from the first (left) program, the addresses of adjacent elements are separated
by 4 bytes, indicating that the C compiler stores each integer (int) using 4 bytes.
On the other hand, in the output from the second (right) program, the addresses are
separated by 8 bytes, which implies that the C compiler stores each double-precision number
(double) using 8 bytes.
A pointer is a variable just like a or b above. The key difference is that it stores the “address
of another variable.” Therefore, if pa is a pointer, the content of pa is not a regular number
such as 20.0 or 50.0, but rather a large value such as 0xbf988c50 as shown in the examples
above.
To declare a pointer variable, you must use the same syntax as for any regular variable,
with the addition of an asterisk (*) preceding the variable name. For example:
#include <stdio.h>
int main()
{
float a = 20.0;
float *pa;
pa=&a;
printf("%p\n", pa);
printf("%p\n", &a);
return 0;
}
$ gcc prog.c
$ ./a.out
0xbfb0f214
0xbfb0f214
In the program above, float *pa declares that pa is a pointer pointing to a float
variable. It is important to note that a pointer itself always holds a large integer value
representing a memory address. Thus, the type of a pointer (such as float in the example
above) indicates the type of the variable the pointer pa refers to. The statement pa=&a;
assigns the address of a to pa.
66 2 Components of C Language
At times, you may want to examine the value that the pointer actually refers to, i.e., the
content of the variable pointed to by the pointer. Consider the following program:
#include <stdio.h>
int main()
{
float a = 20.0;
float *pa;
pa=&a;
printf("%f\n", *pa);
printf("%f\n", a);
return 0;
}
$ gcc prog.c
$ ./a.out
20.000000
20.000000
In the program above, the asterisk (*), known as the de-referencing operator, is used
before a pointer variable. It allows you to access the value stored in the memory location to
which the pointer points. Thus, if pa is a pointer to a floating-point variable a, using a and
*pa within the program is equivalent.
This approach enables you to modify the content of a without directly referencing a
itself.
Pointers can be incremented or decremented like any other variables. The key difference
is that the amount by which a pointer is incremented depends on the type of the variable it
points to. For example:
#include <stdio.h>
int main()
{
float a[3] = {1.0, 2.0, 3.0}, *pa = &a[0];
double b[3] = {1.2345670, 2.009876555, 3.14159265}, *pb = &b[0];
printf("float %15p%15p%15p\n", pa, pa + 1, pa + 2);
printf("double %15p%15p%15p\n", pb, pb + 1, pb + 2);
return 0;
}
$ gcc prog.c
$ ./a.out
float 0xbf8135e4 0xbf8135e8 0xbf8135ec
double 0xbf8135f0 0xbf8135f8 0xbf813600
In the program above, a double-precision variable occupies more memory space (8 bytes)
compared to a single-precision variable (4 bytes). Therefore, incrementing the double-
precision pointer, pb, advances the memory location by 8 bytes, whereas incrementing
the single-precision pointer, pa, advances the memory location by 4 bytes, even though the
increment for both pointers is “1.”
The C language utilizes pointers extensively for the following reasons:
• Pointers are the only way to modify the contents of arguments in a function call.
• Pointers allow direct control of computer hardware.
• Matrices and vectors in linear algebra can be represented using pointers.
One use of pointers is to modify the content of a variable (parameter) passed through a
function call. For example, suppose you want to write a function, tentimes(), that accepts
a variable, a, as a parameter and multiplies a by 10. The program might look like this:
#include <stdio.h>
void tentimes(float a)
{
a = 10.0 * a;
}
int main()
{
float b = 20;
tentimes(b);
printf(" b = %f\n", b);
return 0;
}
$ gcc prog.c
$ ./a.out
b = 20.000000
68 2 Components of C Language
Unfortunately, this program does not work as intended. The content of b remains
unchanged despite the statement a = 10.0*a in the definition of tentimes().
There is nothing inherently wrong with this program; it operates as intended according
to how function calls work in C.
When a function is called with an argument (e.g., tentimes(b)), a copy of the argu-
ment’s value (i.e., b) is made and passed to the function. As a result, the original argument
variable b remains unaltered.
The function tentimes() only has access to the copied value of the parameter (e.g.,
20) and does not interact with the variable b itself. As a result, modifying the argument’s
value within the function does not affect the original variable.
This method of passing parameters in C functions is known as the call by value method.
One solution is to use pointers. Instead of passing a copy of the variable’s value to the
function, you can pass the address of the variable. This allows the function to access the
memory location where the variable is stored and modify its content directly. This method
is known as call by reference. The program can now be modified as follows:
#include <stdio.h>
void tentimes(float *a)
{
*a = 10.0**a;
}
int main()
{
float b = 20;
tentimes(&b);
printf(" b = %f\n", b);
return 0;
}
$ gcc prog.c
$ ./a.out
b = 200.000000
The output is as expected. Note that the variable a is declared as a pointer (*a), and
instead of passing b, the address of b (&b) is passed to the function tentimes().
Within the function tentimes(), *a represents the value of the variable pointed to by
30
a. To illustrate this concept, the following program features a function, swap(), which
30 10.0**a computes the product of 10.0 and the value pointed to by a, which is *a.
2.9 Pointers 69
takes two pointers as arguments and exchanges the values of the variables pointed to by
these pointers.
#include <stdio.h>
void swap(float *pa, float *pb)
{
float tmp;
tmp = *pa;
*pa = *pb;
*pb = tmp;
}
int main()
{
float a = 10.0, b = 50.0;
printf("Old a = %f and old b = %f\n",a,b);
swap(&a,&b);
printf("New a = %f and new b = %f\n", a,b);
return 0;
}
$ gcc prog.c
$ ./a.out
Old a = 10.000000 and old b = 50.000000
New a = 50.000000 and new b = 10.000000
Matrices and vectors in linear algebra are closely associated with the use of pointers. When
an array, a[3], is declared as
#include <stdio.h>
int main()
{
float a[3] = {1.0, 2.0, 3.0};
return 0;
}
70 2 Components of C Language
the C compiler interprets a as a pointer and allocates the corresponding memory space. In
the program above, the array a acts as a pointer to the 0-th element of the array, a[0],
while a[0], a[1], and a[2] behave like regular variables. The value of a is the address
of a[0]. Since a is a pointer, dereferencing the array name (*a) will yield the 0-th element
of the array, i.e., a[0]. This provides us with several equivalent notations for accessing
arrays.
In Table 2.7, *(a+2) indicates that the pointer a is advanced by two units, and the
content stored at that address is accessed. This is equivalent to the value of a[2].
Since an array is a pointer, it is possible to pass an array to a function and modify its
elements. Here is an example:
#include <stdio.h>
void twice(float *a)
{
int i;
for (i = 0; i < 3; i++) a[i] = 2 * a[i];
}
int main()
{
float b[3] = {1.0, 2.0, 3.0};
int i;
twice(b);
for (i = 0; i < 3; i++) printf("%f\n", b[i]);
return 0;
}
$ gcc prog.c
$ ./a.out
2.000000
4.000000
6.000000
The program above demonstrates a function that takes an array as input and doubles all
its elements. It was previously noted that the only way to modify an argument in a function
is by using a pointer. This principle applies to arrays as well, since the name of an array
is a pointer. Therefore, when passing an array to a function, the address operator (&) is not
needed before the array name. The following program achieves the same result as the one
above.
#include <stdio.h>
void twice(float a[3])
{
int i;
for (i = 0; i < 3; i++) a[i] = 2 * a[i];
}
int main()
{
float b[3] = {1.0, 2.0, 3.0};
int i;
twice(b);
for (i = 0; i < 3; i++) printf("%f\n", b[i]);
return 0;
}
There is a special type of pointer that can point to a function instead of a variable. This is
called a function pointer. Using a function pointer, it is possible to have a variable represent
different functions. This is particularly useful in scenarios where an operation (such as
numerical integration) must be performed on multiple functions.
Without a function pointer, the program would need to repeat the operations as many
times as there are different functions. However, with a function pointer, the program can
be designed such that the function’s name can be treated like a variable name, allowing the
substitution of different function names as needed.
When a function is declared, such as float myfunc(), the function name, myfunc,
is actually a pointer to the memory location where the function’s code begins.31
A function pointer can be declared as
type_of_function (*func_name)(type_of_argument)
31 This is similar to an array, where the array name itself is a pointer to the address of the first element.
72 2 Components of C Language
where type_of_function is the return type of the function to which the pointer refers,
func_name is the name of the function pointer, and type_of_argument represents
the type of the argument(s) for the function.
For example, a function pointer declared as float (*foo)(float, float) can
point to a function defined as float f1(float x, float y).
In the following code, func() is a function pointer that points to an actual function
returning a double type and taking a double type variable as an argument.
#include <stdio.h>
#include <math.h>
double f1(double x)
{
return x ;
}
double f2(double x)
{
return x*x ;
}
int main()
{
double (*func)(double);
func = &f1;
printf("%f\n", func(2)) ;
func = &f2;
printf("%f\n", func(2));
func = &cos;
printf("%f\n", func(3.141));
return 0;
}
Since func() is a function pointer, a statement such as func = &cos assigns the
address of the cosine function defined in math.h to func. After this assignment, func()
and cos() become equivalent.
2.9 Pointers 73
2.9.6 Summary
• Pointers must be used if the values of arguments in a function need to be modified. This
is why scanf() requires the address operator &, whereas printf() does not. The
value of the variable passed to scanf() is entered from the keyboard, so it is necessary
to pass the address of that variable to scanf() to allow modification of the variable’s
content.
2.9.7 Exercise
1. Write a function, circle, that takes the radius of a circle as input and assigns the
area of the circle to the variable area and the perimeter of the circle to the variable
perimeter. Since the function needs to modify these variables, pointers should be
used. Complete the following template:
#include <stdio.h>
void circle(float r, (fill in your code))
{
(fill in your code...);
}
int main()
{
float r, area, perimeter;
printf("Enter radius = "); scanf("%f", &r);
circle(r, &area, &perimeter);
printf("r = %f area = %f peri = %f\n", r, area, perimeter);
return 0;
}
74 2 Components of C Language
2. Write a function that takes two floating-point variables, .a and .b, and rearranges them in
ascending order:
#include <stdio.h>
void reorder(float *pa, float *pb)
{
(your code here)
}
int main()
{
float a = 15, b = -6;
reorder(&a, &b);
printf("%f %f\n", a, b);
return 0;
}
-6 15
#include <stdio.h>
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int i;
for (i = 0; i < 5; i++) printf("%d\n", a[i]);
for (i = 0; i < 5; i++) printf("%d\n", *(a+i));
return 0;
}
Since a[i] and *(a + i) are equivalent, use this concept to write a program that
computes the average of all the elements in the following array:
#include <stdio.h>
int main() #include <stdio.h>
{ int main()
float a = 2.0; {
printf("%f\n",a); char b = ’A’;
return 0; printf("%c\n",b);
} return 0;
}
In the program on the left, the float variable a represents a single value, .2.0. In the
program on the right, the char variable c represents a single character, A.
#include <stdio.h>
int main() #include <stdio.h>
{ int main()
float a[] = {2.0, 3.0, 4.0, 5.0}; {
printf("%f\n",a[0]); char b[] = "Hello!";
return 0; printf("%c\n",b[0]);
} return 0;
}
In the program on the left, the array a represents a set of four numbers: 2.0, 3.0,
4.0, 5.0, and the first number is printed. In the program on the right, the array b represents
a string of six characters: “Hello!”, and the first character H is printed.
76 2 Components of C Language
#include <stdio.h>
int main() #include <stdio.h>
{ int main()
float *a; {
a[0] = 1.0; a[1] = 2.0; char *a;
printf("%f\n",a[0]); a = "HELLO!";
return 0; printf("%c\n",a[0]);
} return 0;
}
In the program on the left, the pointer variable a points to the address of a[0]. In the
program on the right, the pointer variable a points to the address of the first character, “H”.
Instead of using individual assignments such as *a = {’H’, ’E’,’L’,
’L’,’O’,’!’} or a[0] =’H’; a[1] =’E’ and so on, a direct assignment of a
="HELLO!" can be used with double quotation marks (").
Use the %s format specifier to print the entire string, rather than the %c format specifier,
which represents only a single character.
#include <stdio.h>
int main()
{
char *a;
a = "Hello, World!";
printf("%s\n",a);
return 0;
}
$ gcc prog.c
$ ./a.out
Hello, World!
As shown in the examples above, single quotation marks (’) and double quotation marks
(") serve different purposes. Single quotation marks are used for single characters, while
double quotation marks are used for strings of characters. For example, "ABC" (double
quotation marks) represents a string of characters and thus is an array (or pointer). In contrast,
’A’ (single quotation marks) represents a single character.
Consider the following program:
2.10 String Manipulation 77
#include <stdio.h>
int main()
{
char s[4] = "ABC";
printf("%s\n", s);
return 0;
}
You might wonder why the number of elements in the array s is 4 rather than 3. The
C compiler automatically appends a special character, NULL (ASCII code 0, commonly
denoted as “.\0”), to the end of the string to mark the end of the character array. As a result,
the number of elements in a character array is always one more than the number of characters
in the string.
To read a string from standard input (i.e., the keyboard), refer to the following example.
#include <stdio.h>
int main()
{
char str[100];
printf("Enter a word = ");
scanf("%s", str);
printf("%s\n",str);
return 0;
}
$ gcc prog.c
$ ./a.out
Enter a word = Good morning
Good
Note that there is no “&” before str in the scanf() function, as str is already a
pointer. Note that only “Good” is printed even though “Good morning” was entered.
This occurs because the format specifier %s in scanf() reads input only up to the first
space (i.e., a single word). To read two strings (or two words), use “%s %s”. The C compiler
automatically adds “\0” at the end of the string to indicate its termination.
78 2 Components of C Language
To copy a string to another string or to compare one string against another, it is best to use
the functions strcpy() and strcmp(), which are available in string.h.
#include <stdio.h>
#include <string.h>
int main()
{
char c1[] = "ABCDE", c2[6];
strcpy(c2, c1);
printf("%s\n", c2);
return 0;
}
$ gcc prog.c
$ ./a.out
ABCDE
In the program above, the function strcpy(c2, c1) copies the string pointed to by
c1 to the string pointed to by c2. Note that c2 should have space for an extra character
(i.e., c2[6] instead of c2[5]) to accommodate the NULL character added at the end of
the string.
To compare two strings, use strcmp(), which is available in string.h.
#include <stdio.h>
#include <string.h>
int main()
{
char s[100];
printf("Enter \"ABCDEF\"");
scanf("%s", s);
if (strcmp(s, "ABCDEF") == 0)
printf("ABCDEF was entered correctly.\n");
else
printf("Wrong. %s was entered.\n",s);
return 0;
}
2.11 Command Line Arguments 79
In the program above, the strcmp() function takes two strings as arguments and returns
0 if the strings are identical. Note that you can output a double quotation mark (") by
“escaping” it with the backslash character.
To find the length of a string, use the strlen() function.
#include <stdio.h>
#include <string.h>
int main()
{
char c[50];
printf("Enter string = ");
scanf("%s", c);
printf("You entered %s\n", c);
printf("Its length is %d\n", strlen(c));
return 0;
}
$ gcc prog.c
$ ./a.out
Enter string = Good afternoon.
You entered Good
Its length is 4
Again, only “Good” was read because the format specifier %s reads a string only up to
the first space (i.e., a single word).
The conventional method for executing a C program after successful compilation involves
entering the program’s name at the system prompt, as demonstrated in the following example.
Instead of providing the necessary information during an interactive session (using the
scanf() and printf() functions), you can specify the required parameters when enter-
ing the program name, as shown below:
This is known as “command line arguments” (also referred to as “command line parame-
ters”) , and it provides a convenient method for passing necessary arguments to the main()
function without requiring user interaction.
Recall that the function main() is the entry point of a C program. Apart from being
the initial function executed, it is an ordinary C function, similar to any other functions, and
thus must include a parameter list.
The main() function can actually take two arguments. The first argument represents the
number of command line arguments, including the program name itself. The second argu-
ment, an array of strings, contains each command line argument entered after the program
name. The syntax for the parameters in main() is as follows:
The first argument, argc, is an integer that represents the number of command line
arguments, including the program name itself.32 The second argument, argv, is a pointer
to an array of strings that contains each of the command line arguments. It is important
to note that argv[] is an array, which allows it to accommodate multiple command line
arguments.33
Consider the following program:
#include <stdio.h>
int main(int argc, char *argv[])
{
int i;
printf("Number of arguments = %d\n", argc);
for (i = 0; i < argc; i++) printf("%d: %s\n", i, argv[i]);
return 0;
}
Run the program above with command line arguments as demonstrated in the following
example:
$ gcc prog.c
$ ./a.out I like C language.
Number of arguments = 5
0: ./a.out
1: I
2: like
3: C
4: language.
The string “./a.out” is stored in argv[0], and the string “I,” is stored in argv[1],
and so on.
Note that each command line argument is entered as a string. For example, when the
number 4 is entered, it is stored as the character “4” (ASCII code 52), not as the numeric
value 4. To interpret the entered parameters as numeric values rather than as a string of
characters, use the functions atoi() (ASCII to INTEGER) or atof() (ASCII to FLOAT)
available in stdlib.h.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
printf("%d\n", atoi(argv[1]));
return 0;
}
$ gcc prog.c
$ ./a.out 2018
2018
The following program calculates the sum of all numbers provided as command line
arguments.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
82 2 Components of C Language
{
float sum = 0.0; int i;
for (i = 1; i < argc; i++) sum = sum + atof(argv[i]);
printf("The sum is %f.\n" , sum);
return 0;
}
$ gcc prog.c
$ ./a.out 1 2 3 4 5 6 7 8 9 10
The sum is 55.000000.
2.11.2 Exercise
1. Write a program that uses command line arguments to solve a quadratic equation of the
form .ax 2 + bx + c = 0. The coefficients .a, .b, and .c should be provided as command
line arguments. For example, executing
$ ./a.out 3 -1 -1
will output
x1 = -0.434259, x2 = 0.767592
will output
2.12 Structures
2.12.1 Mixture of Different Types of Variables
An array is a single variable that can store multiple elements of the same type. However, if
you need a single variable to represent different types of elements, such as a mix of integers,
floating-point numbers, and strings, you can use a structure.
A structure is a collection of one or more variables grouped under a single name. These
variables can be of different types and are accessed by their individual names. Structures
are a convenient way to group together several related pieces of information.
For example, a structure called student can be defined to represent a student’s school
records, including the ID number, midterm score, final score, and final grade. The following
program illustrates the concept of structures:
#include <stdio.h>
struct student
{
char *ID;
int Midterm;
int Final;
char Grade;
};
int main()
{
struct student smith = {"1000123456", 89, 98, ’A’},
doe = {"1000123457", 45, 53, ’F’};
printf("%s\n", smith.ID);
printf("%d\n", doe.Midterm);
doe.Grade = ’D’;
printf("%c\n", doe.Grade);
return 0;
}
$ gcc prog.c
$ ./a.out
1000123456
45
D
In the program above, a structure called student is defined with four members: ID,
Midterm, Final, and Grade, which encompass different types. The member ID is a
string (thus, a pointer), while Midterm and Final are integers, and Grade is a character.
84 2 Components of C Language
Two variables, smith and doe, are declared as type student and initialized accordingly.
Members of a structure are accessed using the dot (.) operator.
You can also define an array of structures as
#include <stdio.h>
struct student
{
char *ID;
int Midterm;
int Final;
char Grade;
};
int main()
{
struct student myclass[15];
int i;
myclass[0].ID = "10000123212";
myclass[0].Grade = ’C’;
(.....)
myclass[14].Grade = ’B’;
(.....)
return 0;
}
#include <stdio.h>
struct student
{
char *ID;
int Midterm;
int Final;
char Grade;
};
int main()
{
struct student Smith = {"David Smith", 12, 45, ’F’}, *ptr;
ptr = &Smith;
/*
..................
*/
Note that members of a structure can be accessed using the .-> operator. Using pointers
within structures facilitates dynamic memory allocation, as discussed in Sect. 2.12.3, and
enhances the efficiency of function calls.
Finally, with the typedef keyword, you can define a structure and declare variables
of that structure type in a manner similar to int or float, without needing to use the
struct prefix.
#include <stdio.h>
typedef struct
{
char *ID;
int Midterm;
int Final;
char Grade;
} student;
int main()
{
student Jones = {"Jones", 12, 45, ’F’}, *ptr;
ptr = &Jones;
/*
..................
*/
The typedef keyword is used to create a new name (alias) for an existing data type.
The syntax is
For example,
In this example, real is a new name for float. This makes the code more concise and
readable.
86 2 Components of C Language
#include <stdio.h>
typedef struct
{float Real; float Im;} Complex;
The output is
$ gcc prog.c
$ ./a.out
Enter real and imaginary parts of z1 separated by space = 2 3
Enter real and imaginary parts of z2 separated by space = -1 4
1.000000 + 7.000000 I
By using typedef, the structure Complex can be treated similarly to int or float,
allowing variables to be declared as Complex z1, z2;.
2.12.2 Exercise
1. Based on the previous example, write a program to perform division of two complex
numbers, i.e., .z 1 /z 2 . Use the program to compute .z 1 /z 2 , where .z 1 = 2.12 + 1.21i and
. z 2 = −2.8 + 7.8i.
As a reference, the following program computes the product of two complex numbers35 :
#include <stdio.h>
typedef struct
{float Real; float Im;} Complex;
$ gcc prog.c
$ ./a.out
The product of z1 * z2 = 5.443686 + -2.664626 I.
When an array, a, is declared, the compiler automatically allocates the memory space based
on the number of elements and type of a. In an example below, the size of an array, a, must
be declared at the time of compilation.
#include <stdio.h>
#define N 1000
int main()
{
float a[N]; int i;
for (i = 0; i < N; i++) a[i] = 1.0/(i + 1);
return 0;
}
This is known as static memory allocation, where the size of the memory is fixed prior
to use. Once the code is compiled, the size of the array or the memory portion occupied
by a cannot be changed or released. However, in many cases, the size of an array may not
be known in advance, or it may be necessary to release memory previously allocated to an
array to make room for additional memory.
Dynamic memory allocation provides the flexibility to allocate memory at runtime, allow-
ing for more efficient use of memory resources. Memory allocated dynamically can vary in
size based on the program’s requirements.
The function malloc()36 is used for dynamic memory allocation. It stands for “memory
allocation” and is declared in the.<stdlib.h> header file. The typical usage of malloc()
is as follows:
where .n represents the number of array elements and size_type is the size of the type of
the array variable in bytes. For instance, size_type is 4 for integers and float numbers and
8 for double-precision numbers. The sizeof function can be used to determine this size.
For example, sizeof(int) returns 4, and sizeof(double) returns 8. The function
malloc() returns a void pointer37 (void *) to the allocated memory if successful, or
NULL if the allocation fails.
#include <stdio.h>
#include <stdlib.h>
int main()
{
float *ptr; int i;
ptr = malloc(500 * sizeof(float));
for (i = 0; i < 500; i++) ptr[i] = 1.0/(i + 1);
printf("%f %f\n",ptr[0], ptr[499]);
free(ptr);
return 0;
}
In the example above, memory space for 500 float numbers (2,000 bytes) is allocated.
The top address of that memory block is assigned to the pointer ptr. Since a pointer and an
array are essentially equivalent, ptr[i] accesses the float number stored at the i-th offset
from ptr. The free(ptr) statement releases the memory space occupied by ptr.
A more robust example is provided below:
#include <stdio.h>
#include <stdlib.h>
int main()
{
float *ptr; int i;
ptr = (float*)malloc(500 * sizeof(float));
if (ptr==NULL)
{
printf("No more memory space !\n"); exit(0);
}
for ( i=0; i < 500; i++) ptr[i] = 1.0/(i + 1);
printf("%f %f\n",ptr[0], ptr[499]);
free(ptr);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i, size;
double *vector;
free(vector);
printf("Memory has been successfully freed.\n");
return 0;
}
$ gcc malloc_sample.c
$ ./a.out
Enter the size of double precision array =100000000
Vector first element: 0.000000
Vector last element: 49999999.500000
Memory has been successfully freed.
$ ./a.out
Enter the size of double precision array =1000000000
Memory allocation failed!
The output indicates that the program can successfully allocate and handle .100,000,000
(one hundred million) double-precision components, displaying the first and last elements
of the array. However, when attempting to allocate one billion components, the program
encounters an error message due to insufficient memory.
The following two additional functions are available for memory management:
2.12 Structures 91
In this example, memory for 10 integers is allocated, the starting address is assigned to
arr, and all integers are initialized to 0. If the initialization is not required, malloc()
can be used.
2. realloc() The realloc() function changes the size of an existing memory block
allocated by malloc() or calloc(). It can either expand or shrink the block, and if
necessary, move it to a new location. An example is:
In this example, memory for 1,000 integers is allocated, and the starting address is
assigned to arr. The values previously assigned to arr[i] are preserved, while any
newly allocated elements are initialized to 0.
Dynamic memory allocation is useful when dealing with large arrays of varying sizes
with limited RAM.
Part II
Numerical Analysis
Now that the basic syntax of the C language has been explained, you are equipped to
write C programs to address numerous problems in engineering and science.
In Part II, we will discuss numerical methods for solving non-linear equations, sets
of simultaneous equations, and ordinary differential equations, as well as techniques for
numerical differentiation and integration of functions.
Solving these equations analytically often requires advanced mathematical skills.
However, numerical solutions can frequently be obtained through intuitive or visual
interpretation, without the need for higher-level mathematics.
While all essential topics in numerical analysis are covered, it is beyond the scope of
this text to address every aspect of the field. For a comprehensive reference, Numerical
Recipes in C [6] is recommended.
Note on Numerical Errors
3
In Part I, the data type float was used for all real numbers, with 4 bytes allocated for each
floating-point number. A float variable can represent values ranging from .10−38 to .1038 ,
which covers most practical needs.
However, this range translates to a precision of only 6 to 8 decimal digits, which is
insufficient for many scientific and engineering problems that require higher precision.
Consider the following examples:
1. #include <stdio.h>
int main()
{
float s = 0.0; int i;
for (i = 0; i < 10000; i++) s = s + 0.1;
printf("%f\n",s);
return 0;
}
The program is intended to add .0.1 a total of .10,000 times. The expected result is .1,000.
However, the program outputs the following result:
$ gcc prog.c
$ ./a.out
999.902893
The output is not .1,000 but rather .999.902893. While this result is close to .1,000, it is
not acceptable when precision is critical.
The error in this example arises from the conversion between decimal and binary num-
bers. Decimal numbers in the source code are converted to binary with a potential con-
version error, and binary numbers are converted back to decimal, introducing additional
conversion errors.1
2. #include <stdio.h>
int main()
{
float a, b, c;
a = 123.45678;
b = 123.45655;
printf("%f\n",a - b);
return 0;
}
As this program subtracts 123.45655 from 123.45678, the result should be 0.00023.
However, the output from the program is not what it is supposed to be.
$ gcc prog.c
$ ./a.out
0.000229
The output is not .0.00023 but .0.000229. Although this error may seem small, it is not
acceptable when high precision is required. This discrepancy arises from subtracting
one number from another very close in value, resulting in the loss of significant figures,
commonly known as cancellation error.
Both types of errors are inevitable, and it is impossible to completely eliminate them.
However, their impact can be minimized by using the double data type instead of float
for floating-point numbers.
When a number is declared as a double, it is allocated 8 bytes, which effectively
increases the valid range and precision. While the range of a float variable is .±10−38 ∼
1038 with seven significant digits, a double variable provides a range of .±10−308 ∼ 10308
with 15 significant digits.
The format specifier for double is %lf (long float). Use %lf in scanf() when reading
double-precision values. However, for printf(), there is no difference between %lf and
%f, as they are functionally equivalent for compatibility reasons.
1 For example, converting the decimal .0.1 to binary results in the recurring binary
.0.0001100110011001100, and converting this binary number back to decimal yields .0.0999985.
3 Note on Numerical Errors 97
1. #include <stdio.h>
int main()
{
double s = 0.0; int i;
for (i=0; i < 10000; i++) s = s + 0.1;
printf("%f\n",s);
return 0;
}
$ gcc prog.c
$ ./a.out
1000.000000
2. #include <stdio.h>
int main()
{
double a,b,c;
a = 123.45678;
b = 123.45655;
printf("%f\n",a - b);
return 0;
}
$ gcc prog.c
$ ./a.out
0.000230
Another example of cancellation error is found in the seemingly simple quadratic equation:
.ax 2 + bx + c = 0,
. x 2 + 200000x − 3 = 0,
. x1 = −200000, x2 = 0.000015.
98 3 Note on Numerical Errors
#include <stdio.h>
#include <math.h>
int main()
{
float a, b, c, disc,x1, x2;
a = 1.0; b = 200000; c = -3;
disc = b*b - 4*a*c;
x1 = (-b - sqrt(disc))/(2*a);
x2 = (-b + sqrt(disc))/(2*a);
printf("x1 = %f, x2 = %f\n",x1, x2);
return 0;
}
On
√ the other hand, .b is .200000.0. A cancellation error occurs when .b is subtracted from
. D due to their close proximity. This issue can be mitigated by using double data type,
as demonstrated:
#include <stdio.h>
#include <math.h>
int main()
{
double a, b, c, disc, x1, x2;
a = 1.0; b = 200000; c = -3;
disc = b*b - 4*a*c;
x1 = (-b - sqrt(disc))/(2*a);
x2 = (-b + sqrt(disc))/(2*a);
printf("x1 = %f, x2 = %f\n",x1, x2);
return 0;
}
3 Note on Numerical Errors 99
In this chapter, we explore numerical solutions for a single equation of the form . f (x) = 0.
The function . f (x) can be a polynomial or any other non-linear function of .x.
The Fundamental Theorem of Algebra [8] states that an .nth order polynomial equation
has .n roots, which may include complex roots. However, this does not imply that all roots
of polynomial equations can be expressed analytically in closed form. In fact, there is no
closed-form solution for the roots of polynomial equations of degree five or higher.1
This chapter describes two important algorithms for numerically solving the equation
. f (x) = 0:
The bisection method is based on the Mean Value Theorem, which states:
If . f (x) is continuous over the interval .[x1 , x2 ], . f (x) exists for .x1 < x < x2 , and
. f (x 1 ) f (x 2 ) < 0, then there is at least one point . x in the interval .[x 1 , x 2 ] such that . f (x) = 0.
As illustrated in Fig. 4.1, if there is at least one zero of . f (x) = 0 between .x1 and .x2 , then
the product . f (x1 ) f (x2 ) must be negative, indicating that the curve crosses the .x-axis.
1 This result, known as Galois theory, was established by the French mathematician Évariste Galois
(1811–1832), pronounced gal-wa, at the age of 19.
© The Author(s), under exclusive license to Springer Nature Switzerland AG 2025 101
S. Nomura, C Programming and Numerical Analysis, Synthesis Lectures on Mechanical
Engineering, https://doi.org/10.1007/978-3-031-83457-8_4
102 4 Roots of . f (x) = 0
Once such an interval .[x1 , x2 ] is identified, the next step is to bisect this interval and
test whether . f (x1 ) f (x2 ) < 0 holds with .x2 replaced by the midpoint .(x1 + x2 )/2. If
. f (x 1 ) f (midpoint) < 0, then the zero must be in the first subinterval .[x 1 , midpoint]. Other-
double f(double x)
{
return pow(x,2) - 2;
}
/* start of main */
int main()
{
double x1, x2, x3;
int count;
do
{
printf("Enter xleft and xright separated by space = ");
scanf("%lf %lf", &x1, &x2);
} while (f(x1)*f(x2) > 0);
/* bisection start */
for (count = 0; count < N; count++)
{
x3 = (x1 + x2)/2.0;
if (f(x1)*f(x3) < 0 ) x2 = x3; else x1 = x3;
if (f(x3) == 0.0 || fabs(x1-x2) < EPS ) break;
}
printf("iteration = %d\n", count);
printf("x= %f\n", x1);
return 0;
}
The program prompts the user to enter two points, .x1 and .x2 . If . f (x1 ) f (x2 ) > 0, the
program will prompt the user again until . f (x1 ) f (x2 ) < 0 is satisfied. Note the use of the
do {…} while{…} statement.
The function fabs(), available in math.h, returns the absolute value of its argument.
The output is displayed as follows:
Typically, the bisection method converges within 30 to 40 iterations for most equations.
To provide .x1 and .x2 as initial guesses, it is advisable to sketch a rough graph of . f (x)
using a graphical tool such as gnuplot (see Appendix A) to estimate an approximate
interval containing a root.
Although the bisection method may not be the fastest available, it guarantees that at least
one root will be found if the initial interval is selected correctly.
Newton’s method, also known as the Newton-Raphson method, is a widely used algorithm
for finding roots of the equation . f (x) = 0.
Newton’s method converges quadratically, and unlike the bisection method, it requires
only a single initial guess to start.
As illustrated in Fig. 4.3, Newton’s method begins with an initial guess .x1 , chosen as
close as possible to a root of . f (x) = 0. At the point .(x1 , f (x1 )), a tangent line is drawn to
approximate . f (x) linearly. The intersection of this tangent line with the .x-axis, denoted as
. x 2 , serves as the next approximation, which is expected to be closer to the root.
This iterative process continues until the method converges to a sufficient degree.
From Fig. 4.4, the equation of a straight line passing through .(a, b) with a slope of .m is
given by
. y − b = m(x − a).
The tangent line passing through the point .(x1 , f (x1 )) with a slope of . f (x1 ) is given by
f (x1 )
. x2 = x1 − .
f (x1 )
In general, the iterative formula
f (xn )
. xn+1 = xn − , (4.1)
f (xn )
can be used to obtain the .(n + 1)th approximation from the .nth approximation.
Starting with an initial guess .x1 that is sufficiently close to the root, compute subse-
quent approximations .x2 , .x3 , .x4 , …, using Eq. (4.1). Repeat this iteration until the absolute
difference .|xn+1 √
− xn | is smaller than a specified threshold.
For example, . 2 can be approximated by solving the equation . f (x) ≡ x 2 − 2 = 0. With
Starting with an initial guess of .x1 = 2.0, the iterations proceed as follows:
. x1 = 2.0,
f (2.0) 2.0
x2 2.0 − = 2.0 − = 1.5,
f (2.0) 4.0
f (1.5) 0.25
x3 = 1.5 − = 1.5 − = 1.41667,
f (1.5) 3.0
f (1.41667)
x4 = 1.41667 − = . . . = 1.4142.
f (1.41667)
As demonstrated, convergence is achieved after only 4 iterations.
The algorithm for Newton’s method is as follows:
f (x1 )
(a) Compute .x2 ← x1 − f (x1 ) .
(b) Update .x1 ← x2 .
Note that Newton’s method fails when . f (xn ) is zero, which results in a division by zero
in Eq. (4.1).
A C program for Newton’s method to solve .x 2 − 2 = 0 is shown below:
#include <stdio.h>
#include <math.h>
#define EPS 1.0e-10
double f(double x)
{
return x*x - 2;
}
double fp(double x)
{
return 2*x;
}
double newton(double x)
{
return x - f(x)/fp(x);
}
4.2 Newton’s Method 107
int main()
{
double x1, x2;
int i;
do
{
printf("Enter initial guess = ");
scanf("%lf", &x1);
} while (fp(x1) == 0.0);
√
This iteration√scheme can be used to approximate . a, even manually. For instance, to
approximate . 3 ≈ 1.732:
1 3
. x 1 = 1, x2 = 1+ = 2,
2 1
1 3 1 3
x3 = 2+ = 1.75, x4 = 1.75 + = 1.732.
2 2 2 1.75
Newton’s method can also be applied to solve simultaneous equations numerically [9].2 For
simplicity, consider the following system of two simultaneous equations:
. f (x, y) = 0,
g(x, y) = 0.
Expanding each equation using the Taylor series for functions of two variables, we obtain:
∂ f ∂ f
. f (x, y) ≈ f (x 0 , y0 ) + (x − x0 ) + (y − y0 ), (4.2)
∂ x (x0 ,y0 ) ∂ y (x0 ,y0 )
∂ g ∂ g
. g(x, y) ≈ g(x 0 , y0 ) + (x − x0 ) + (y − y0 ). (4.3)
∂x ∂y
(x0 ,y0 ) (x0 ,y0 )
If .(x, y) satisfies
. f (x, y) = 0, g(x, y) = 0,
Equations (4.2) and (4.3) can be written as
∂f ∂f
0 f (x0 , y0 ) , x − x0
. = + ∂∂ gx ∂y
∂g ,
0 g(x0 , y0 ) ∂x , ∂y (x0 ,y0 ) y − y0
or in vector-matrix form as
. 0 = f(x0 , y0 ) + J (x − x0 ), (4.4)
where
∂f ∂f
∂x ∂ y f (x0 , y0 ) x − x0
. J≡ ∂g ∂g , f(x0 , y0 ) = , x − x0 = .
∂x ∂ y (x0 ,y0 ) g(x0 , y0 ) y − y0
The matrix . J is known as the Jacobian matrix. Equation (4.4) can be solved for .x as
x = x0 − J −1 f(x0 ),
. (4.5)
where . J −1 denotes the inverse matrix of . J . Equation (4.5) represents the two-dimensional
extension of Eq. (4.1).
Example
Numerically solve the following system of simultaneous equations for .(x, y):
1
. x 3 + y 2 = 1, x y = .
2
Solution
Define
1
f ≡ x 3 + y 2 − 1, g ≡ x y − .
.
2
The Jacobian matrix . J is given by
2
3x 2y
.J = ,
y x
Thus,
⎛ ⎞
x 4 −x y 2 +1 +y
x
3x 3 −2y 2
− 3x 32y
−2y 2 x3 + −1 y2
. J −1 f = 3x 2 =⎝ 3x 3 −2y 2 ⎠.
y
− 3x 3 −2y x y − 21 4x 3 y−3x 2 −2y 3 +2y
2 3x 3 −2y 2 6x 3 −4y 2
#include <stdio.h>
#include <math.h>
int main()
{
double x = 1.0, y = 1.0;
int i, n;
printf("Enter x, y and # of iterations = ");
scanf("%lf %lf %d", &x, &y, &n);
for (i = 0; i < = n; i++)
{
x = x -(pow(x,4) + y - x*(1 + y*y))/(3*pow(x,3) - 2*y*y);
y = y -(-3*x*x + 2*y + 4*pow(x,3)*y -
2*pow(y,3))/(6*pow(x,3) - 4*y*y);
}
printf("%f %f\n", x, y);
return 0;
}
Starting with the initial guess .(x, y) = (1.0, 1.0), convergence was achieved at .(x, y) =
(0.877275, 0.569947) after only 4 iterations. Note that this solution represents just one of
the possible roots. To find other roots, it is necessary to use different initial guesses.
4.2.3 Exercise
e x − 3x = 0,
.
. x sin x = e x − x sin x 2 ,
5.1 Introduction
The graph of . f (x) is shown in Fig. 5.1. Our objective is to estimate . f (x) for each .x in
the table using difference approximations.
Graphically, differentiation corresponds to the slope or rate of change. For instance, to
approximate . f (2.0), consider the following two approaches:
1. By comparing . f (2.0) with . f (2.5), the rate of change is
1 www.wolframalpha.com.
© The Author(s), under exclusive license to Springer Nature Switzerland AG 2025 111
S. Nomura, C Programming and Numerical Analysis, Synthesis Lectures on Mechanical
Engineering, https://doi.org/10.1007/978-3-031-83457-8_5
112 5 Numerical Differentiation
There are three fundamental schemes for numerical differentiation that utilize three neigh-
boring points. Each of these schemes can be derived from the Taylor series expansion of
. f (x + h).
• Forward difference
In the forward difference scheme, the derivative of . f (x) is approximated by comparing
. f (x + h) and . f (x). The Taylor series expansion of . f (x) is given by
h 2 h 3
. f (x + h) = f (x) + h f (x) + f (x) + f (x) + · · ·
2! 3!
≈ f (x) + h f (x). (5.1)
By retaining only the first two terms of the Taylor series, the derivative . f (x) can be
approximated as
f (x + h) − f (x)
. f (x) ≈ . (5.2)
h
5.2 Forward/Backward/Central Difference 113
This method is known as the forward difference scheme. For instance, using Eq. (5.2), the
approximation of . f (2) from Table 5.1 is calculated as .( f (2.5) − f (2.0))/0.5 = 15.25.
• Backward difference
The backward difference scheme is derived by substituting .h with .−h in Eq. (5.1),
resulting in
h 2 h 3
. f (x − h) = f (x) − h f (x) + f (x) − f (x) + · · ·
2! 3!
≈ f (x) − h f (x). (5.3)
f (x) − f (x − h)
. f (x) ≈ . (5.4)
h
This method is known as the backward difference scheme. For example, using Eq. (5.4),
the approximation of . f (2) from Table 5.1 is .( f (2.0) − f (1.5))/0.5 = 9.25.
• Central difference
Equations (5.1) and (5.3) are restated as
h 2 h 3
. f (x + h) = f (x) + h f (x) +f (x) + f (x) + · · · , (5.5)
2! 3!
h 2 h 3
. f (x − h) = f (x) − h f (x) + f (x) − f (x) + · · · . (5.6)
2! 3!
Subtracting Eq. (5.6) from Eq. (5.5) yields
2h 3
. f (x + h) − f (x − h) = 2h f (x) + f (x) + · · · .
3!
By neglecting terms of order .h 3 and higher, . f (x) can be approximated as
f (x + h) − f (x − h)
. f (x) ≈ . (5.7)
2h
This is known as the central difference scheme.
f (2.5) − f (1.5)
. f (2) ≈ = 12.25.
2 × 0.5
Given that . f (x) in Table 3.1 is . f (x) = x 3 , it follows that . f (x) = 3x 2 and thus . f (2) = 3 ×
22 = 12. From the above analysis, it is evident that the central difference method provides
the most accurate approximation, as its truncation error is of the order .h 2 . In contrast, the
truncation errors for the forward and backward difference methods are of the order .h.
114 5 Numerical Differentiation
. f (x + h) + f (x − h) = 2 f (x) + h 2 f (x) + . . .
f (x + h) + f (x − h) − 2 f (x)
. f (x) ≈ . (5.8)
h2
Equation (5.8) provides a formula for approximating the second-order derivative of . f (x).
5.2.1 Example
Table 5.2 presents the numerical values of . f (x) (numerical values of .sin x from .x = 0.0 ∼
1.0).
The following code implements the central difference scheme:
#include <stdio.h>
#define N 11
int main()
{
double y[N] = {0, 0.0998, 0.1986, 0.2955, 0.3894, 0.4794, 0.5646,
0.6442, 0.7173, 0.7833, 0.8414};
double central[N], h = 0.1;
int i;
for (i = 1; i < N-1; i++)
central[i] = (y[i + 1] - y[i - 1])/(2*h);
printf (" x Central \n---------------------------\n");
for (i = 1; i < N - 1; i++)
printf ("%f %f\n", i*h, central[i]);
return 0;
}
$ gcc central.c
$ ./a.out
x Central
---------------------------
0.100000 0.993000
0.200000 0.978500
0.300000 0.954000
0.400000 0.919500
0.500000 0.876000
0.600000 0.824000
0.700000 0.763500
0.800000 0.695500
0.900000 0.620500
The central difference scheme generally offers higher accuracy compared to both the
forward and backward difference schemes. However, as demonstrated in the output above,
the central difference scheme cannot compute . f (0.0) and . f (1.0) since the values . f (−0.1)
and . f (1.1) are not available. Opting for the forward difference scheme at . f (0.1) or the
backward difference scheme at . f (1.0) represents a suboptimal compromise.
There is a method to approximate . f (0.0) and . f (1.0) with the same accuracy as the
central difference scheme. By replacing .h in Eq. (5.6) with .2h, we obtain
4 h 2
. f (x − 2 h) = f (x) − 2 h f (x) + f (x) + . . . (5.9)
2!
The .h 2 term in Eq. (5.9) can be eliminated by subtracting Eq. (5.9) from four times
Eq. (5.6), yielding
3 f (x) − 4 f (x − h) + f (x − 2 h)
. f (x) ∼ . (5.10)
2h
Equation (5.10) achieves the same order of accuracy as the central difference scheme.
The trade-off is that three values of . f (x) are required instead of two.
For .x = 1.0, . f (1.0) can be approximated using . f (1.0), . f (0.9) and . f (0.8). Thus, the
previous C code can be modified as:
116 5 Numerical Differentiation
#include <stdio.h>
#define N 11
int main()
{
double y[N] = {0, 0.0998, 0.1986, 0.2955, 0.3894, 0.4794, 0.5646,
0.6442, 0.7173, 0.7833, 0.8414};
double central[N], h = 0.1;
int i;
for (i = 1; i < N-1; i++) central[i] = (y[i + 1]-y[i - 1])/(2*h);
central[10] = (3*y[10] - 4*y[9] + y[8])/(2*h);
printf (" x Central \n---------------------------\n");
for (i = 1; i < N; i++) printf("%f %f\n", i*h, central[i]);
return 0;
}
5.3 Exercise
1. Derive a formula analogous to Eq. (5.10) for the case where .x = 0 (the first point).
2. The altitude (in feet) from sea level and the corresponding time (in seconds) for a hypo-
thetical rocket were recorded as follows:
5.3 Exercise 117
Time 0 20 40 60 80 100
Altitude 370 9170 23835 45624 62065 87368
Numerically compute the velocity from the table above for.t = 0 to.t = 200, maintaining
the same accuracy as the central difference scheme. At .t = 200, use Eq. (5.10) and at
.t = 0, use the formula derived in Problem 1.
Numerical Integration
6
6.1 Introduction
Any function provided explicitly can be differentiated analytically, but very few functions
can be integrated analytically.1 This is why numerical integration is often more critical than
numerical differentiation.
While analytical integration is generally a challenging task, graphically it is equivalent
to computing the area enclosed by . f (x) and the .x-axis from the lower and upper bounds of
integration.
Three methods2 are widely used to numerically integrate a function, . f (x): (1) the rect-
angular rule, (2) the trapezoidal rule, and (3) Simpson’s rule. As will be demonstrated in
the subsequent sections, Simpson’s rule provides the best approximation among them and
is considered the de facto standard for numerical integration.
1 Even a simple function such as .sin 1 cannot be integrated analytically in terms of elementary
x
functions.
2 The three methods approximate . f (x) using polynomials of order 0, 1, and 2, respectively.
© The Author(s), under exclusive license to Springer Nature Switzerland AG 2025 119
S. Nomura, C Programming and Numerical Analysis, Synthesis Lectures on Mechanical
Engineering, https://doi.org/10.1007/978-3-031-83457-8_6
120 6 Numerical Integration
. I ∼ h × f 0 + h × f 1 + h × f 2 + · · · + h × f n−1
= h ( f 0 + f 1 + · · · + f n−1 ) , (6.1)
where .h = (b − a)/n is the step size, and . f 0 , f 1 , f 2 , . . . , f n−1 are the values of the function
at the left endpoints of the equally divided subintervals of .[a, b] starting from .a (the lower
bound). As an example, consider
1 4
. I = d x.
0 1 + x2
Since this integration can be carried out analytically3 and the exact value is .π, it serves as a
benchmark for assessing the accuracy of each numerical integration scheme. The rectangular
rule, as expressed in Eq. (6.1), can be implemented as
#include <stdio.h>
double f(double x)
{
return 4.0/(1.0 + x*x);
}
int main()
{
int i, n;
double a = 0.0, b = 1.0, h, s = 0.0, x;
printf("Number of partitions = ");
scanf("%d", &n);
3
b
1 π
. f (x) d x = arctan x 0 = .
a 4
.
6.3 Trapezoidal Rule 121
h = (b - a)/n;
for (i = 0; i < n; i++) s = s + f(a + i*h);
s = s*h;
printf("Result = %f\n", s);
return 0;
}
$ gcc rectangle.c
$ ./a.out
Number of partitions = 10
Result = 3.239926
$ ./a.out
Number of partitions = 100
Result = 3.151576
$ ./a.out
Number of partitions = 10000
Result = 3.141693
$ ./a.out
Number of partitions = 100000
Result = 3.141603
$ ./a.out
Number of partitions = 1000000
Result = 3.141594
The convergence of the rectangular rule is marginal. Achieving an accuracy of five sig-
nificant figures requires 1,000,000 iterations.
As depicted in Fig. 6.2, the trapezoidal rule approximates the integral, . I , by using a set of
trapezoids over the interval.
The sum of all the trapezoids is expressed as
h h h
. I ∼ ( f 0 + f 1 ) + ( f 1 + f 2 ) + · · · + ( f n−1 + f n )
2 2 2
h
= ( f 0 + 2 f 1 + 2 f 2 + · · · + 2 f n−1 + f n )
2
h
= ( f 0 + f n ) + h × ( f 1 + f 2 + f 3 + · · · + f n−1 ) . (6.2)
2
The following code is implementation of Eq. (6.2).
122 6 Numerical Integration
#include <stdio.h>
double f(double x)
{
return 4.0/(1.0 + x*x);
}
int main()
{
int i, n ;
double a = 0.0, b = 1.0, h, s = 0.0, x;
printf("Enter number of partitions = ");
scanf("%d", &n);
h = (b - a)/n ;
for (i = 1; i <= n - 1; i++) s = s + f(a + i*h);
s = h/2*(f(a) + f(b)) + h*s;
printf("%20.12f\n", s) ;
return 0;
}
$ gcc trapezoid.c
$ ./a.out
Enter number of partitions = 10
3.139925988907
$ ./a.out
Enter number of partitions = 100
3.141575986923
$ ./a.out
Enter number of partitions = 1000
3.141592486923
The convergence of the trapezoidal rule is significantly faster than that of the rectangular
rule.
6.4 Simpson’s Rule 123
In the rectangular rule, a segment of . f (x) is approximated by a flat line (a .0th-order poly-
nomial), whereas in the trapezoidal rule, it is approximated by a straight line with a slope
(a .1st-order polynomial). As expected, the next level of refinement involves using a curved
line (a .2nd-order polynomial), leading to Simpson’s rule.
Following Fig. 6.3, we first seek a second-order polynomial that passes through the three
points shown in the figure. The curve that passes through the points .(−h, f (−h)), .(0, f (0)),
and .(h, f (h)) is assumed to be
. y = ax + bx + c.
2
Imposing the condition that this equation passes through the three points results in
. f (−h) = ah 2 − bh + c,
f (0) = c,
f (h) = ah 2 + bh + c,
#include <stdio.h>
double f(double x)
{
return 4.0/(1.0 + x*x);
}
int main()
{
int i, n;
double a = 0.0, b = 1.0, h, s1 = 0.0, s2 = 0.0, s3 = 0.0, x;
6.4 Simpson’s Rule 125
$ gcc simpson.c
$ ./a.out
Enter number of partitions (must be even) = 10
3.141592652970
$ ./a.out
Enter number of partitions (must be even) = 20
3.141592653580
As observed in the output above, convergence is achieved with only 10 partitions. Simp-
son’s rule is considered the de facto standard for numerical integration.
While it might be tempting to extend Simpson’s rule to approximate . f (x) using a third-
order polynomial or higher, such extensions are generally excessive and do not significantly
enhance accuracy.
According to error analysis, the truncation error for each rule is as follows:
• Rectangular rule
. I ∼ A + f (ξ)h,
• Trapezoidal rule
. I ∼ A + f (ξ)h 2 ,
• Simpson’s rule
. I ∼ A + f (ξ)h 3 ,
where . I is the exact integral value, . A is its approximation, .h is the step size, and .ξ is a value
within the interval.
As shown above, the accuracy of numerical integration depends not only on the step size
.h but also on the behavior of the derivatives of . f (x).
√
For example, consider using . f (x) = 1 − x 2 to approximate .π/4 with the code pro-
vided. Even with Simpson’s
√ rule, the convergence rate is notably slow. This occurs because
the slope of . f (x) = 1 − x 2 tends to infinity as .x approaches 1, causing . f (ξ), . f (ξ), and
126 6 Numerical Integration
.f (ξ) to become singular. These singularities contribute to the slow convergence in the
numerical integration.
The three methods introduced above are collectively known as the Newton-Cotes meth-
ods, where . f (x) is approximated by a polynomial that is integrated over the interval of
interest. It is possible to extend these formulas by using higher-order polynomials, yielding
increasingly accurate approximations.
However, Newton-Cotes methods can become inefficient for high-degree polynomials,
especially as the number of sample points grows, which may introduce numerical instability.
Alternatively, the Gaussian quadrature method [10] provides an efficient approach,
using a fixed number of sample points to achieve high accuracy. Although Gaussian quadra-
ture is not covered in this book, it represents a valuable option for cases requiring efficient
numerical integration.
6.5 Exercise
7.1 Introduction
. Ax = c,
or ⎛ ⎞⎛ ⎞ ⎛ ⎞
a11 a12 . . . a1n x1 c1
⎜ a21 a22 . . . a2n ⎟ ⎜ x 2 ⎟ ⎜ c2 ⎟
⎜ ⎟⎜ ⎟ ⎜ ⎟
.⎜ . .. .. .. ⎟ ⎜ .. ⎟ = ⎜ .. ⎟ ,
⎝ .. . . . ⎠⎝ . ⎠ ⎝ . ⎠
an1 an2 . . . ann xn cn
or equivalently
© The Author(s), under exclusive license to Springer Nature Switzerland AG 2025 127
S. Nomura, C Programming and Numerical Analysis, Synthesis Lectures on Mechanical
Engineering, https://doi.org/10.1007/978-3-031-83457-8_7
128 7 Solving Simultaneous Equations
⎧
⎪
⎪ a11 x1 + a12 x2 + a13 x3 + . . . + a1n xn = c1 ,
⎪
⎪
⎪
⎨ a21 x1 + a22 x2 + a23 x3 + . . . + a2n xn = c2 ,
. a31 x1 + a32 x2 + a33 x3 + . . . + a3n xn = c3 , (7.1)
⎪
⎪
⎪
⎪ ......
⎪
⎩a x + a x + a x + ... + a x = c .
n1 1 n2 2 n3 3 nn n n
The number .n represents the number of equations and the size of the matrix, . A.
Cramer’s Rule
Cramer’s rule is suitable for systems of two or three simultaneous equations. The determinant
of a .2 × 2 matrix is defined as
a11 a12
. ≡ a11 a22 − a12 a21 .
a21 a22 .
a11 x1 + a12 x2 = c1 ,
.
a21 x1 + a22 x2 = c2 ,
are expressed as
c1 a12
c2 a22 c1 a22 − c2 a12
. x1 = = ,
a11 a12 a11 a22 − a12 a21
a21 a22
a11 c1
a21 c2 c2 a11 − c1 a21
x2 = = .
a11 a12 a11 a22 − a12 a21
a21 a22
7.1 Introduction 129
c1 a12 a13
c2 a22 a23
c3 a32 a33
. x1 = ,
a11 a12 a13
a21 a22 a23
a31 a32 a33
a11 c1 a13
a21 c2 a23
a31 c3 a33
x2 = ,
a11 a12 a13
a21 a22 a23
a31 a32 a33
a11 a12 c1
a21 a22 c2
a31 a32 c3
x3 = .
a11 a12 a13
a21 a22 a23
a31 a32 a33
These formulas provided for the solutions are known as Cramer’s rule. Although Cramer’s
rule can be extended to handle more than three simultaneous equations using determinants
of larger matrices, its practical application is generally limited to systems with two or three
equations.
This limitation arises from the computational complexity involved in calculating the
determinant. For an .n × n matrix, the determinant involves .n! terms, as illustrated for .2 × 2
and .3 × 3 matrices above. Each term requires .n − 1 multiplications. Consequently, the total
number of multiplications required for Cramer’s rule with .n simultaneous equations is .n! ·
(n − 1) · (n + 1), including the denominator. For .n = 4, this results in 360 multiplications,
and for .n = 10, it amounts to 359,251,200 multiplications.
The approximate computational time for solving.n simultaneous equations using Cramer’s
rule on a 100 MFLOPS computer (an older PC) is estimated as shown in Table 7.1 [11].
130 7 Solving Simultaneous Equations
Multiplying .λ1 by Eq. (7.2) and .λ2 by Eq. (7.3) and adding the two yield
(λ1 a11 + λ2 a12 )x1 + (λ1 a21 + λ2 a22 )x2 + . . . + (λ1 a1n + λ2 a2n )xn = λ1 c1 + λ2 c2 .
.
(7.4)
Equation (7.4) represents another valid linear equation.
The Gauss-Jordan elimination method involves selecting .λ appropriately to successively
transform Eqs. (7.1) into
⎧
⎪
⎪ 1 × x 1 + 0 × x 2 + 0 × x 3 + . . . + 0 × x n = d1 ,
⎪
⎪
⎪
⎨ 0 × x 1 + 1 × x 2 + 0 × x 3 + . . . + 0 × x n = d2 ,
. 0 × x 1 + 0 × x 2 + 1 × x 3 + . . . + 0 × x n = d3 ,
⎪
⎪
⎪
⎪ ......
⎪
⎩0 × x + 0 × x + 0 × x + ... + 1 × x = d .
1 2 3 n n
7.2.1 Example
The Gauss-Jordan elimination method is illustrated by the following example. The objective
is to transform Eq. (7.5) into Eq. (7.6) by using the diagonal elements as pivots.
⎫
2x − y + z = 2, ⎬
. −x + 3y + 3z = 3, (7.5)
⎭
2x + y + 4z = 1.
. ⇒
⎫
1x + 0y + 0z =?, ⎬
. 0x + 1y + 0z =?, (7.6)
⎭
0x + 0y + 1z =?.
The following table can be used for this conversion.
(5) −1
. 3 3 3
(6) 2 1 4 1
(7) 1 .−1/2 1/2 1
(8) 0 5/2 7/2 4 (5) − (4) × (−1)
.
(12) 0 2 3 .−1
(21) 0 0 1 .−21
In the Gauss-Jordan elimination method, the elements in the first row are divided by .a11 to
normalize .a11 to 1 (line (4)).
Using .a11 = 1 as a pivot, the elements .a21 and .a31 are eliminated (lines (8)–(9)). Next,
the elements in the second row (line (8)) are divided by .a22 to normalize .a22 to 1, which
then serves as the next pivot. Using .a22 = 1, the elements .a12 and .a32 are eliminated (lines
(13)–(15)).
132 7 Solving Simultaneous Equations
This process is repeated for .a33 . Finally, lines (19)–(21) yield the results:
It can be shown that the number of multiplications required for the Gauss-Jordan elim-
ination method is approximately proportional to .n 3 . For .n = 10, this amounts to 1,000
multiplications, in contrast to the 359,251,200 multiplications required by Cramer’s rule.
The inverse of the matrix . A can be computed using the Gauss-Jordan elimination method
by augmenting the matrix . A with the identity matrix . I and performing row operations to
transform . A into . I .
. (A | I )
i.e.
⎛ ⎞ ⎛ ⎞
2 −1 1 1 0 0 1 − 21 1
2
1
2 00
⎜ ⎟
. ⎝ −1 3 3 0 1 0 ⎠ → ⎝ 0
5 7 1
2 2 2 1 0⎠
2 1 4001 0 2 3 −1 0 1
⎛ ⎞ ⎛ ⎞
1 0 65 53 51 0 10 0 9 5 −6
⎜ ⎟ ⎝
→ ⎝0 1 5 5 5 0⎠ → 0 1
7 1 2
0 10 6 −7 ⎠ .
0 0 1 −7 −4 1 00 1 −7 −4 5
5 5 5
The inverse of . A is obtained and stored in the second half of the augmented matrix as
⎛ ⎞
9 5 −6
. A−1 = ⎝ 10 6 −7 ⎠ .
−7 −4 5
The following code implements the Gauss-Jordan elimination method. Note that array
indices in C begin at 0, whereas in linear algebra, indices for vectors and matrices start at 1.
Therefore, when coding linear algebra equations in C, each index must be shifted by 1.
In the following program, the matrix . A and the vector .c are combined and stored in a 2D
array, a[3][4].
#include <stdio.h>
#define N 3
int main()
{
double a[N][N + 1] = {{2, -1, 1, 2},{-1, 3, 3, 3},{2, 1, 4, 1}};
double pivot, d;
int i, j, k;
The output is
$ gcc gaussjordan.c
$ ./a.out
x[1]=27.000000
x[2]=31.000000
x[3]=-21.000000
The Gauss-Jordan elimination method is suitable for solving large sets of linear simultaneous
equations.
The LU decomposition2 (also known as LU factorization) is an enhancement of the
Gauss-Jordan elimination method that reduces the number of operations, resulting in faster
execution. With LU decomposition, the number of operations is reduced to approximately
.n /3, compared to .n for the Gauss-Jordan elimination method.
3 3
. A = LU , (7.7)
where . L and .U are lower and upper triangular matrices whose components are expressed as
⎛ ⎞ ⎛ ⎞
1 0 0 ... 0 u 11 u 12 u 13 . . . u 1n
⎜l 1 0 ... 0⎟ ⎜ 0 u u ... u ⎟
⎜ 21 ⎟ ⎜ 22 23 2n ⎟
⎜ ⎟ ⎜ ⎟
. L = ⎜ l 31 l 32 1 . . . 0 ⎟ , U = ⎜ 0 0 u 33 . . . u 3n ⎟ .
⎜ . . . ⎟ ⎜ ⎟
⎜ . . . . . .. ⎟ ⎜ .. .. .. . . .. ⎟
⎝ . . . . .⎠ ⎝ . . . . . ⎠
ln1 ln2 ln3 . . . 1 0 0 0 . . . u nn
Note that the diagonal elements of . L are set to 1. This decomposition is known as LU
decomposition (or LU factorization) and is the most efficient method for solving simultane-
ous equations.
The decomposition in Eq. (7.7) is unique, as it allows for the direct determination of . L
and .U . For instance, for a .4 × 4 matrix, Eq. (7.7) can be expressed as
⎛ ⎞⎛ ⎞ ⎛ ⎞
1 0 0 0 u 11 u 12 u 13 u 14 a11 a12 a13 a14
⎜ l21 1 ⎟
0 0⎟⎜ 0⎜ u 22 u 23 ⎟ ⎜
u 24 ⎟ ⎜ a21 a22 a23 a24 ⎟
.⎜ = ⎟. (7.8)
⎝ l31 l32 1 0⎠⎝ 0 0 u 33 u 34 ⎠ ⎝ a31 a32 a33 a34 ⎠
l41 l42 l43 1 0 0 0 u 44 a41 a42 a43 a44
Note that the computed values for .u i j and .li j are used immediately to determine subsequent
results.
Using the LU decomposition, . Ax = c is written as
. LU x = c. (7.9)
At first glance, it might seem that solving these two equations requires more computation
than solving a single equation of . Ax = c. However, the process of finding . L and .U from . A
is computationally less intensive, and solving the two equations is nearly trivial.
The first equation, . Ly = c, can be written as
⎛ ⎞⎛ ⎞ ⎛ ⎞
1 0 0 0 y1 c1
⎜ l21 1 0 0⎟ ⎜ y2 ⎟ ⎜ c2 ⎟
.⎜ ⎟⎜ ⎟ = ⎜ ⎟,
⎝ l31 l32 1 0 ⎠ ⎝ y3 ⎠ ⎝ c3 ⎠
l41 l42 l43 1 y4 c4
7.3 LU Decomposition (Optional) 135
or written explicitly as
⎧
⎪ y1 = c1 ,
⎪
⎨
l21 y1 + y2 = c2 ,
.
⎪
⎪ l y + l32 y2 + y3 = c3 ,
⎩ 31 1
l41 y1 + l42 y2 + l43 y3 + y4 = c4 .
The solution for . y1 ∼ y4 is straightforward as
⎧
⎪ y1 = c1 ,
⎪
⎨
y2 = c2 − l21 y1 ,
.
⎪
⎪ y = c3 − l31 y1 − l32 y2 ,
⎩ 3
y4 = c4 − l41 y1 − l42 y2 − l43 y3 .
or equivalently as
⎧
⎪ u 11 x1 + u 12 x2 + u 13 x3 + u 14 x4 = y1 ,
⎪
⎨
u 22 x2 + u 23 x3 + u 24 x4 = y2 ,
.
⎪ u 33 x3 + u 34 x4 = y3 ,
⎪
⎩
u 44 x4 = y4 .
Solving for .x1 ∼ x4 is straightforward as well and yields
⎧ y4
⎪ x4 = u 44 ,
⎪
⎪
⎨ x = y3 −u 34 x4 ,
3 u 33
⎪ x2 = y2 −u 23ux223 −u 24 x4 ,
.
⎪
⎪
⎩
x1 = y1 −u 11 x1 −u 12 x 2 −u 13 x 3
u 11 .
are proportional to .n . As a result, the total number of operations is on the order of .n 3 /3,3
2
which is about one-third of the operations required for the Gauss-Jordan elimination method.
Below is a C code implementation of the LU decomposition, solving the same equations
as in the Gauss-Jordan elimination example.
#include <stdio.h>
#define N 3
int main()
{
double a[N][N + 1] = {{2, -1, 1, 2},{-1, 3, 3, 3},{2, 1, 4, 1}};
int i, j, k, l;
The output is
$ gcc lu.c
$ ./a.out
x[0]=27.000000
x[1]=31.000000
x[2]=-21.000000
Notes
The Gauss-Seidel method and the Jacobi method are iterative techniques for solving specific
types of simultaneous equations. Unlike the Gauss-Jordan elimination method, these iterative
methods do not guarantee convergence, but they require less complex programming and can
also be applied to certain nonlinear systems of equations.
To illustrate these methods, consider the following set of three simultaneous equations.
⎧
⎨ 7x + y + 2z = 10,
. x + 8y + 3z = 8, (7.10)
⎩
2x + 3y + 9z = 6.
Equation (7.12) can be solved iteratively by starting with initial guesses for .x0 , . y0 , and .z 0 .
This method is known as the Jacobi method.
A more refined iterative scheme involves using the most recent values for subsequent
approximations. Specifically,
⎧
⎨ xn+1 = (10 − yn − 2z n )/7,
. y = (8 − xn+1 − 3z n )/8, (7.13)
⎩ n+1
z n+1 = (6 − 2xn+1 − 3yn+1 )/9.
This iterative scheme is known as the Gauss-Seidel method. It generally converges faster
than the Jacobi method because it utilizes the most recent values for the iterations.
The Gauss-Seidel method is straightforward to implement. In C programming, since
variable substitutions automatically use the most recent values, the Gauss-Seidel method is
typically preferred over the Jacobi method.
In the following code, the initial guess is set to .(x0 , y0 , z 0 ) = (0, 0, 0).
#include <stdio.h>
int main()
{
double x, y, z;
138 7 Solving Simultaneous Equations
int i, n;
x = y = z = 0.0;
printf("Enter # of iteration = ");
scanf("%d", &n);
The output is
$ gcc gauss-seidel.c
$ ./a.out
Enter # of iteration = 9
x = 1.281553, y= 0.796117, z=0.116505
$ ./a.out
Enter # of iteration = 10
x = 1.281553, y= 0.796117, z=0.116505
7.5 Exercise
a[5][5]={
{3.55618, 5.87317, 7.84934, 5.6951, 3.84642},
{-4.82893, 8.38177, -0.301221, 5.10182, -4.1169},
{-7.64196, 5.66605,3.20481, 1.55619, -1.19814},
{-2.95914, -9.16958,7.3216, 2.39876, -8.1302},
{-8.42043, -0.369407, -5.4102, -8.00545, 9.22153}
};
The basic form of a differential equation for initial value problems can be expressed as
dy
. = f (t, y), (8.1)
dt
where . y is the unknown function, .t is the independent variable often interpreted as time and
f (t, y) is a function of .t and . y. Equation (8.1) together with the initial condition of
.
© The Author(s), under exclusive license to Springer Nature Switzerland AG 2025 141
S. Nomura, C Programming and Numerical Analysis, Synthesis Lectures on Mechanical
Engineering, https://doi.org/10.1007/978-3-031-83457-8_8
142 8 Differential Equations
. y(t0 ) = y0 ,
Euler’s method is a rudimentary technique for numerically solving initial value problems.
In Euler’s method, . dy
dt in Eq. (8.1) is approximated using the forward difference as
dy yn+1 − yn
. ∼ .
dt h
Therefore, Eq. (8.1) can be approximated as
yn+1 − yn
. = f (t, y). (8.2)
h
Equation (8.2) can be expressed as
which enables the prediction of . yn+1 based on . yn and the slope at that point, as illustrated
in Fig. 8.2.
8.1 Initial Value Problems 143
Example 1
Consider the following initial value problem.
dy
. = y, y(0) = 1. (8.4)
dt
Using the separation of variables, the exact solution is easily obtained as
. y = et .
#include <stdio.h>
#include <math.h>
double f(double t, double y)
{
return y;
}
int main()
{
double h = 0.1, y, t;
int i;
t = 0.0; y = 1.0;
printf("t Euler Exact\n");
for (i = 0; i <= 10; i++)
{
printf("t = %f %f %f\n", t, y, exp(t));
y = y + h*f(t,y);
t = t + h;
}
144 8 Differential Equations
return 0;
}
As illustrated in the table above, Euler’s method offers limited accuracy. However, despite
its modest performance, Euler’s method is robust and can be employed to quickly obtain
preliminary results.
du
. = p(v − u),
dt
dv
= −uw + Ru − v,
dt
dw
= uv − bw. (8.5)
dt
1 Edward Lorenz (1917–2008) was a mathematician, meteorologist, and a pioneer of chaos theory.
8.1 Initial Value Problems 145
The following code solves Eqs. (8.5) using Euler’s method. The parameters are set to . P =
16.0, .b = 4.0, and . R = 35.0, with initial values of .u = 5.0, .v = 5.0, and .w = 5.0. The step
size is chosen as .h = 0.01, and 3,000 iterations are performed.
#include <stdio.h>
#define P 16.0
#define b 4.0
#define R 35.0
int main()
{
double h, t, u, v, w;
int i;
/* initial values */
t = 0.0; h = 0.01;
u = 5.0; v = 5.0; w = 5.0;
To plot .(u, w) from the program, gnuplot, a freely available graphics package detailed
in Appendix A, can be utilized. The output from a.out is saved to a data file, lorenz.dat,
146 8 Differential Equations
using I/O redirection, and this file is placed or transferred to a directory accessible by
gnuplot.
$ gcc lorenz.c
$ ./a.out > lorenz.dat
The Runge-Kutta4 method is a refinement of Euler’s method, where . f (tn , yn ) in Eq. (8.3)
is replaced by a weighted average of .k1 through .k4 , defined as
k1 = f (t, y),
.
h h
k2 = f (t + , y + k1 ),
2 2
h h
k3 = f (t + , y + k2 ),
2 2
k4 = f (t + h, y + hk3 ).
Using the above, the iterative scheme in the Runge-Kutta method is expressed as
k1 + 2k2 + 2k3 + k4
. yn+1 = yn + h . (8.6)
6
The quantities, .k1 ∼ k4 , are computed at .t, .t + h2 and .t + h. The derivation of Eq. (8.6) is
more involved and is therefore deferred to an advanced textbook.5
The following C code implements the Runge-Kutta method for Eq. (8.4).
#include <stdio.h>
#include <math.h>
double f(double t, double y)
{
return y;
}
int main()
{
double h = 0.1, t, y, k1, k2, k3, k4;
int i;
/* initial value */
t = 0.0; y = 1.0;
for (i = 0; i <= 10; i++)
{
printf("t= %f rk= %f exact=%f\n", t, y, exp(t));
k1 = h*f(t,y);
k2 = h*f(t + h/2, y + k1/2.0);
k3 = h*f(t + h/2, y + k2/2.0);
k4 = h*f(t + h, y + k3);
y = y + (k1 + 2.0*k2 + 2.0*k3 + k4)/6.0;
t = t + h;
}
return 0;
}
As evidenced by the output above, the Runge-Kutta method provides a significantly more
accurate approximation compared to Euler’s method. It is considered the de facto standard
for solving initial value problems.
Equation (8.1) is a first-order ordinary differential equation. However, many important and
useful differential equations are of higher order, such as the equation of motion (second-
order) and the equation of beam deflections (fourth-order).
A higher-order differential equation can be transformed into a system of first-order dif-
ferential equations, allowing numerical methods such as Euler’s method or the Runge-Kutta
method to be applied for solving these equations.
To illustrate this technique, consider Eq. (8.7), which describes harmonic oscillation in
a spring-mass system. It is a second-order ordinary differential equation with two initial
conditions.
d2 y
. = −y, y(0) = 0, y (0) = 1. (8.7)
dt 2
Equation (8.7) can be transformed into a system of two simultaneous differential equations
by setting
dy1
. y1 ≡ y, y2 ≡ . (8.8)
dt
8.2 Higher Order Ordinary Differential Equations 149
#include <stdio.h>
#include <math.h>
double f1(double x, double y1, double y2)
{
return y2;
}
double f2(double x, double y1, double y2)
{
return -y1;
}
int main()
{
double h = 0.01, y1, y2, x;
int i;
y1 = 0.0; y2 = 1.0;
x = 0.0;
printf(" x y2 cos(x)\n");
for (i = 0; i <= 10; i++)
{
printf("x = %f %f %f\n", x, y2, cos(x));
y1 = y1 + h*f1(x, y1, y2);
y2 = y2 + h*f2(x, y1, y2);
x = x + h;
}
return 0;
}
In this example, the exact solution for . y2 is .cos x. The output is:
150 8 Differential Equations
All the differential equations presented so far are classified as “initial value problems,”
where the initial conditions of the unknown function are specified at a single starting point.
Typically, time is used as the independent variable. Another class of differential equations is
known as “boundary value problems,” in which the solution is defined by values prescribed
on the boundaries of the domain over which the differential equation is defined. In these
cases, position is typically the independent variable. Many of physical quantities such as
temperature, stress fields, velocity of particles are modeled as boundary value problems.
In general, boundary value problems are more difficult to solve than initial value problems
and are not covered in this book. Common numerical methods for boundary value problems
include the finite element method (FEM) and the finite difference method (FDM).
8.4 Exercise
. y = −x 2 y, y(0) = 1,
and plot the three results on a single graph using gnuplot (see Appendix A). Use the
interval.0 ≤ x ≤ 1 with a step size of.h = 0.1. Utilize the following syntax in gnuplot:
where “data1.txt” contains data from Euler’s method and “data2.txt” contains
data from the Runge-Kutta method.
2. Numerically solve the following differential equations using Euler’s method.
du
. = v,
dt
dv
= −kv − u 3 + B cos t,
dt
with
. k = 0.1, B = 11.0,
and plot the result using gnuplot. Change the parameters to
. k = 0.4, B = 20.0,
and show the graph as well. You can use the following as the initial condition:
u(0) = 1, v(0) = 1.
.
Also, use
. h = 0.01, i = 10000.
One shows chaos and the other does not.
Gnuplot
A
The C language itself does not include standard library support for graphics, as drawing
graphics is machine-dependent. To visualize outputs from a C program, machine-specific
library files are required, which are not part of the gcc distribution. An alternative approach
is to export data generated by a C program to an external application that can read and plot
the data.
The graphical application gnuplot,1 meets this requirement. For Windows PCs, down-
load the zipped distribution file from http://www.gnuplot.info. After downloading,
extract all files into a single directory and run wgnuplot.exe from that location.
Below is a list of commonly used commands in gnuplot. Comments in gnuplot
begin with the “#” symbol. Most commands are self-explanatory, so feel free to experiment
with them.
1 Freely available for Windows, macOS, and Linux. Source code is also available.
© The Editor(s) (if applicable) and The Author(s), under exclusive license to Springer
153
Nature Switzerland AG 2025
S. Nomura, C Programming and Numerical Analysis, Synthesis Lectures on Mechanical
Engineering, https://doi.org/10.1007/978-3-031-83457-8
154 Appendix A: Gnuplot
To draw the graph shown in Fig. A.1, enter the following commands:
Note that two asterisks (**) are used for exponentiation instead of a caret ( ˆ).
To exit gnuplot, enter quit.
While gnuplot can be used to plot built-in functions, its primary utility lies in visual-
izing data generated by a C program. Data produced by a C program can be imported into
gnuplot for graphical representation.
Below is an example of how to generate a data file using C and export it to gnuplot
for plotting.
Prepare the following C code and execute it with I/O redirection to store the output in a
separate file.
Appendix A: Gnuplot 155
#include <stdio.h>
#include <math.h> int main()
{
int i; double x;
for (i = 0; i < 100; i++)
{
x = 0.1*i;
printf("%f %f\n", x , sin(x));
}
return 0;
}
The output from a.out is saved to a file named data.dat. Move this file to a directory
accessible by gnuplot. For this purpose, we use the directory C:\tmp as the working
directory on a Windows system.
Next, open gnuplot and enter the following commands:
Note that the file name, data.dat, must be enclosed in single quotation marks (’).2
Figure A.2 illustrates the output.
For another example of plotting data generated by C, refer to Example 2 in Sect. 8.1.1.
To export a graph from gnuplot to another application such as Word, right-click the
top bar of the graph window, select Options, and then choose Copy to Clipboard.
The graphic image is saved to the clipboard and can be pasted into any application.
Alternatively, you can save a graph from gnuplot directly to a file in various supported
graphic formats, including jpg, gif, png, and eps. To save a graph in gif format, use
the following command:
To save a graph in eps (Encapsulated PostScript) format, use the following command:
gnuplot offers many additional commands that are beyond the scope of this appendix.
It also has scripting capabilities. For more information, refer to online tutorials or reference
books.3
3 For example, see Janert, Gnuplot in Action: Understanding Data with Graphs, Manning Publica-
tions, 2009.
Octave (MATLAB) Tutorial for C Programmers
B
B.1 Introduction
MATLAB is a powerful software package for scientific and engineering tasks, combining
the ease of hand-held calculators with the versatility of programming. Many engineering
and science courses require students to use MATLAB for homework and projects. This
appendix provides a brief tutorial on MATLAB for those who are familiar with C but have
not yet learned MATLAB. The goal is to enable users to start working with MATLAB
quickly and efficiently. This is feasible because many MATLAB commands are similar to
or variations of corresponding C commands, allowing C programmers to recognize many
MATLAB commands with minimal reference. However, the reverse is not necessarily true:
proficiency in MATLAB does not automatically translate to mastery of C, which may require
significant additional effort.
There are several MATLAB alternatives with compatible syntax available for free on the
internet, including GNU Octave and Scilab [13]. GNU Octave4 offers extensive tools for
solving common numerical problems and features syntax largely compatible with MATLAB.
Octave can be used in either GUI mode or from the command line, as shown in Fig. B.1. This
appendix is not intended to be a comprehensive reference for Octave/MATLAB but rather a
quick-start guide for those familiar with C who wish to learn the basics of Octave/MATLAB
efficiently. Numerous reference books on Octave/MATLAB are available for further study.
This appendix is based on GNU Octave; however, all functions and commands covered
are compatible with MATLAB.
© The Editor(s) (if applicable) and The Author(s), under exclusive license to Springer
157
Nature Switzerland AG 2025
S. Nomura, C Programming and Numerical Analysis, Synthesis Lectures on Mechanical
Engineering, https://doi.org/10.1007/978-3-031-83457-8
158 Appendix B: Octave (MATLAB) Tutorial for C Programmers
1. There is no distinction between integers and floating-point numbers; all variables are of
double precision by default.
2. Variable names are case-sensitive.
3. Array indices begin at 1 (unlike 0 in C).
4. Due to its original design philosophy,5 all sets of numbers are represented as matrices,
including single variables. Intervals are also represented as matrices.
5. Vectors and matrices are defined using square brackets, e.g., [3 4 1], [1 2 3; 4
5 6; 7 8 9].
6. Any statement following % is treated as a comment.
7. Statements ending with a semicolon (;) do not produce output.
8. Both double quotation marks (") and single quotation marks (’) can be used for strings
in Octave, but MATLAB only accepts single quotation marks (’).
Try entering the following commands at the command line. Most of these commands are
self-explanatory.
5 MATLAB stands for Matrix Laboratory and was initially developed for solving problems in linear
algebra.
Appendix B: Octave (MATLAB) Tutorial for C Programmers 159
octave.exe:1> pi
ans = 3.1416
octave.exe:2> pi=3.0 % User defined value.
pi = 3
octave.exe:3> pi*pi % pi=3 is used.
ans = 9
octave.exe:4> clear pi % User defined value cancelled.
octave.exe:5> pi
ans = 3.1416
160 Appendix B: Octave (MATLAB) Tutorial for C Programmers
B.2.3 Vectors/Matrices
Both vectors and matrices use square brackets [· · ·] to enclose their components. Components
are separated by either a space or a comma (,), as illustrated in the following example:
6 9 −8 z 0
use
a=[1 4 5; 8 1 2; 6 9 -8];
b=[4; 7; 0];
sol = inv(a)*b % sol = a\b also works.
Appendix B: Octave (MATLAB) Tutorial for C Programmers 161
1 3 5 7 9
1 3 5 7 9
1 9 25 49 81
1 1 1 1 1
B.2.4 Graph
Both MATLAB and Octave have built-in graphics support. In Octave, built-in gnuplot is
automatically called for graphics. Try the following graphics commands (Fig. B.2).
%%%%%%%%%%%%%%%%%%
x=[0: 0.2 : 10];
y=1/2 * sin(2*x) ./ x; % ./ divides by individual components.
xlabel(’X-axis’);
ylabel(’sin(2x)/x’);
plot(x, y);
close;
%%%%%%%%%%%%%%%%%
t=[0: 0.02: 2*pi];
plot(cos(3*t), sin(2*t)) % Parametric plot.
%%%%%%%%%%%%%%%%%%%%
x=[0: 0.01: 2*pi];
y1=sin(x);
y2=sin(2*x);
y3=sin(3*x);
plot(x, y1, x, y2,x, y3); % Plotting multiple graphs.
%%%%%%%%%%%%%%
xx=[-10:0.4:10];
yy=xx;
[x,y]=meshgrid(xx,yy);
z=(x .ˆ2+y.ˆ2).*sin(y)./y;
surfc(x,y,z) % 3-D graph.
close
Appendix B: Octave (MATLAB) Tutorial for C Programmers 163
B.2.5 I/O
Octave/MATLAB provides several options for input and output. Unlike C, Octave/MATLAB
does not have a scanf() function. Instead, the input function can be used, as demon-
strated in the example below. This function serves a similar purpose to a combination
of scanf() and printf() in C. The printf() function in C is replaced by the
fprintf()6 function in Octave/MATLAB.
B.2.6 M-files
There are two types of external files that can be loaded into Octave/MATLAB: script m-files
and function m-files. Both types must have the extension m (*.m) and be located in a directory
that Octave/MATLAB can access.
1. Function m-files
In C, functions must be declared before use. In Octave/MATLAB, a separate file must be
created for each user-defined function. The file containing the function definition must be
saved with the same name as the function, using the m extension. If the file is located in a
directory accessible to Octave/MATLAB, the function is automatically available for use
as if it were a built-in function. There is no need to explicitly load the m-file (attempting
to do so will result in an error). For example, if a file named myfunction.m exists
with the following content:
function y=myfunction(x)
y=xˆ3-x+1;
end;
the input. The second line, y=xˆ3-x+1;, defines the output. The end keyword is
optional but recommended.
A function can return multiple values. In the following example, when z is provided as
input, x stores zˆ2 and y stores zˆ3.
[a,b]=myfunction2(3);
There is a way to define a function without a separate function m-file called anonymous
functions. Use the following example:
f=@(x,y) x-yˆ2;
The code above defines f (x, y) = x − y 2 that can be included in the script program
without preparing a separate function m-file, f.m,
In an Octave/MATLAB session, this function can be called directly as
octave.exe:21> f(1,2)
ans = -3
Anonymous functions provide a quick way to create simple, one-line functions, useful
when you don’t need a full function file.
2. Script m-files
A script m-file is a batch file that contains a set of statements which would otherwise be
typed directly from the keyboard. Any Octave/MATLAB statement can be included in a
script m-file, with the exception of function definitions, which must be saved separately
as function m-files. To load a script m-file, type the file name without the extension m.7
For example, a file named myscript.m can be created with the following content and
saved in the C:\tmp directory.
7 Note that a script m-file name should not contain a minus (-) sign. Why?.
Appendix B: Octave (MATLAB) Tutorial for C Programmers 165
a=input(’Enter a number=’);
fprintf(’The square of %f is %f\n.’, a, aˆ2);
The conditional statements in Octave/MATLAB are similar to those in C, with minor mod-
ifications in syntax. The following examples illustrate these conditional statements clearly.
1. If statement
The following example demonstrates the usage of if statements. Note that each if
statement must be paired with an end.
if a>2
disp(’a is greater than 2.’)
else
disp(’a is less than 2.’)
end
if (a > 2)
printf("a is greater than 2.");
else
printf("a is less than 2.");
2. For statement
The following example illustrates the usage of for statements. Note that each for
statement must be paired with an end.
166 Appendix B: Octave (MATLAB) Tutorial for C Programmers
For C programmers, a highly effective way to quickly learn the syntax of Octave/MATLAB
is to compare equivalent programs written in each language side by side.
The following section provides a side-by-side comparison of programs that implement
the same logic in both C and Octave/MATLAB.
1. Programs to solve quadratic equations
/* This program computes two roots
for a quadratic equation. */
% This program computes two roots
#include <stdio.h> % for a quadratic equation.
#include <math.h>
a=input("Enter a = ");
int main() b=input("Enter b = ");
{ c=input("Enter c = ");
double a, b, c, disc, x1, x2;
disc=bˆ2-4*a*c;
printf("Enter 3 coeffs =");
scanf("%lf %lf %lf", &a, &b, &c); if disc<0
disp(’Imaginary roots !’);
disc = b*b - 4*a*c; return;
if (disc < 0) end;
{
printf("Imaginary roots !\n"); x1=(-b-sqrt(disc))/(2*a);
return 0; x2=(-b+sqrt(disc))/(2*a);
}
x1 = (-b + sqrt(disc))/(2*a); fprintf(’Roots are %f %f.\n,x1,x2);
x2 = (-b - sqrt(disc))/(2*a);
printf("The roots are\
%f, %f.\n", x1, x2);
return 0;
}
#include <stdio.h>
#include <math.h>
int main() sum=0;
{
int i; for k=0:1000
double sum = 0.0; sum=sum+(-1)ˆk/(2*k+1);
for (i = 0; i < 1000; i++) end
sum+= pow(-1,i)/(double)(2*i + 1);
printf("approx= %f fprintf(’Approx and exact
exact value= %f.\n", values = %f %f.\n’,
4*sum, 4*atan(1.0)); 4*sum, 4*atan(1));
return 0;
}
#include <stdio.h>
#include <math.h>
function y=f(x)
#define EPS 1.0e-6
y=x*x-2;
double f(double x)
%
{
% Save this file as f.m
return x*x - 2;
%
}
function y=fp(x)
y=2*x;
double fp(double x)
%
{
% Save this file as fp.m
return 2*x;
%
}
function y=newton(x)
double newton(double x)
y=x-f(x)/fp(x);
{
%
return x - f(x)/fp(x);
% Save this file as newton.m
}
%
int main()
x1=input(’Enter initial
{
guess = ’);
double x1, x2;
int i;
if fp(x1)<eps
printf("Enter initial guess =");
disp(’No convergence !’);
scanf("%lf", &x1);
return; end
if (fp(x1) == 0.0)
{
for i=0:1:99
printf("No convergence.\n");
x2=newton(x1);
return 1;
if abs(x1-x2)<1e-10 break;end
}
x1=x2;
for (i = 0; i < 100; i++)
end
{
x2 = newton(x1);
fprintf(’Iteration = %d\n’, i);
if (fabs(x1 - x2) < EPS) break;
fprintf(’x = %f\n’, x1);
x1 = x2;
}
return 0;
}
Appendix B: Octave (MATLAB) Tutorial for C Programmers 169
B.4 Exercise
#include <stdio.h>
int main()
{
double x, y, z;
int i,n;
x = y = z = 0.0;
printf("Enter # of iteration = ");
scanf("%d", &n);
for (i = 0; i < n; i++)
{
x = (10 - y - 2*z)/7;
y = (8 - x - 3*z)/8.0;
z = (6 - 2*x - 3*y)/9.0;
}
printf("x = %f, y= %f, z=%f.\n", x,y,z);
return 0;
}
2. Translate the following C code into equivalent Octave/MATLAB m-files. You will
need to create two m-files: one for a function m-file (f.m) and one for a script m-file
(simpson.m).
/* Simpson’s rule */
#include <stdio.h>
#include <math.h>
double f(double x)
{
return 4.0/(1.0 + x*x);
}
int main()
{
int i, n;
double a = 0.0, b = 1.0, h, s1 = 0.0, s2 = 0.0, s3 = 0.0, x;
printf("Enter number of partitions (must be even) = ");
170 Appendix B: Octave (MATLAB) Tutorial for C Programmers
scanf("%d", &n);
h = (b - a)/(2.0*n);
s1 = (f(a) + f(b));
for (i = 1; i < 2*n; i = i + 2) s2 = s2 + f(a + i*h);
for (i = 2; i < 2*n; i = i + 2) s3 = s3 + f(a + i*h);
printf("%f\n", (h/3.0)*(s1 + 4.0*s2 + 2.0*s3)) ;
return 0;
}
FORTRAN Tutorial for C Programmers
C
As FORTRAN programs were prepared using a deck of IBM punch cards shown in Fig. C.1
one line per card, the width of the card (80 characters) was all it was able to utilize. Unlike
modern computer languages, FORTRAN has the following restrictions:
1. Case insensitivity. While originally only uppercase letters were used, modern programs
can handle both uppercase and lowercase letters.
2. Fixed-format structure: Each line is restricted to 80 columns.
© The Editor(s) (if applicable) and The Author(s), under exclusive license to Springer
171
Nature Switzerland AG 2025
S. Nomura, C Programming and Numerical Analysis, Synthesis Lectures on Mechanical
Engineering, https://doi.org/10.1007/978-3-031-83457-8
172 Appendix C: FORTRAN Tutorial for C Programmers
(a) Column 1 is reserved for comments. Any character in Column 1 signifies that the line
is a comment line.
(b) Columns 2–5 are designated for line identification, primarily used by GOTO state-
ments.
(c) Column 6 is reserved for continuation. Any character in Column 6 indicates that the
line is a continuation of the previous line.
(d) Columns 7–72 are reserved for FORTRAN statements. This is the sole area where
FORTRAN code can be written.
3. Variables starting with letters I through N are implicitly declared as integers. Variables
starting with any other letters are implicitly declared as real numbers. To override this
default behavior, explicitly declare variables as INTEGER, REAL, or DOUBLE PRE-
CISION.
4. Mixed data types are not allowed. If an integer variable needs to be used as a floating-
point number, apply the FLOAT function, e.g., FLOAT(I). Conversely, to use an integer
constant with floating-point variables, convert it from 3 to 3.0.
1. UNIX system
On a typical UNIX system, either g77 (GNU FORTRAN compiler) or f77 or f95
(manufacturer-supplied FORTRAN compiler) is available. Instead of using gcc, invoke
g77 followed by the name of the FORTRAN file. The file extension for FORTRAN files
must be .f or .f77 or f95.
Appendix C: FORTRAN Tutorial for C Programmers 173
$ nano MyProgram.f
$ g77 MyProgram.f
$ ./a.out
2. Windows system
The free FORTRAN compiler, g77, for Windows is available from the same site where
gcc was downloaded. Since download sites may change over time, perform a Google
search using keywords such as gnu g77 windows to find the current download loca-
tion.
To compile a FORTRAN program, issue
g77 MyProgram.c
Note:
• The WRITE(*,*) "STRING", A line means to write STRING and the value of
A to the default device (the first *, screen) using the default format (the second *).
• A to the power of B (A B ) can be entered as A**B.
• The following is a list of relational operators used in IF.
#include <stdio.h>
#include <math.h>
PARAMETER(N=10)
#define N 10
REAL X(N), SUM, AVE, VAR
int main()
INTEGER I
{
float x[] = { -4.0, 1.2, 1.3, 2.5,
DATA X /-4.0,1.2,1.3,2.5,
-12.7, 9.0, 1.41, 65.2, -2.1,
c -12.7,9.0,1.41,
2.36};
c 65.2,-2.1, 2.36/
int i;
float sum = 0.0, average, variance;
SUM=0.0
#include <stdio.h>
#include <math.h>
#define EPS 1.0e-6 ******************************
FUNCTION F(X)
double f(double x) DOUBLE PRECISION F, X
{ F=X*X-2.0
return x*x - 2; RETURN
} END
******************************
double fp(double x) FUNCTION FP(X)
{ DOUBLE PRECISION FP, X
return 2*x; FP=2.0*X
} RETURN
END
double newton(double x) ******************************
{ FUNCTION NEWTON(X)
return x - f(x)/fp(x); DOUBLE PRECISION NEWTON,
} c X, F, FP
NEWTON=X-F(X)/FP(X)
int main() RETURN
{ END
double x1, x2; ****************************
int i;
PARAMETER(EPS=1.0E-6)
printf("Enter initial guess ="); DOUBLE PRECISION X1, X2,
scanf("%lf", &x1); c NEWTON, F, FP
INTEGER I
if (fp(x1) == 0.0)
{ WRITE(*,*) "Enter initial guess"
printf("No convergence.\n"); READ(*,*) X1
return 1;
} IF(FP(X1).EQ.0.0) THEN
WRITE(*,*) "No conv"
for (i = 0; i < 100; i++) STOP
{ ENDIF
x2 = newton(x1);
if (fabs(x1 - x2)< EPS) break; DO 10 I=0,99,1
x1 = x2; X2=NEWTON(X1)
} IF(ABS(X1-X2).LT.EPS) GOTO 11
X1=X2
printf("iteration = %d\n", i); 10 CONTINUE
printf("x= %f\n", x1);
return 0; 11 CONTINUE
}
WRITE(*,*)"Iteration =",I
WRITE(*,*)"X=",X1
STOP
END
• The type of of each function defined must be also declared within the main program.
Appendix C: FORTRAN Tutorial for C Programmers 177
• To access an external file, use OPEN to open the file, assign a unit number to
UNIT, and specify whether the file is for writing (STATUS=’new’) or for reading
(STATUS=’old’).
• READ(1, *) A indicates reading a variable A from unit 1 (e.g., an external file
data1.dat) using the default format.
• WRITE(2, *) B signifies writing the value of B to unit 2 (e.g., an external file
data2.dat) using the default format.
C.4 Exercise
#include <stdio.h>
#define N 10
int main()
{
float x[N] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
y[N] = {549.88, 693.932, 415.337, 624.482,
436.095, 355.256, 185.603,
178 Appendix C: FORTRAN Tutorial for C Programmers
#include <stdio.h>
#include <math.h>
double f(double t, double y)
{
return y;
}
int main()
{
double h = 0.1, t, y, k1, k2, k3, k4;
int i;
/* initial value */
t = 0.0; y = 1.0;
1. Richard M. Stallman. Using the Gnu Compiler Collection. Createspace Independent Pub., 2009.
2. Brian W. Kernighan and Dennis M. Ritchie. The C Programming Language, 2nd Edition.
Prentice-Hall, Inc., 1988.
3. Alfred S. Posamentier and Ingmar Lehmann. The Fabulous Fibonacci Numbers. Prometheus,
1992.
4. Nicholas Metropolis and Ariel Rubinstein. Monte Carlo Methods in Practice. Springer, 1st
edition, 1996.
5. S. M. Ross. A simple explanation of the birthday paradox. Probability in the Engineering and
Informational Sciences, 12(4):121–126, 1998.
6. William H. Press, Saul A. Teukolsky, William T. Vetterling, and Brian P. Flannery. Numerical
Recipes in C: The Art of Scientific Computing, Second Edition. Cambridge University Press,
1992.
7. Stephen Wolfram. An Elementary Introduction to the Wolfram Language. Wolfram Media, 2017.
8. Tristan Needham. Visual Complex Analysis. Oxford University Press, 1997.
9. Steven C. Chapra and Raymond P. Canale. Numerical Methods for Engineers. McGraw-Hill
Education, 5th edition, 2006.
10. Richard L. Burden, J. Douglas Faires, and Annette M. Burden. Numerical Analysis: 10th Edition.
Cengage Learning, 2015.
11. Wolfgang Dahmen and Arnold Reusken. Numerik für Ingenieure und Naturwissenschaftler.
Springer, 2006.
12. Edward N. Lorenz. Deterministic nonperiodic flow. Journal of the Atmospheric Sciences,
20(2):130–141, 1963.
13. Alfio Quarteroni, Fausto Saleri, Paola Gervasio, et al. Scientific computing with MATLAB and
Octave, volume 3. Springer, 2006.
14. Larry Nyhoff and Sanford Leestma. FORTRAN 77 for Engineers and Scientists with an Intro-
duction to FORTRAN 90 (4th Edition). Pearson, 2019.
© The Editor(s) (if applicable) and The Author(s), under exclusive license to Springer
179
Nature Switzerland AG 2025
S. Nomura, C Programming and Numerical Analysis, Synthesis Lectures on Mechanical
Engineering, https://doi.org/10.1007/978-3-031-83457-8
Index
A D
address-of operator, 63 dereferencing operator, 62, 66
address operator, 62 determinant, 128
alternating harmonic series, 28 differential equations, 141
anonymous functions, 164 double, 15
arrays, 51 double precision, 99
double quotation marks, 76
do while, 32
B Dynamic Memory Allocation Example, 88
backward difference, 113 dynamic memory manipulation, 87
Birthday Paradox, 57
bisection method, 101
boundary value problems, 150
E
Euler’s method, 142
C
call by reference, 68
call by value, 68 F
calloc(), 91 Fibonacci numbers, 42
cancellation error, 96 file handling, 59
cast operator, 16, 17 finite difference method, 150
central difference, 113 finite element method, 150
chaos, 144 float, 15
char, 15 for, 26
Cholesky decomposition, 136 FORTRAN, 171
command line arguments, 79 forward difference, 112
command line parameters, 80 fprintf(), 163
compiler, 6 free(), 89
control statement, 25 function, 38
conversion error, 96 function m-files, 163
Cramer’s rule, 127, 128 function pointers, 71
curve fitting, 55 Fundamental theorem of algebra, 101
© The Editor(s) (if applicable) and The Author(s), under exclusive license to Springer
181
Nature Switzerland AG 2025
S. Nomura, C Programming and Numerical Analysis, Synthesis Lectures on Mechanical
Engineering, https://doi.org/10.1007/978-3-031-83457-8
182 Index
G P
Gaussian elimination, 130 pointer, 62, 65
Gaussian quadrature method, 126 preprocessor, 36
Gauss-Jordan elimination method, 130 principle of linearity, 130
Gauss-Seidel method, 137 principle of superposition, 130
gcc, 3, 4 printf(), 19
getchar(), 21 putchar(), 21
gnuplot, 153
R
H
random numbers, 43
higher order differential equations, 148
realloc(), 91
rectangular rule, 119
I recursive algorithm, 41
if, 25 recursivity, 41
initial value problems, 141 reference operator, 63
input/output, 18 regression, 55
int, 15 row reduction, 130
Runge-Kutta method, 147
J
Jacobian, 109 S
Jacobi method, 137 scanf(), 19
script m-files, 163
L Simpson’s rule, 123
locality, 41 simulations, 47
Lorenz equations, 144 simultaneous equations, 127
LU decomposition, 133 ssh, 3
standard deviation, 54
static memory allocation, 88
M strange attractors, 144
malloc(), 87, 88 strcmp(), 78
MATLAB, 157 strcpy(), 78
m-files, 163 string, 75
Monte Carlo method, 47 structures, 83
multi-dimensional arrays, 53 switch, 32
N
T
nano, 6
trapezoidal rule, 121
Newton-Cotes method, 126
Newton-Raphson method, 104
Newton’s method, 104
V
numerical differentiation, 111
numerical integration, 119 variance, 54
O W
Octave, 157 while, 30