0% found this document useful (0 votes)
12 views184 pages

C Programming and Numerical Analysis 2nd Edition

The document discusses the Synthesis Lectures on Mechanical Engineering series, which focuses on mechanical engineering principles and applications. It introduces a book on C programming and numerical analysis aimed at STEM students, emphasizing practical applications and foundational programming concepts. The book is structured into two parts, covering C programming fundamentals and numerical analysis techniques, and is designed to help readers solve engineering and science problems using C.

Uploaded by

Imad Tantawi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
12 views184 pages

C Programming and Numerical Analysis 2nd Edition

The document discusses the Synthesis Lectures on Mechanical Engineering series, which focuses on mechanical engineering principles and applications. It introduces a book on C programming and numerical analysis aimed at STEM students, emphasizing practical applications and foundational programming concepts. The book is structured into two parts, covering C programming fundamentals and numerical analysis techniques, and is designed to help readers solve engineering and science problems using C.

Uploaded by

Imad Tantawi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 184

Synthesis Lectures on Mechanical

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

ISSN 2573-3168 ISSN 2573-3176 (electronic)


Synthesis Lectures on Mechanical Engineering
ISBN 978-3-031-83456-1 ISBN 978-3-031-83457-8 (eBook)
https://doi.org/10.1007/978-3-031-83457-8

1st edition: © Springer Nature Switzerland AG 2018


2nd edition: © The Editor(s) (if applicable) and The Author(s), under exclusive license to Springer
Nature Switzerland AG 2025

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

If disposing of this product, please recycle the paper.


Preface

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:

1. To introduce the fundamental concepts of computer programming using the C lan-


guage. This includes understanding syntax, data structures, algorithms, and best
practices in coding. Through step-by-step explanations and examples, readers will gain
a solid foundation in programming that will serve as a valuable tool in their academic
and professional careers.
2. To use programming skills to tackle the numerical analysis challenges essential in
science and engineering. This book helps readers learn how to implement algorithms
to solve mathematical problems such as non-linear equations, simultaneous equations,
differential equations, and numerical integrations. By focusing on practical exercises
and real-world examples, it shows students how programming is relevant to their fields
of study.

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.

Arlington, TX, USA Seiichi Nomura


Contents

Part I Introduction to C Programming


1 First Steps to Run a C Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1 A Cycle of C Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 UNIX Command Primer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.3 Overview of C Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3.1 Principles of C Language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3.2 Skeleton C Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.4 Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2 Components of C Language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.1 Variables and Data Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.1.1 Cast Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.1.2 Examples of Data Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.2 Input/Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.3 Operators Between Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.3.1 Relational Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.3.2 Logical Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.3.3 Increment/Decrement/Substitution Operators . . . . . . . . . . . . . . . . 23
2.4 Control Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.4.1 if Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.4.2 for Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.4.3 while Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.4.4 do While Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.4.5 switch Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.5 Miscellaneous Remarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.5.1 Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.6 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
2.6.1 Definition of Functions in C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
2.6.2 Locality of Variables Within a Function . . . . . . . . . . . . . . . . . . . . 41

ix
x Contents

2.6.3 Recursivity of Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41


2.6.4 Random Numbers, rand() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
2.6.5 Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
2.7 Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
2.7.1 Definition of Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
2.7.2 Multi-dimensional Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
2.7.3 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
2.7.4 Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
2.8 File Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
2.8.1 I/O Redirection (Standard Input/Output Redirection) . . . . . . . . . 59
2.8.2 File Handling (from Within a Program) . . . . . . . . . . . . . . . . . . . . 60
2.9 Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
2.9.1 Address Operator & and Dereferencing Operator * . . . . . . . . . . . 62
2.9.2 Properties of Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
2.9.3 Function Arguments and Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . 67
2.9.4 Pointers and Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
2.9.5 Function Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
2.9.6 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
2.9.7 Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
2.10 String Manipulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
2.10.1 How to Handle a String of Characters (Text) . . . . . . . . . . . . . . . . 75
2.10.2 String Copy/Compare/Length . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
2.11 Command Line Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
2.11.1 Entering Command Line Arguments . . . . . . . . . . . . . . . . . . . . . . . 79
2.11.2 Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
2.12 Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
2.12.1 Mixture of Different Types of Variables . . . . . . . . . . . . . . . . . . . . 83
2.12.2 Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
2.12.3 Dynamic Memory Manipulation with malloc() . . . . . . . . . . . . . . 87

Part II Numerical Analysis


3 Note on Numerical Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
4 Roots of f (x) = 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
4.1 Bisection Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
4.2 Newton’s Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
4.2.1 Newton’s Method for a Single Equation . . . . . . . . . . . . . . . . . . . . 104
4.2.2 Newton’s Method for Simultaneous Equations (Optional) . . . . . 108
4.2.3 Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
Contents xi

5 Numerical Differentiation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111


5.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
5.2 Forward/Backward/Central Difference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
5.2.1 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
5.3 Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
6 Numerical Integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
6.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
6.2 Rectangular Rule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
6.3 Trapezoidal Rule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
6.4 Simpson’s Rule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
6.5 Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
7 Solving Simultaneous Equations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
7.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
7.2 Gauss-Jordan Elimination Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
7.2.1 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
7.3 LU Decomposition (Optional) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
7.4 Gauss-Seidel Method (Jacobi Method) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
7.5 Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
8 Differential Equations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
8.1 Initial Value Problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
8.1.1 Euler’s Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
8.1.2 Runge-Kutta Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
8.2 Higher Order Ordinary Differential Equations . . . . . . . . . . . . . . . . . . . . . . . 148
8.3 Boundary Value Problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
8.4 Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150

Appendix A: Gnuplot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153


Appendix B: Octave (MATLAB) Tutorial for C Programmers . . . . . . . . . . . . . . . 157
Appendix C: FORTRAN Tutorial for C Programmers . . . . . . . . . . . . . . . . . . . . . . 171
References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
First Steps to Run a C Program
1

In this chapter, we outline the fundamental process for executing a C program.


To run a C program successfully, the initial step involves writing C code using a text
editor, saving the code with the file extension “.c”, launching a C compiler to translate the
text into binary code, and subsequently, if all processes transpire smoothly, executing an
executable file (referred to as a.out in UNIX).1 For those of you starting your learning
path with C programming, it is absolutely necessary to carefully follow each step outlined
in the subsequent sections.

1.1 A Cycle of C Programming

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.

© The Author(s), under exclusive license to Springer Nature Switzerland AG 2025 3


S. Nomura, C Programming and Numerical Analysis, Synthesis Lectures on Mechanical
Engineering, https://doi.org/10.1007/978-3-031-83457-8_1
4 1 First Steps to Run a C Program

Fig. 1.1 Connecting to an ssh server via Windows search box

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

Fig. 1.2 Opening screen of the ssh server

Fig. 1.3 A nano session in a shell


6 1 First Steps to Run a C Program

1. Login to the server via ssh [email protected] from the search


box.
2. Using nano,4 a simple text editor, to compose your C program (Fig. 1.3).

$ 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

6. If there is a syntax error, go back to item 2 and reissue nano.

$ nano MyProgram.c

7. If there is no syntax error, run the executable file.

$ ./a.out

8. To logoff from the server, enter exit, logout or hit control-D.

1.2 UNIX Command Primer

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

• mv old.c new.c (Renames old.c to new.c.)


• rm program.c (Deletes program.c.)
• rm *.c (Do not do this. It will delete all the files with extension c.)
• whoami (Shows your username.)
• who (Shows who are logged on.)
• cal (Shows this year’s calendar.)
• cal 1980 (Shows the calendar of 1980.)

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.

• ˆf (Moves cursor forward by one character, f for forward.)


• ˆb (Moves cursor backward by one character, b for backward.)
• ˆd (Deletes a character on cursor, d for delete.)
• ˆk (Deletes entire line, k for kill.)
• ˆp (Moves to previous line, same as up arrow, p for previous.)
• ˆn (Moves to next line, same as down arrow, n for next.)
• ˆa (Moves to top of line, a for the first alphabet.)
• ˆe (Moves to end of line, e for end.)

1.3 Overview of C Programming

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.

1.3.1 Principles of C Language

Surprisingly the C language is based on a few simple principles summarized as follows:

1. A C program is a set of functions.


2. A function in C is a code that follows the syntax below:

type name(type var)


{
//your C code here.....
...
return value;
}
1.3 Overview of C Programming 9

3. A function must be defined before it is used.


4. A function must return a value whose type must be declared (e.g., int, float, double,
char). The last line of a function must be a return statement where value is the
value to be returned upon exit.
5. A function must take arguments and must have a placeholder () even if there are no
arguments.
6. The content of a function must be enclosed by “{” and “}”.
7. The special function, int main(), is the one executed first.9 It is recommended that
this function returns an integer value of 0.
8. All the variables used within a function must be declared.

1.3.2 Skeleton C Program

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;.

9 int main(void) is also used. If void is used, no arguments are accepted.


10 1 First Steps to Run a C Program

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().

You can execute this program by

$ nano hello.c

(Enter the content of the program above.)

$ gcc hello.c

(If it is not compiled, reedit hello.c.)

$ ./a.out
Hello, World!

Here is another program that does some scientific computation.

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:

$ gcc MyProgram.c -lm


12 1 First Steps to Run a C Program

Note that the -lm10 option is necessary when including .<math.h>.11


Here is a line by line analysis of the program:

Line 1 The program preloads a header file, .<stdio.h>.


Line 2 The program also preloads an another header file, .<math.h>. This header file is
necessary whenever mathematical functions such as.sin(x) are used in the program.
Line 3 This entire line is a comment. Anything surrounded by /* and /* is a comment
and is ignored by a compiler.13
Line 4 This is the declaration of a function, main(), that returns an integer value but
with no parameter.
Line 5 The { character indicates that this is the beginning of the content of the function,
main().
Line 6 Two variables, x and y, are declared both of which represent a floating number.
Line 7 The variable, x, is assigned a floating number, 6.28.
Line 8 The function, .sin(x), is evaluated where .x is 6.28 and the result is assigned to the
variable, y.
Line 9 The result is printed. First, a literal string of “Sine of” is printed followed by
the actual value of x and “is” is printed followed by the actual value of y, a period
and a new line.
Line 10 The function, main(), exits with a return value of 0.
Line 11 The } character indicates that this is the end of the content of the function, main().

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;
}

\n prints a new line.


2. Write a program to read two real numbers from the keyboard and prints their product.
Use the following code as a template. Do not worry about the syntax, Just modify one
place.

#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;
}

You have to use the -lm option when compiling.

$ gcc MyProgram.c -lm


$ ./a.out

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.

2.1 Variables and Data Types

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 (’).

© The Author(s), under exclusive license to Springer Nature Switzerland AG 2025 15


S. Nomura, C Programming and Numerical Analysis, Synthesis Lectures on Mechanical
Engineering, https://doi.org/10.1007/978-3-031-83457-8_2
16 2 Components of C Language

Table 2.1 Data types


Type Content Format Range Example
int Integer %d .−2147483647 ∼ 10
+2147483647
float Floating number %f .±2.9387e− 39 ∼ 3.14
±1.7014e + 38
double Double precision %lf .2
−63 ∼ 2+63 3.14159265358979
char Character %c ASCII code ‘a’

2.1.1 Cast Operators

When an operation between variables of different types is performed, the variables of a


lower type are automatically converted to the highest type according to the following order:

char < int < float < double


.

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;
}

The output is:

$ 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;
}

The output is:

$ 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.

2.1.2 Examples of Data Type

1. This program prints a character, ‘h’.

/*
Print a character
*/
#include <stdio.h>
int main()
{
char a = ’h’;
printf("%c\n",a);
return 0;
}

1 Another way to achieve this is by modifying a/b to 1.0*a/b.


18 2 Components of C Language

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.

/* Print a floating number */


#include <stdio.h>
int main()
{
float a = 10.5;
printf("%f\n",a);
return 0;
}

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

Examples Compare the following two programs:

#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;
}

The output is:

$ 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;
}

The output is:

$ 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;
}

The output is:

$ gcc program.c
$ ./a.out
Enter a character: A
You entered: A

2.3 Operators Between Variables

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

2.3.1 Relational Operators

Relational operators are mathematical operators commonly utilized in the if statement.


As shown in Table 2.2, a single equal sign (=) and a double equal sign (==) serve distinct
purposes.
Examples

1. if (a == b) printf("a and b are equal.\n");


else printf("a and b are not equal.\n");

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.

Table 2.2 Relational operators


Symbol Meaning
.< .a < b; a is less than b
.<= .a <= b; a is less than or equal to b
.> .a > b; a is greater than b
.>= .a >= b; a is greater than or equal to b
.== .a == b; a is equal to b.
.! = .a! = b ; a is not equal to b
2.3 Operators Between Variables 23

2.3.2 Logical Operators

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.

if (a > 0 && a < 100) printf("a is between 0 and 100.\n");

if (a > 0 || a < -5) printf("a is positive or less than -5.\n");

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.3.3 Increment/Decrement/Substitution Operators

In C programming, the expression a = a + 1 increments the value of a by 1. However,


a more concise notation, a++ or ++a, can be used as a shorthand. While this is not the
primary motivation, using a++ or ++a reduces memory usage by 2 bytes compared to a =
a + 1, appealing to C programmers, who often value minimalist code.2

Table 2.3 Logical operators


Symbol Meaning
.&& And
.|| Or
! Not

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

Table 2.4 Shorthand notations for assignment operations


Symbol Meaning
.++ .b =++a .a is incremented by 1 and assigned to .b. Same as
.a = a + 1; b = a;
.b =a++ .a is assigned to b first and incremented by 1. Same as
.b = a; a = a + 1;

.−− .b =−−a .a is decremented by 1 and assigned to .b. Same as


.a = a − 1; b = a;
.b =a−− .a is assigned to .b first and decremented by 1. Same as
.b = a; a = a − 1;

.+ = .a+ =b .a + b is assigned to .a. Same as .a = a + b;


.− = .a− =b .a − b is assigned to .a. Same as .a = a − b;
.∗ = .a∗ =b .a ∗ b is assigned to .a. Same as .a = a ∗ b;
./ = .a/ =b .a/b is assigned to .a. Same as .a = a/b;
%= .a% =b Remainder of .a/b is assigned to .a. Same as .a = a%b;

Table 2.4 lists shorthand notations for assignment operations.


Although ++a and a++ behave identically when used in isolation, there is a distinction
when they are part of an assignment. In b = ++a, ++a is referred to as the pre-increment
operator, which increments the value of a by 1 before assigning it to b. Conversely, in b
= a++, a++ is known as the post-increment operator. In this case, the value of a is first
assigned to b, and then a is incremented by 1.
Examples

i = 100;
i++;

is the same as

i = 100;
i = i + 1;

All of the following statements increment i by 1.

i = i + 1;
i+ = 1;
i++;
++i;
2.4 Control Statements 25

2.4 Control Statements

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

A block, denoted by { and }, following an if statement is executed when the condition


inside the parentheses (.. . .) is satisfied. If there is no block, only the next statement is
executed. The else clause is optional.
The following program tells whether an integer entered from the keyboard is between 1
and 100 or not.

#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;
}

The output looks like:


26 2 Components of C Language

$ 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.

2.4.2 for 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.

for (initial value; condition ; counter increment) statement;

Examples

1. The following program prints 0 to 9.

#include <stdio.h>
int main()
{
int i;
for (i = 0; i < 10; i++) printf("i = %d\n",i);
return 0;
}

The output is:

$ 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

2. The following program computes

. 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;
}

The output is:

$ 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

Note: The iteration variables (i, j, k, .. . .) must be always declared as int.


3. Approximating .ln 2
The following series
1 1 1 1
.1 − + − + − ... (2.1)
2 3 4 5
is known as the alternating harmonic series, and to converge to .ln 2 (natural logarithm).3
Therefore, by numerically summing up Eq. (2.1), one can obtain a numerical value of
.ln 2 (= 0.693147...). Equation (2.1) can be written as


 (−1)i+1
. . (2.2)
i
i=1

To implement Eq. (2.2) in C, use the following statement.

sum = sum + pow(-1, i+1)/i;

Note that pow(a,b) is a function found in math.h which returns .a b . A program to


implement Eq. (2.1) is as follows:

#include <stdio.h>
#include <math.h>
int main()
{
int i, n;
float sum = 0.0;
printf("Enter # of iterations = ");
scanf("%d", &n);

3 Integrating the both sides of the geometric series,

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

for (i = 1; i < n; i++)


sum = sum + pow(-1, i+1)/i;
printf("Approximation of log(2) = %f.\n ", sum);
/* Natural logarithms in C is log(x).*/
printf("Exact value of log(2) = %f.\n", log(2));
return 0;
}

The output is:

$ gcc prog.c -lm

$ ./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;
}

The output is:

$ 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.

2.4.3 while Statement

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

The output is:

$ 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;
}

The output is:

$ 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

2.4.4 do While Statement

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;
}

The output is:

$ 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

2.4.5 switch Statement

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;
}

The output is:

$ 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 (;).

2.5 Miscellaneous Remarks

Several key aspects of C and UNIX, while not warranting separate sections, are still important
to highlight. These are summarized below:

• Exiting an infinite loop.


Occasionally, a program may become trapped in an infinite loop, and the only option
might be to close the window or shutting down the machine. For instance, the following
program creates an infinite loop as shown in Fig. 2.1:

#include <stdio.h>
int main()
{
int i ;
for (i = 1; i > 0; i++) printf("loop");
return 0;
}
34 2 Components of C Language

Fig. 2.1 Trapped in infinite loop

To exit the infinite loop, press Control-C.4


• Output formatting
The appearance of the output generated by the printf() function can be customized.
Consider the following format control codes used within the printf() statement.

#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;
}

The output is:

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.

$ gcc -o MyProgram MyProgram.c

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;
}

The output is:

$ gcc prog.c
$ ./a.out
Enter radius = 2.0
The area of circle is = 12.566368.

Whenever the C compiler encounters PI, it replaces PI by 3.141592. It is customary to


use upper case letters to define constants with the #define preprocessor.
• Why does the return 0; in int main() need to return 0?
Returning 0 is not absolutely necessary. In fact, return -1; or return 2025; is
equally valid. However, by returning 0 to the operating system when int main() exits,
the operating system knows that the program has terminated normally with no errors or
issues. It informs the operating system that it can proceed without invoking any additional
error-handling procedures.

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

and the two roots are expressed as



−b ± b2 − 4ac
. x= .
2a
Note: sqrt is available in math.h. You need to compile the program with the -lm
option, i.e.

$ gcc MyProgram.c -lm

4. Write a C program to numerically compute the following series:


1 1 1 1
1−
. + − + − ··· ,
3 5 7 9
As this series is known to be convergent to .π/4,6 approximate .π using the program. Vary
iteration numbers. Note that the general term, .an , is expressed as

6 We start with the geometric series:

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

The concept of functions in C is important as a C program is essentially composed of a set


of functions.
You have already encountered a basic example of a C function, main(), which serves
as the entry point of every C program and is always executed first. However, aside from
this unique behavior, main() follows the same syntax as any other functions in C. This
common structure includes:

• Declaration of the return type (e.g., int for main())


• Listing arguments within parentheses (leave blank if there are none, e.g. main())
• Enclosing all statements within { and }
• Concluding with a return statement, which typically appears as the last line in the
function.

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;
}

The output is:

$ 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;
}

The output is:

$ 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 output is:

$ gcc prog.c -lm


$ ./a.out
Enter x and y separated by space = 2 4
2.000000 to power of exponent 4.000000 is 16.000000.
$ ./a.out
Enter x and y separated by space = -2 4
x must be positive !!

The program exits if .x is negative. Otherwise, it computes .x y and prints its value.

8 If .z = x y , taking the natural logarithm of both sides yields .ln z = ln x y = y ln x. Hence, .z =


e y ln x .
9 pow(x,y) in math.h returns . x y .
2.6 Functions 41

2.6.2 Locality of Variables Within a Function

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;
}

The output is:

$ 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().

2.6.3 Recursivity of Functions

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.

10 In older languages such as FORTRAN, recursion is not supported.


42 2 Components of C Language

Examples

1. Fibonacci numbers
The Fibonacci numbers [3], .an , are defined as follows:

an = an−1 + an−2 , a1 = 1, a2 = 1.
. (2.5)

Given .a1 = 1 and .a2 = 1, .a3 can be computed as .a3 = a2 + a1 = 1 + 1 = 2. Similarly,


a4 can be computed as .a4 = a3 + a2 = 2 + 1 = 3. Starting with .a1 and .a2 , .an for any
.

value of .n can be computed by repeatedly applying Eq. (2.5).11


As C functions can be defined recursively, coding the Fibonacci number is straightforward
as shown in the program below:

#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;
}

The output looks like:

$ gcc prog.c
$ ./a.out
Enter n = 12
144

The program simply translates Eq. (2.5) into the definition of fibo().12

11 There is an interesting background story with this number. See


www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibnat.
html#Rabbits.
12 The explicit formula of the Fibonacci sequence is given by

  √ n  1  √ n
1
2 1+ 5 − 2 1− 5
.an = √ .
5
.
2.6 Functions 43

2. Compute .1 + 2 + 3 + 4 + . . . + n using a recursive algorithm.


If we define
.sum(n) ≡ 1 + 2 + 3 + 4 + . . . + n,

the following relation holds:

.sum(n) = sum(n − 1) + n, sum(0) = 0.

Using this recursive property, the following program can compute

1 + 2 + 3 + ... + n
.

without using a for statement.

#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;
}

The output is:

$ gcc prog.c
$ ./a.out
Enter n = 100
1+2+....+100 =5050

2.6.4 Random Numbers, rand()

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;
}

The output is:

$ 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

The output is:

$ 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;
}

The output looks like:

$ gcc prog.c
$ ./a.out
Enter a seed integer = 10
1215069295

15 srand = seed random.


46 2 Components of C Language

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;
}

The output is:

$ 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;
}

The output may look like:

16 This is the birthday of UNIX!


17 If the program is run twice within 1 s, the same random number is generated.
2.6 Functions 47

$ gcc prog.c;
$ ./a.out
1729224502
(Wait a few seconds.)
$ ./a.out
1729224510

Generating Random Numbers in Various Ranges


With an integer random number generated by rand() and MAX_RAND, it is possible to
convert this number to any desired range, whether it be an integer range or a floating-point
number range. The following examples illustrate how to generate random numbers withing
various ranges using rand().

1. rand() returns an integer between .0 and RAND_MAX.


2. 1.0*rand()/RAND_MAX returns a floating number between .0 and .1.
3. 5.0*rand()/RAND_MAX returns a floating number between .0 and .5.
4. 10.0*rand()/RAND_MAX-5 returns a floating number between .−5 and .5.
5. rand()%718 returns an integer of 0, 1, 2, 3, 4, 5, or 6.
6. rand()%7+10 returns an integer of 10, 11, 12, 13, 14, 15, or 16.

Using Random Numbers to Do Simulations (Monte Carlo Method)


Using random numbers to conduct numerical simulations is called the Monte Carlo method
[4].19
As an example, consider the following integral:
 1 π
. 1 − x 2d x = . (2.6)
0 4
This integration represents the shaded area in Fig. 2.2. As it is a quarter of the whole unit
circle, its area is .π/4.20
Instead of carrying out the integral directly, the Monte Carlo method can be used to
approximate this area, providing an approximate value of .π. To understand how this works,

18 a%b returns the remainder of a/b.


19 Monte Carlo is a city in Monaco where one of the major businesses is casinos.
20 Mathematically, one can integrate the function directly as

 1   1
1 π
. 1 − x 2d x = 1 − x 2 x + sin−1 (x) = .
0 2 0 4
.
48 2 Components of C Language

Fig. 2.2 Monte Carlo method


for numerical integration

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;
}

The output looks like:


2.6 Functions 49

$ gcc prog.c -lm


$ ./a.out
Enter iteration number = 100
True value = 0.785398
Appx value = 0.790000
$ ./a.out
Enter iteration number = 1000
True value = 0.785398
Appx value = 0.814000
$ ./a.out
Enter iteration number = 100000
True value = 0.785398
Appx value = 0.787400

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. A sequence .an is defined by the following rule:

an+2 = −2an+1 + 3an , a0 = 2, a1 = −1.


.

Write a C program to compute .a17 .


2. (a) Write a function, int factorial(int n), which returns .n! (the factorial of .n,
i.e. .1 × 2 × 3 × . . . × n) using the recursive rule.
(b) Using int factorial(int n) above, write a program to compute

Fig. 2.3 Monte Carlo method


for numerical integration for
Problem 3
50 2 Components of C Language

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:

1. The values above are illustrative and not accurate.


2. Equation (2.7) can also be written as

 (−1)i x 2i
. cos(x) = ,
(2i)!
i=0

3. Use the first 10 terms of the series for your calculations.


2.7 Arrays 51

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 */

(for i = 1; i< .......) printf.......


return 0;
}

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.

2.7.1 Definition of Arrays

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

The output is:

$ gcc prog.c
$ ./a.out
The sum is = -4.500000.

2.7.2 Multi-dimensional Arrays

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:

a[0][0] a[0][1] a[0][2] a[0][3] a[0][4]


a=
. .
a[1][0] a[1][1] a[1][2] a[1][3] a[1][4]

Note that the index begins with 0, not 1.


The following program defines a 2 .× 5 matrix, mat, given as

1.0, 2.0, 3.0, 4.0, 5.0


mat =
. ,
6.0, 7.0, 8.0, 9.0, 10.0

and prints all the elements using double indices, i and j.

#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;
}

The output is:


54 2 Components of C Language

$ 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. Standard deviation and variance


In statistics, the average of a sequence, .{x1 , x2 , x3 , . . . xn }, is the arithmetic mean of its
components, defined as
1 
N
. X̄ ≡ xi .
N
i=1
The variance of the same sequence is defined as

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 output is:


2.7 Arrays 55

Fig. 2.4 General regression


analysis

$ gcc prog.c -lm


$ ./a.out
Average = 0.595222 S.D.= 0.310107

2. Regression analysis (curve fitting)


As another example of array usage, regression analysis, which determines the best fit
curve for given experimental data, is introduced. The following table presents . N mea-
sured data points as shown in Fig. 2.4.

X .x1 .x2 .x3 … .xN


Y . y1 . y2 . y3 … . yN

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 :

21 The dimension of . E 2 is the same as .a 2 and .b2 , hence it is denoted as . E 2 .


22 This method is known as the least squares method.
56 2 Components of C Language

∂ 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

These equations can be rewritten as:


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

Solving these equations using Cramer’s rule, we obtain:


 N 
 N 
 i=1 xi yi i=1 xi 
 N 
 i=1 yi N 
.a =     ,
 N 2 N 
 i=1 xi i=1 x i 
 N 
 i=1 xi N 
  
 N 2 N 
 i=1 xi x i i
y
 N i=1 
 i=1 xi i=1 yi 
N
b =   .
 N 2 N 
 i=1 xi xi 
 N i=1

 i=1 xi N 

The following program implements this calculation using 10 data points:

#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;
}

The output of the program is:

$ gcc prog.c
$ ./a.out
The regression line is 0.863636 x + -1.359998.

Regression analysis can be applied to a curved line, such as . y = ax 2 + bx + c.

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.

To exit from the loop, use the goto statement as

for (i = 0; i< N - 1; i++) for (j = i + 1; j < N; j++)


if (a[i] == a[j])
{count++; goto ExitLoop;}

ExitLoop:;
.....

Try .n = 100, 1000, 100000.


2. Roll a die 10, 1,000, and 10,000 times, and compute both the average and the standard
deviation for each case.
Template:

#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));

for (i = 0; i < 10000; i++) a[i] =....;

for (i = 0; i < 10000; i++) sum+= ....;


...................;
return 0;
}

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

Fig. 2.5 Curve fitting by


.y= ax 2 + b

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};

2.8 File Handling

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.

2.8.1 I/O Redirection (Standard Input/Output Redirection)

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

Table 2.5 I/O redirections


Notion Meaning
$ program .> filename Output to file
$ program .» filename Append output to file
$ program .< filename Get input from file

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

c:\dir > filelist.dat

2.8.2 File Handling (from Within a Program)

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;
}

The function fopen() accepts a (append), w (write), or r (read) as possible arguments.


The following program opens an external file named data.dat for writing and writes the
string “Hello, World!” to the file.

#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

Multiple files can be opened for reading or writing as follows:

#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).

2.9.1 Address Operator & and Dereferencing Operator *

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

Table 2.6 Memory map on a fictitious machine


Variable name Absolute memory address Content
.. . . 100 .. . .

.. . . 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.

Note that a number prefixed with 0x is interpreted as a hexadecimal value. Similarly, oX


indicates an octal format.
Compare the following two programs side by side, using an array a[3].

#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.

2.9.2 Properties of Pointers

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;
}

The output may vary for each machine:

$ 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;
}

The output is:

$ 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;
}

The output may vary depending on the machine:


2.9 Pointers 67

$ 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.

2.9.3 Function Arguments and 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;
}

The output is:

$ 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;
}

The output of this program is:

$ 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;
}

The output is:

$ gcc prog.c
$ ./a.out
Old a = 10.000000 and old b = 50.000000
New a = 50.000000 and new b = 10.000000

2.9.4 Pointers and Arrays

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;
}

The output from the program is:

$ 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

Table 2.7 Accessing array Array access Pointer equivalent


elements in two different
ways a[0] *a
a[1] *(a+1)
a[2] *(a+2)
2.9 Pointers 71

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;
}

2.9.5 Function Pointers

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;
}

The output is:

$ gcc prog.c -lm


$ ./a.out
2.000000
4.000000
-1.000000

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

• The asterisk (*) in C has the following meanings:

1. Multiplication when used as a binary operator (a * b).


2. Dereferencing a pointer when used as a unary operator (*pa).
3. Declaration of a variable as a pointer in the declaration statement (int *pa).

• The ampersand symbol (&) in C has the following meanings:

1. The address of a variable when used as a unary operator (&a).


2. Logical AND when used as a binary operator (a == 1.0 && b == 2.0).
3. Bitwise AND between two binary numbers (a & b).

• 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;
}

This program will produce the output:

-6 15

3. Remember that an array is also a pointer, as illustrated by:

#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:

a[20]={0.228952, 0.568418, 0.820277, 0.117099, 0.755212,


0.509299, 0.572073, 0.224526, 0.852861, 0.0612133, 0.175636,
0.568243, 0.0100543, 0.702012, 0.0345108, 0.146549, 0.189951,
0.144139, 0.261263, 0.474034};
2.10 String Manipulation 75

2.10 String Manipulation


2.10.1 How to Handle a String of Characters (Text)

In C, a string of characters (text) is managed as an array of individual characters. Therefore,


an array must be used to represent a string. Since any array variable is a pointer, as discussed
in Sect. 2.9, a variable representing a string is also a pointer.
Compare the following programs side by side:

#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;
}

The output is:

$ 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;
}

The output may appear as follows:

$ 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

2.10.2 String Copy/Compare/Length

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;
}

The output is:

$ 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;
}

The output is:

$ 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).

2.11 Command Line Arguments


2.11.1 Entering Command Line Arguments

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.

$ gcc prog.c -lm -o prog


$ ./prog
(interactive session)
80 2 Components of C Language

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:

$ gcc prog.c -lm -o prog


$ ./prog 3421 8756
(executes program to manipulate 3421 and 8756 and prints results)

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:

int main(int argc, char *argv[])

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;
}

32 The “c” in argc stands for “count.”.


33 The “v” in argv stands for “vector.” Technically, argv[] is a pointer to another pointer (an array
of arrays), as each element of argv[] is a string of characters.
2.11 Command Line Arguments 81

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;
}

The output looks like:

$ 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;
}

The output looks lie:

$ 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

by solving the equation .3x 2 − x − 1 = 0.


2. Write a program that uses command line arguments to compute the average and standard
deviation of the numbers entered as command line parameters. For example, executing

$ ./a.out 2. 4. -6. 10.2, -19.0

will output

average = 1.2, standard deviation=7.83199


2.12 Structures 83

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;
}

The output is:

$ 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;
}

It is also possible to use a pointer to a structure as

#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;
/*
..................
*/

printf("%s %d %d %c\n", ptr->Name, ptr->Midterm, ptr->Final, ptr->Grade);


return 0;
}
2.12 Structures 85

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;
/*
..................
*/

printf("%s\n", ptr -> Name);


return 0;
}

The typedef keyword is used to create a new name (alias) for an existing data type.
The syntax is

typedef existing_type new name;

For example,

typedef float real;


real a, b; // ’a’ and ’b’ are now of type float.

In this example, real is a new name for float. This makes the code more concise and
readable.
86 2 Components of C Language

The concept of structure extends to the concept of class, which is fundamental in


C++ (and Java).
A useful application of structures in scientific and engineering computations is the rep-
resentation of complex numbers. While the C language does not natively support complex
numbers,34 it is straightforward to implement them using structures. The following program
defines a complex type (.a + bi) and computes the addition of two complex numbers:

#include <stdio.h>
typedef struct
{float Real; float Im;} Complex;

Complex ComplexAdd(Complex z1, Complex z2)


{
Complex z;
z.Real = z1.Real + z2.Real;
z.Im = z1.Im + z2.Im;
return z;
}
int main()
{
Complex z1, z2, z;
printf("Enter real and imaginary parts of z1 separated by space = ");
scanf("%f %f", &z1.Real, &z1.Im);
printf("Enter real and imaginary parts of z2 separated by space = ");
scanf("%f %f", &z2.Real, &z2.Im);
z = ComplexAdd(z1, z2);
printf("%f + %f I \n", z.Real, z.Im);
return 0;
}

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;.

34 C++ includes a complex number class.


2.12 Structures 87

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;

Complex ComplexMultiply(Complex z1, Complex z2)


{
Complex z;
z.Real = z1.Real * z2.Real - z1.Im * z2.Im;
z.Im = z1.Real * z2.Im + z1.Im * z2.Real;
return z;
}
int main()
{
Complex z1, z2, z;
z1.Real = 0.25; z1.Im = -3.1412;
z2.Real = 0.98; z2.Im = 1.655;
z = ComplexMultiply(z1, z2);
printf("The product of z1 * z2 = %f + %f I.\n", z.Real, z.Im);
return 0;
}

The output is:

$ gcc prog.c
$ ./a.out
The product of z1 * z2 = 5.443686 + -2.664626 I.

2.12.3 Dynamic Memory Manipulation with malloc()

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.

35 For .z = a + bi and .z2 = c + di, .z1 × z2 = (ac − bd) + (bc + ad)i.


1
88 2 Components of C Language

#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:

ptr = malloc(n * size_type);

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.

Dynamic Memory Allocation Example


A simple example of using malloc() is as follows:

#include <stdio.h>
#include <stdlib.h>
int main()

36 Typically pronounced as “MAL-ock.”


37 A pointer whose type is undefined.
2.12 Structures 89

{
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;
}

In the program above, the malloc(500*sizeof(float)) allocates a block of 500


float values in memory and returns a pointer to the beginning of the block. However, it is a
generic pointer without a specific type. The type cast (float *) is necessary to convert
the generic pointer returned by malloc (of type void *) into a pointer of type
float.
If there is not enough memory to allocate the required space, NULL is returned. The
statement if (pa==NULL) checks whether enough memory is available to hold 500 float
values. The free(ptr) function releases the previously allocated memory.
The following example code prompts the user to enter the size of an array and checks if
sufficient memory is available for allocation. If there is not enough memory, the program
displays an error message and exits with an exit code of 1 (general error). If memory
allocation is successful, the program outputs the first and last elements of the array.
90 2 Components of C Language

#include <stdio.h>
#include <stdlib.h>
int main()
{
int i, size;
double *vector;

printf("Enter the size of double precision array =");


scanf("%d", &size);

vector = (double *)malloc(size * sizeof(double));


if (vector == NULL)
{
printf("Memory allocation failed!\n");
return 1;
}

for (i = 0; i < size; i++) vector[i] = i * 0.5;

// Print the first and last elements of the vector to verify


printf("Vector first element: %f\n", vector[0]);
printf("Vector last element: %f\n", vector[size - 1]);

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

1. calloc() The calloc() function stands for “contiguous allocation.” It is similar to


malloc() except that it initializes the allocated memory to zero. An example is:

int *arr = (int*) calloc(10, sizeof(int));

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:

arr = (int*) realloc(arr, 1000 * sizeof(int))

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 Author(s), under exclusive license to Springer Nature Switzerland AG 2025 95


S. Nomura, C Programming and Numerical Analysis, Synthesis Lectures on Mechanical
Engineering, https://doi.org/10.1007/978-3-031-83457-8_3
96 3 Note on Numerical Errors

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,

whose two roots are given by



−b ± D 
.x = , D= b2 − 4ac. (3.1)
2a
Consider the following equation:

. x 2 + 200000x − 3 = 0,

where the exact solutions are

. x1 = −200000, x2 = 0.000015.
98 3 Note on Numerical Errors

The coding for this equation is straightforward as follows:

#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;
}

The output is:

$ gcc prog.c -lm


$ ./a.out
x1 = -200000.000000, x2 = 0.000000

Apparently, .x2 is incorrect, while .x1 is correct.


From Eq. (3.1), the discriminant . D is calculated as follows:

. D = 2000002 − 4 × 1.0 × (−3)


= 40000000012,

D = 200000.00003.

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

The output is:

$ gcc prog.c -lm


$ ./a.out
x1 = -200000.000015, x2 = 0.000015

In numerical analysis, it is advisable to use double rather than float exclusively.


A variable declared as double occupies twice the memory space (8 bytes) of a float
variable (4 bytes), which increases the size of the resulting executable. However, this is a
minor trade-off for enhanced precision.
For applications requiring infinite precision, symbolic computation systems such as Math-
ematica [7] and Maple are recommended.
Roots of f (x) = 0 .
4

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:

1. The bisection method, which is guaranteed to find at least one root.


2. Newton’s method, which is generally faster for finding roots.

4.1 Bisection Method

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-

wise, the zero lies in the second subinterval .[midpoint, x2 ].


This process is repeated, successively halving the interval, until the interval becomes
sufficiently small or . f (x) is found to be zero at the midpoint. As illustrated in Fig. 4.1, this
procedure can be summarized as follows (Fig. 4.2):

1. Choose .x1 and .x2 such that . f (x1 ) f (x2 ) < 0.


2. Set .x3 ← (x1 + x2 )/2.
3. If . f (x1 ) f (x3 ) < 0, then, set .x2 ← x3 .
4. Else set .x1 ← x3 .
5. Until .|x1 − x2 | <  (small threshold) or . f (x3 ) = 0, repeat .2 ∼ 4.

√ of the bisection method for .x − 2 = 0. By


The following C code is implementation 2

solving this equation, an approximation to . 2 can be obtained.

Fig. 4.1 Bisection method

Fig. 4.2 Algorithm of


bi-section method
4.1 Bisection Method 103

/* Compute the square root of 2 */


#include <stdio.h>
#include <math.h>
#define EPS 1.0e-10
#define N 100

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:

$ gcc bisection.c -lm


$ ./a.out
Enter xleft and xright separated by
space = 0 1
Enter xleft and xright separated by space = 0 2
iteration = 34
x= 1.414214
104 4 Roots of . f (x) = 0

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.

4.2 Newton’s Method


4.2.1 Newton’s Method for a Single Equation

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).

Fig. 4.3 Iteration scheme in


Newton’s method
4.2 Newton’s Method 105

Fig. 4.4 Tangent line as


approximation to . f (x)

The tangent line passing through the point .(x1 , f (x1 )) with a slope of . f  (x1 ) is given by

. y − f (x1 ) = f  (x1 )(x − x1 ).

The condition for this line to intersect the .x-axis is

0 − f (x1 ) = f  (x1 )(x − x1 ),


.

which can be solved for .x as


f (x1 )
. x = x1 − .
f  (x1 )
Thus, the second approximation is

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

. f (x) = x 2 − 2, f  (x) = 2x,

Equation (4.1) is expressed as


xn2 − 2
. xn+1 = xn − .
2xn
106 4 Roots of . f (x) = 0

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:

1. Select an initial guess, .x1 .


2. Verify that . f  (x1 )  = 0.
3. Repeat

f (x1 )
(a) Compute .x2 ← x1 − f  (x1 ) .
(b) Update .x1 ← x2 .

4. Continue until .|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);

for (i = 0; i < 100; i++)


{
x2 = newton(x1);
if (fabs(x1 - x2)< EPS) break;
x1 = x2;
}
printf("iteration = %d\n", i);
printf("x = %f\n", x1);
return 0;
}

The output looks like:

$ gcc newton.c -lm


$ ./a.out
Enter initial guess = 2.9
iteration = 5
x= 1.414214
$ ./a.out
Enter initial guess = 0
Enter initial guess = 1.2
iteration = 4
x= 1.414214

In this example, convergence is achieved in 4 iterations, which is significantly fewer than


the number required by the bisection method.

As an additional note, the square root of .a (. a) can be approximated by solving the
equation . f (x) = x 2 − a. Using Newton’s method, the following iterative relation can be
derived:
x2 − a
. xn+1 = xn − n
2xn
 
1 a
= xn + .
2 xn
108 4 Roots of . f (x) = 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

In summary, while the bisection method guarantees convergence with an appropriate


initial interval, it converges relatively slowly. In contrast, Newton’s method converges much
more rapidly when a suitable initial guess is provided.

4.2.2 Newton’s Method for Simultaneous Equations (Optional)

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

2 This topic can be skipped.


4.2 Newton’s Method 109

∂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

and its inverse . J −1 is  


−1
x
3x 3 −2y 2
− 3x 32y
−2y 2
. J = y 3x 2 .
− 3x 3 −2y 2 3x 3 −2y 2

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

The iteration scheme is thus expressed as


⎛ ⎞
    xn4 −xn yn2 +1 +yn
xn+1 xn 3xn3 −2yn2
. = −⎝ 4xn3 yn −3xn2 −2yn3 +2yn
⎠. (4.6)
yn+1 yn
6xn3 −4yn2

Equation (4.6) can be implemented in C as:


110 4 Roots of . f (x) = 0

#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;
}

The output looks like:

$ gcc newton2.c -lm


$ ./a.out
Enter x, y and # of iterations = 1 1 4
0.877275 0.569947
$ ./a.out
Enter x, y and # of iterations = 1 1 5
0.877275 0.569947

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

1. Determine all the roots of the equation

e x − 3x = 0,
.

using the bisection method.


2. Find the value of .x that satisfies

. x sin x = e x − x sin x 2 ,

by applying Newton’s method within the interval .[−2, 2].


Numerical Differentiation
5

5.1 Introduction

Analytical differentiation of a function is always feasible, regardless of its complexity,


provided the function is explicitly defined. Computer algebra systems, such as Mathematica
[7] and Wolfram Alpha,1 can differentiate any analytical function exactly.
Numerical differentiation becomes necessary only when a function is provided in a
numerical form. Consider Table 5.1 which defines . f (x) numerically.

Table 5.1 Example of .x . f (x)


numerically given function
1.0 1.0
1.5 3.375
2.0 8.0
2.5 15.625

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

f (2.5) − f (2.0) 15.625 − 8.0


. = = 15.25.
0.5 0.5
2. By comparing . f (2.0) with . f (1.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

f (2.0) − f (1.5) 8.0 − 3.375


. = = 9.25.
0.5 0.5
The discrepancy between these two approximations suggests that neither provides an
accurate estimate of . f  (2.0).

Fig. 5.1 Graph of . f (x)

5.2 Forward/Backward/Central Difference

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)

From Eq. (5.3), the derivative . f  (x) can be approximated by

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.

Using Eq. (5.7), the approximation of . f  (2) from Table 5.1 is

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

Table 5.2 Example of numerical differentiation


Time 0.0 0.1 0.2 0.3 0.4 0.5
f (x) 0 0.0998 0.1986 0.2955 0.3894 0.4794
0.6 0.7 0.8 0.9 1.0
0.5646 0.6442 0.7173 0.7833 0.8414

To approximate the second-order derivative of . f (x),2 consider the following approach.


Adding Eqs. (5.5) and (5.6) yields

. f (x + h) + f (x − h) = 2 f (x) + h 2 f  (x) + . . .

from which . f  (x) can be approximated as

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;
}

2 If . f (x) represents a position at time, . x, . f  (x) is its acceleration.


5.2 Forward/Backward/Central Difference 115

The output is:

$ 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

4 f (x − h) − f (x − 2 h) = 3 f (x) − 2 h f  (x) + (higher order terms) . . .


.

from which . f  (x) can be solved as

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;
}

The output is:

$/home/nomura: gcc prog.c


$/home/nomura: ./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
1.000000 0.541500

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

120 140 160 180 200


97355 103422 127892 149626 160095

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.

6.2 Rectangular Rule


b
As illustrated in Fig. 6.1, the rectangular rule approximates. a f (x) d x by summing.n rectan-
gles over the interval .[a, b]. Depending on which side of the rectangle is chosen to represent
the value of . f (x), the left rectangular rule and the right rectangular rule can be considered.

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

Fig. 6.1 Rectangular rule

In the left rectangular rule, the integral, . I , is approximated by

. 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;
}

The output is:

$ 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.

6.3 Trapezoidal Rule

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

Fig. 6.2 Trapezoidal rule

#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;
}

The output is:

$ 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

6.4 Simpson’s Rule

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,

from which .a, .b and .c can be solved as


f (h) + f (−h) − 2 f (0)
a =
. ,
2h 2
f (h) − f (−h)
b = ,
2h
c = f (0).

Fig. 6.3 Second order


polynomial that passes three
points
124 6 Numerical Integration

Now that . y is determined, its integral from .−h to .h is


 h  h  
f (h) + f (−h) − 2 f (0) 2 f (h) − f (−h)
. (ax 2 + bx + c)d x = x + x + f (0) dx
−h −h 2h 2 2h
h
= ( f (−h) + 4 f (0) + f (h)) . (6.3)
3
The result in Eq. (6.3) indicates that the area under the curve passing through the points
.(−h, f (−h)), .(0, f (0)), and .(h, f (h)) can be expressed as a weighted average of . f (−h),
. f (0), and . f (h) with weights of 1, 4, and 1, respectively, as follows:

 h f (−h) + 4 f (0) + f (h)


. y dx = × (2h).
−h 6

Applying this approach to each segment in the interval .[a, b] yields


h
. I ∼ (( f 0 + 4 f 1 + f 2 ) + ( f 2 + 4 f 3 + f 4 ) + · · · + ( f 2n−2 + 4 f 2n−1 + f 2n ))
3
h
∼ ( f 0 + 4 f 1 + 2 f 2 + 4 f 3 + 2 f 4 + · · · + 2 f 2n−2 + 4 f 2n−1 + f 2n ) , (6.4)
3
where
b−a
. . h=
2n
Equation (6.4) is known as Simpson’s rule. Note that the number of partitions for Simpson’s
rule must be an even number (.= 2n).
For coding purposes, it is convenient to rewrite Eq. (6.4) as
h
. I = ( f 0 + f 2n )
3
h
+ × 4 ( f 1 + f 3 + f 5 + · · · + f 2n−1 )
3
h
+ × 2 ( f 2 + f 4 + f 6 + · · · + f 2n−2 ) . (6.5)
3
Simpson’s rule, as described in Eq. (6.5), can be implemented using the following code:

#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

printf("Enter number of partitions (must be even) = ");


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("%20.12f\n", (h/3.0)*(s1 + 4.0*s2 + 2.0*s3));
return 0;
}

The output is:

$ 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

1. (a) Evaluate analytically


 3 
. 2 −x 2 + 4x − 3 d x.
1
(b) Write a C program to numerically integrate the above integral using both the rectan-
gular rule and Simpson’s rule.
2. (a) Evaluate analytically
 1
. x log x d x.
0
(b) Write a C program to numerically integrate the above integral using Simpson’s rule.
Note that .log x → −∞ as .x → 0, so the challenge is to address this singularity at .x = 0.
Solving Simultaneous Equations
7

7.1 Introduction

In Chap. 4, we discussed numerical techniques for solving a single equation, . f (x) = 0. In


this chapter, we extend our focus to numerical methods for solving systems of simultaneous
equations.
In many engineering and scientific applications, discretization and linearization of prob-
lems often lead to the need to solve systems of linear simultaneous equations. Addressing
such systems is a fundamental topic in numerical analysis.
Students who have taken a linear algebra course may be familiar with Cramer’s rule,
which provides a method for solving linear simultaneous equations using determinants.
However, Cramer’s rule is practical only for small systems with 2 or 3 equations. In real-
world applications, systems can include up to a million equations, necessitating the use of
specialized methods designed for large-scale computation.
A system of linear simultaneous equations can be expressed in matrix-vector form as

. 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 .

Similarly, the determinant for a .3 × 3 matrix is defined as

a11 a12 a13


. a21 a22 a23 ≡ a11 a22 a33 + a12 a23 a31 + a21 a32 a13
a31 a32 a33
−a13 a22 a31 − a12 a21 a33 − a23 a32 a11 .

The determinant of an .n × n matrix (where .n > 3) can be expressed in a manner similar to


the cases above. It involves .n! terms, each of which is a product of .n elements.
Using determinants, the solutions for the following two simultaneous equations

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

Similarly, for three simultaneous equations,

a11 x1 + a12 x2 + a13 x3 = c1 ,


. a21 x 1 + a22 x 2 + a23 x 3 = c2 ,

a31 x1 + a32 x2 + a33 x3 = c3 ,

the solution is expressed as

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

Table 7.1 Time required for Cramer’s rule


n 10 12 14 16 18 20
Time 0.4 s 1 min. 3.6 h 41 d 38 years 16,000 years

7.2 Gauss-Jordan Elimination Method

The Gauss-Jordan elimination method1 is a practical technique for systematically solving


a large set of simultaneous equations numerically. While it is less efficient than the LU
decomposition method discussed in Sect. 7.3, it remains widely taught as a fundamental
numerical technique for solving simultaneous equations.
This method is based on the principle of linearity (also referred to as the principle of super-
position), which states that if two linear equations are combined, their linear combination
results in another linear equation. For example, consider the following two equations:

. a11 x1 + a12 x2 + . . . + a1n xn = c1 , (7.2)


a21 x1 + a22 x2 + . . . + a2n xn = c2 .
. (7.3)

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

The sequence, .{d1 , d2 , d3 , . . . dn }, is the solution for .{x1 , x2 , x3 , . . . xn }.

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.

1 Also known as Gaussian elimination or row reduction.


7.2 Gauss-Jordan Elimination Method 131


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.

Ref. line number x . . y z


. .= Comment
(1) 2 .−1 1 2
(2) .−1 3 3 3
(3) 2 1 4 1
(4) 1 .−1/2 1/2 1 (1) ÷ 2
.

(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)
.

(9) 0 2 3 .−1 .(6) − (4) × 2

(10) 1 .−1/2 1/2 1


(11) 0 1 7/5 8/5 (8) ÷ (5/2)
.

(12) 0 2 3 .−1

(13) 1 0 6/5 9/5 (10) − (11) × (−1/2)


.

(14) 0 1 7/5 8/5


(15) 0 0 1/5 .−21/5 (12) − (11) × 2
.

(16) 1 0 6/5 9/5


(17) 0 1 7/5 8/5
(18) 0 0 1 .−21 (15) ÷ (1/5)
.

(19) 1 0 0 27 .(16) − (18) × (6/5)


(20) 0 1 0 31 .(17) − (18) × (7/5)

(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:

. x = 27, y = 31, z = −21.

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;

for (k = 0; k < N; k++)


{
pivot = a[k][k];
7.3 LU Decomposition (Optional) 133

for (j = k; j < N + 1; j++) a[k][j] = a[k][j]/pivot;


for (i = 0; i < N; i++)
{
if(i != k)
{
d = a[i][k];
for (j = k; j < N + 1; j++) a[i][j] = a[i][j] - d*a[k][j];
}
}
}

for (i = 0; i < N; i++) printf("x[%d]=%f\n", i + 1, a[i][N]);


return 0;
}

The output is

$ gcc gaussjordan.c
$ ./a.out
x[1]=27.000000
x[2]=31.000000
x[3]=-21.000000

7.3 LU Decomposition (Optional)

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

Any matrix . A can be uniquely factorized as

. 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

2 This topic can be omitted if not relevant.


134 7 Solving Simultaneous Equations

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

Explicitly writing each element in Eq. (7.8) yields



⎪ u 11 = a11 ,
⎪ u 12 = a12 , u 13 = a13 , u 14 = a14

l21 u 11 = a21 , l21 u 12 + u 22 = a22 , l21 u 13 + u 23 = a23 , l21 u 14 + u 24 = a24
.

⎪ l u = a31 , l31 u 12 + l32 u 22 = a32 , l31 u 13 + l32 u 23 + u 33 = a33 , l31 u 14 + l32 u 24 + u 34 = a34
⎩ 31 11
l41 u 11 = a41 , l41 u 12 + l42 u 22 = a42 , l41 u 13 + l42 u 23 + l43 u 33 = a43 , l41 u 14 + l42 u 24 + l43 u 34 + u 44 = a44 ,

from which all the elements of .li j and .u i j can be solved as



⎪ u 11 = a11 , u 12 = a12 , u 13 = a13 , u 14 = a14

⎪ l = a21 , u = a − l u ,
⎨ 21 u 11 22 22 21 12 u 23 = a23 − l21 u 13 , u 24 = a24 − l21 u 14
. a a −l u

⎪ l31 = u31 , l32 = 32 u 31 12 , u 33 = a33 − l31 u 13 − l32 u 23 , u 34 = a34 − l31 u 14 − l32 u 24

⎩ a
11
a −l u
22
a43 −l41 u 13 −l42 u 23
l41 = u41 , l42 = 42 u 41 12 , l43 = u 33 , u 44 = a44 − l41 u 14 − l42 u 24 − l43 u 34 .
11 22

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)

To solve Eq. (7.9) for .x, follow these two steps:

1. Define .y ≡ U x and solve . Ly = c for .y.


2. Once .y is determined, solve .U x = y for .x.

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 .

The second equation, .U x = y, can be written as


⎛ ⎞⎛ ⎞ ⎛ ⎞
u 11 u 12 u 13 u 14 x1 y1
⎜ 0 u 22 u 23 u 24 ⎟ ⎜ x2 ⎟ ⎜ y2 ⎟
.⎜ ⎟⎜ ⎟ = ⎜ ⎟,
⎝ 0 0 u 33 u 34 ⎠ ⎝ x3 ⎠ ⎝ y3 ⎠
0 0 0 u 44 x4 y4

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 .

This procedure is known as backward substitution.


The number of operations (multiplications and divisions) required to decompose . A into
. A = LU is approximately .n /3, while the operations needed to solve . L y = c and .U x = y
3

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.

3 .n 2 can be considered negligible compared to .n 3 .


136 7 Solving Simultaneous Equations

#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;

/* LU decomposition and forward reduction */


for (j = 1; j < N + 1; j++) a[0][j] = a[0][j]/a[0][0];
for (k = 1; k < N; k++)
{
for (i = k; i < N; i++)
for (l = 0; l < k; l++) a[i][k] = a[i][k] - a[i][l]*a[l][k];
for (j = k + 1; j < N + 1; j++)
{
for (l = 0; l < k; l++) a[k][j] = a[k][N] - a[k][l]*a[l][j];
a[k][j] = a[k][j]/a[k][k];
}
}
/* Backward substitution */
for (k = N-1; k >= 0; --k)
{
for (l = k + 1; l < N; l++)
{
a[k][N] = a[k][N] - a[k][l]*a[l][N];
}
}
/* print the result */
for (i = 0; i <N; i++)
{
printf("x[%d]=%f",i, a[i][N]); printf("\n");
}
return 0;
}

The output is

$ gcc lu.c
$ ./a.out
x[0]=27.000000
x[1]=31.000000
x[2]=-21.000000

Notes

1. If . A is a symmetric matrix (i.e., .ai j = a ji ), the LU decomposition is known as the


Cholesky decomposition. The number of operations required for the Cholesky decom-
position is further reduced to approximately .n 3 /6.
2. If . A is a sparse matrix, then . L and .U will also be sparse.
3. If. A is a diagonal triangular matrix (as commonly encountered in finite element methods),
then . L and .U will also be diagonal triangular matrices.
7.4 Gauss-Seidel Method (Jacobi Method) 137

7.4 Gauss-Seidel Method (Jacobi Method)

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.

Equations (7.10) can be written as



⎨ x = (10 − y − 2z)/7,
. y = (8 − x − 3z)/8, (7.11)

z = (6 − 2x − 3y)/9.

Equation (7.11) are expressed in the form of an iterative scheme as



⎨ xn+1 = (10 − yn − 2z n )/7,
. y = (8 − xn − 3z n )/8, (7.12)
⎩ n+1
z n+1 = (6 − 2xn − 3yn )/9.

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);

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;
}

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

Convergence was achieved after 9 iterations.


There are instances where either the Jacobi method or the Gauss-Seidel method may fail
to converge. Generally, these methods are effective if the diagonal elements of the matrix
. A are significantly larger than the non-diagonal elements. More precisely, convergence is

assured if the largest eigenvalue, .λ, of . A is greater than 1.


For an example of solving a set of non-linear equations using the Gauss-Seidel method,
refer to Problem 2 in the Exercise.

7.5 Exercise

1. Solve the following 5 simultaneous equations by the Gauss-Jordan elimination method.




⎪ a11 x1 + a12 x2 + a13 x3 + a14 x4 + a15 x5 = c1 ,



⎨ a21 x1 + a22 x2 + a23 x3 + a24 x4 + a25 x5 = c2 ,
. a31 x1 + a32 x2 + a33 x3 + a34 x4 + a35 x5 = c3 ,



⎪ a 31 x 1 + a32 x 2 + a33 x 3 + a44 x 4 + a45 x 5 = c4 ,

⎩a x + a x + a x + a x + a x = c ,
n1 1 n2 2 n3 3 54 4 55 5 5
7.5 Exercise 139

where .ai j is given as

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}
};

and .ci is given as

c[5]={-1.92193, -2.35262, 2.27709, -2.67493, 1.84756};

2. Solve the following set of non-linear equations by the Gauss-Seidel method.



⎨ 27x + e cos y − 0.12z = 3,
x

. −0.2x 2 + 37y + 3x z = 6, (7.14)


⎩ 2
x − 0.2y sin x + 29z = −4.

Start with an initial guess of .x = y = z = 1.


Differential Equations
8

Differential equations are fundamental in scientific computation as they describe virtually


every physical phenomenon in nature. These equations represent the rates of change of
physical objects, and obtaining these rates is a key to understanding their behavior.
For instance, solving the Navier-Stokes equations in fluid mechanics can predict weather
patterns by modeling the movement of air particles. Similarly, stress equilibrium equa-
tions in solid mechanics help in assessing the strength and durability of materials, while
the energy equations in heat transfer enable predictions of temperature distribution—all of
which are governed by differential equations. Despite their significance, solving differen-
tial equations analytically is often challenging. Although differential equations have been
studied for over 400 years, many important equations lack closed-form analytical solutions.
Fortunately, numerical methods for solving differential equations, particularly initial value
problems, do not require advanced mathematics, as will be demonstrated in this chapter. This
chapter focuses on initial value problems and introduces Euler’s method and the Runge-Kutta
method.

8.1 Initial Value Problems

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

Fig. 8.1 Differential equation


(initial value problem)

. y(t0 ) = y0 ,

constitutes an initial value problem.


Equation (8.1) can be interpreted graphically, as illustrated in Fig. 8.1. Solving for . y(x)
in Eq. (8.1) is equivalent to plotting a curve that starts at .(t0 , y0 ), with its slope at each point
.(t1 , y1 ) determined by the function . f (t1 , y1 ).

Only the initial point and the slope . dy


dt at an arbitrary point are given.

8.1.1 Euler’s Method

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

. yn+1 = yn + h f (tn , yn ), (8.3)

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

Fig. 8.2 Euler’s method

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 .

The following C code implements Euler’s 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, 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;
}

The output is:

$ gcc euler.c -lm


$ ./a.out
t Euler Exact
t = 0.000000 1.000000 1.000000
t = 0.100000 1.100000 1.105171
t = 0.200000 1.210000 1.221403
t = 0.300000 1.331000 1.349859
t = 0.400000 1.464100 1.491825
t = 0.500000 1.610510 1.648721
t = 0.600000 1.771561 1.822119
t = 0.700000 1.948717 2.013753
t = 0.800000 2.143589 2.225541
t = 0.900000 2.357948 2.459603
t = 1.000000 2.593742 2.718282

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.

Example 2. Lorenz Equations/Strange Attractor/Chaos


Euler’s method is applicable not only to a single differential equation but also to a set of
simultaneous differential equations. Equation (8.5), known as the Lorenz equations, serve as
an intriguing example where a concise program in C can yield surprising results, paving the
way for contemporary research in non-linear dynamics. Lorenz1 studied Eq. (8.5) arising
from fluid circulation in 1963 [12], which are related to weather patterns. These equations
are non-linear differential equations that are deterministic; however, the behavior of the
solution can be neither periodic, divergent, nor convergent, depending on the parameters . p,
. R, and .b, as well as the chosen initial values.

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

double f1(double u, double v, double w)


{
return P*(v - u);
}

double f2(double u, double v, double w)


{
return -u*w + R*u - v;
}

double f3(double u, double v, double w)


{
return u*v - b*w;
}

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;

for (i = 0; i < 3000; i++)


{
u = u + h*f1(u,v,w);
v = v + h*f2(u,v,w);
w = w + h*f3(u,v,w);
printf("%f %f\n", u, w);
t = t + h;
}
return 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

Fig. 8.3 Lorenz equations generating chaos

using I/O redirection, and this file is placed or transferred to a directory accessible by
gnuplot.

$ gcc lorenz.c
$ ./a.out > lorenz.dat

Transfer lorenz.dat to a directory accessible by gnuplot. Launch gnuplot and


execute the following command:

plot ’lorenz.dat’ with line

The output from gnuplot is shown in Fig. 8.3.


The pattern in Fig. 8.3 was named chaos2 by Lorenz, as Eqs. (8.5) are deterministic, yet
the trajectory.(u, w) in Fig. 8.3 is neither periodic, convergent, nor divergent. By introducing
a small perturbation to the parameters and initial values, the pattern from Eqs. (8.5) changes
drastically. Refer to Problem 2 in the Exercise section for another example. As the pattern
in Fig. 8.3 resembles a butterfly, this phenomenon was later termed the butterfly effect.3

2 Also referred to as a strange attractor.


3 A movie, “The Butterfly Effect” (2004), was inspired by this concept.
8.1 Initial Value Problems 147

8.1.2 Runge-Kutta Method

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;

4 Pronounced as roo ng-uh-koo t-ah. Both were German mathematicians.


5 For example, see Iserles, A First Course in the Numerical Analysis of Differential Equations (Second
Edition), Cambridge University Press, 2008.
148 8 Differential Equations

t = t + h;
}
return 0;
}

The output is:

$ gcc rk4.c -lm


$ ./a.out
t= 0.000000 rk= 1.000000 exact=1.000000
t= 0.100000 rk= 1.105171 exact=1.105171
t= 0.200000 rk= 1.221403 exact=1.221403
t= 0.300000 rk= 1.349858 exact=1.349859
t= 0.400000 rk= 1.491824 exact=1.491825
t= 0.500000 rk= 1.648721 exact=1.648721
t= 0.600000 rk= 1.822118 exact=1.822119
t= 0.700000 rk= 2.013752 exact=2.013753
t= 0.800000 rk= 2.225540 exact=2.225541
t= 0.900000 rk= 2.459601 exact=2.459603
t= 1.000000 rk= 2.718280 exact=2.718282

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.

8.2 Higher Order Ordinary Differential Equations

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

With Eqs.(8.8), Eqs. (8.7) can be rewritten as


dy1
. = y2 , (8.9)
dt
dy2
. = −y1 , (8.10)
dt
with
. y1 (0) = 0, y2 (0) = 1.
The unknowns, . y1 (t) and . y2 (t), in Eqs. (8.9)–(8.10) can be solved simultaneously. The
following code uses Euler’s method to compute . y1 and . y2 .

#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

$ gcc highorder.c -lm


$ ./a.out
x y2 cos(x)
x= 0.000000 1.000000 1.000000
x= 0.010000 0.999900 0.999950
x= 0.020000 0.999700 0.999800
x= 0.030000 0.999400 0.999550
x= 0.040000 0.999000 0.999200
x= 0.050000 0.998500 0.998750
x= 0.060000 0.997901 0.998201
x= 0.070000 0.997201 0.997551
x= 0.080000 0.996402 0.996802
x= 0.090000 0.995503 0.995953
x= 0.100000 0.994505 0.995004

Similarly, a third-order differential equation, as well as higher-order differential equa-


tions, can also be transformed into a system of simultaneous first-order differential equations.

8.3 Boundary Value Problems

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

1. Solve the following differential equation:

. y  = −x 2 y, y(0) = 1,

using the following methods:


a. Exact (analytical) solution,
b. Euler’s method,
8.4 Exercise 151

c. The Runge-Kutta method,

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:

plot ’data1.txt’, ’data2.txt’, sin(x)

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.

gnuplot > set title ’My graph’ # Prints the title.


gnuplot > plot x**3-x-1 # Note ** (power)
gnuplot > plot sin(x) with dots # w d can be used.
gnuplot > plot sin(x) with impulse # w i also works.
gnuplot > plot [-5:5] sin(x)/(x**2+1) # [-5,5] specifies x range.
gnuplot > plot [-pi:pi] sin(x), sin(2*x), sin(3*x) # List of
functions to plot.
gnuplot > set xlabel ’My x axis’
gnuplot > set ylabel ’My y axis’
gnuplot > plot [-4:4] [0:10] x/exp(x) # Specifies x and y ranges.
gnuplot > splot [-pi:pi] [-2*pi:3*pi] sin(x*y) # splot draws 3-d
plot.

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

Fig. A.1 gnuplot example

To draw the graph shown in Fig. A.1, enter the following commands:

gnuplot> set isosamples 100


gnuplot> set hidden3d
gnuplot> set contour base
gnuplot> splot [0:pi][0:pi] sin(x*y)
gnuplot > quit

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 is:

$ gcc gnuplotdata.c -lm


$ ./a.out > data.dat

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:

gnuplot > cd ’C:\tmp’ % Change directory to C:\tmp.


gnuplot > plot ’data.dat’ with lines % Plot data in data.dat.
gnuplot > exit

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:

2 Double quotation marks (") can also be used.


156 Appendix A: Gnuplot

Fig. A.2 Plotting a graph from a file

gnuplot > cd ’c:\tmp’ % Save file to c:\tmp


gnuplot > set terminal gif size 640, 480 % size is optional
gnuplot > set output ’mygraph.gif’ gnuplot > plot sin(x)
gnuplot > quit

To save a graph in eps (Encapsulated PostScript) format, use the following command:

gnuplot > cd ’c:\tmp’ % Save file to c:\tmp.


gnuplot > set terminal postscript eps enhanced color
gnuplot > set output ’mygraph.eps’
gnuplot > plot sin(x)
gnuplot > quit

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.

4 The program can be downloaded from www.gnu.org/software/octave/.

© 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

Fig. B.1 Octave run from command line

B.2 Basic Operations


B.2.1 Principles of Octave/MATLAB

The following are some key principles of Octave/MATLAB:

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

clc % Clears screen.


1+3 2ˆ12
(3+2*i)*(2-4*i) % Complex algebra, i*i=-1.
abs(4-3*i) % Absolute value.
sin(pi) % pi is a reserved constant.
cos(2*pi)
log(2.718) % Natural logarithm.
log2(1024) % Logarithm with base 2.
log10(1000) % Logarithm with base 10.
format long % Output in long format. Just appearance.
sqrt(5) pi
format short % Output in short format. Just appearance.
sqrt(3)

B.2.2 Reserved Constants

Octave/MATLAB has reserved constants. Examples follow:

i % Imaginary number, i*i=-1.


j % Same as i, mainly used in electrical engineering.
clock % Current time (six elements).
date % Current date.
pi % 3.14156.
eps % Smallest tolerance number in the system.

Reserved constants in Octave/MATLAB can be assigned user-defined values. In the


example below, pi is assigned a user-defined value of 3.0, and this value remains in effect
until the next clear pi command is issued. As a result, variables such as i and j, which

are pre-defined as −1, can still be used as iteration variables.

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:

v1=[1 2 3] % Defines a 3-D row vector.


v1=[1, 2, 3] % Same as above. Separator is either a space or a comma.

v2=[1;2;3] % This defines a column vector.


v2=v1’ % Transpose of v1, i.e. column vector.

m1=[1 2 3;4 5 6;7 8 9] % Defines a 3x3 matrix.


m1=[1,2,3;4,5,6;7,8,9]; % A semicolon after statement suppresses
echo back.

m2=[-4 5 6; 1 2 -87; 12 -43 12] % Defines a 3 by 3 matrix.

m2(:, 1) % Extracts the first column.


m2(2, :) % Extracts the second row.

m2(2,3)=10; % Assigns 10 to the (2,3) element of m2.

m1*v2 % Matrix multiplication of m1 times m2.

inv(m1) % Inverse of m1.

[vec, lambda] = eig(m1) % Eigenvectors and eigenvalues of m1.

m1\m2 % Inverse of m1 times m2, same as inv(m1)*m2.

m1*v2 % Matrix m1 times vector v2.

a=eye(3) % 3x3 identity matrix.

a=zeros(3) % 3x3 matrix with 0 as components.

a=ones(3) % 3x3 matrix with 1 as components.

det(m1) % Determinant of m1.

To solve the following three simultaneous equations,


⎛ ⎞⎛ ⎞ ⎛ ⎞
14 5 x 4
.⎝8 1 2 ⎠⎝ y ⎠ = ⎝7⎠,

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

Although Octave/MATLAB can perform a variety of mathematical operations beyond


linear algebra (such as working with matrices and vectors), its fundamental design principle
is to handle numbers as matrices (including vectors). This extends to defining an interval
range as a row vector along with the corresponding function values.

octave.exe:1> % A sequence between 1 and 10 with an increment of 2.


octave.exe:1> x=[1: 2 :10]

1 3 5 7 9

octave.exe:2> %A sequence between 1 and 9 with equidistant 5 entries.


octave.exe:2> x=linspace(1, 9, 5)
x =

1 3 5 7 9

octave.exe:3> y=x; % Copies x to y.


octave.exe:4> z=x*y % This generates an error message and won’t work.
error: operator *: nonconformant arguments (op1 is 1x5, op2 is 1x5)
octave.exe:4> z=x.*y % This works. Component wise multiplication.
z =

1 9 25 49 81

octave.exe:5> z=x./y % Component wise division.


z =

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.1 : 10]; % Initial value, increment, final value.


y=sin(x);
plot(x, sin(x)); % Calls gnuplot.

x=linspace(0, 2, 20) % Between 0 and 2 with 20 divisions.


plot(x, sin(x))
162 Appendix B: Octave (MATLAB) Tutorial for C Programmers

Fig. B.2 Graph generated by Octave

%%%%%%%%%%%%%%%%%%
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.

disp(a) % Shows the content of a.


disp(’Enter a number = ’) % Prints a string on screen.
a=input(’Enter a number =’); % Prompts for input and value entered is stored in a.
fprintf(’The solution is %f\n’, a);% Same as printf() in C.
# %f and %d are available but not %lf in fprintf.

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 function, myfunction(x), is automatically available in Octave/MATLAB. In


the example above, function is the reserved keyword for a function, y is the output,
myfunction is the name of the function which should match the file name and x is

6 The fprintf() function in Octave/MATLAB is a combination of printf() and fprintf()


in C providing output options.
164 Appendix B: Octave (MATLAB) Tutorial for C Programmers

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.

function [x, y]=myfunction2(z)


x=zˆ2;
y=zˆ3;
end;

In Octave/MATLAB, this function can be called as:

[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);

In an Octave/MATLAB session, this script m-file can be executed as

octave.exe:10> cd ’C:\tmp’ % Changes the working directory.


octave.exe:11> myscript % Note no extension (m).
Enter a number=12
The square of 12.000000 is 144.000000

B.2.7 Conditional Statement

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

Unlike in C, parentheses are not required for expressions such as a > 2.


Equivalent C code would look as follows:

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 k=0:2:10 % k goes from 0 to 10 with an increment of 2.


disp(2*k)
end

Equivalent C code would look as follows:

for (k = 0; k <= 10; k = k + 2)


printf("%d\n",2*k);

B.3 Sketch of Comparison Between C and Octave/MATLAB

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;
}

2. Programs for series summation


Appendix B: Octave (MATLAB) Tutorial for C Programmers 167

#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;
}

Note that sum is a reserved function in Octave/MATLAB.


3. Programs to handle arrays
#include <math.h>
#define N 10
N=10;
int main()
x=[-4 1.2 1.3 2.5 -12.7 9 1.41
{
65.2 -2.1 2.36];
float x[]={ -4.0, 1.2, 1.3, 2.5,
sum=0;
-12.7, 9.0,1.41, 65.2, -2.1, 2.36};
for k=1:N
int i;
sum=sum+x(k)
float sum = 0.0, average, variance;
end
for (i = 0; i < N; i++) sum+= x[i];
ave=sum/N;
average = sum/N;
var=0;
for k=1:N
for (i = 0; i < N; i++) var=var+(x(k)-ave)ˆ2
variance += pow(x[i] - average, 2); end
variance = variance/(N - 1); var=var/(N-1);
fprintf(’Average = %f,
printf("avg.= %f std. dev. = %f \n ", Standard deviation= %f.\n’,
average, sqrt(variance)); ave, sqrt(var));
return 0;
}

4. Programs to use functions


168 Appendix B: Octave (MATLAB) Tutorial for C Programmers

#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;
}

printf("iteration = %d\n", i);


printf("x= %f\n", x1);
return 0;
}

5. Programs to handle external files


#include <stdio.h>
int main() fp1=fopen(’data1.dat’, ’r’);
{ a=fscanf(fp1, ’%f’);
FILE *fp1, *fp2; fclose(fp1);
float a, b, c;
fp1 = fopen("data1.dat","r");
fp2=fopen(’data2.dat’, ’w’);
fscanf(fp1, "%f", &a);
fprintf(fp2, ’This is the
fclose(fp1);
first line.\n’);
fclose(fp2);
fp2 = fopen("data2.dat","w");
fprintf(fp2, "This is the
first file.\n");
fclose(fp2);

return 0;
}
Appendix B: Octave (MATLAB) Tutorial for C Programmers 169

B.4 Exercise

1. Translate the following C code to an equivalent Octave/MATLAB m-file.

#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;
}

Note the following syntax:

fprintf(’x = %f, y= %f, z=%f\n’, x,y,z);

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

FORTRAN (FORmula TRANslation), developed in 1957 at IBM, was the predominant


programming language for engineers and scientists until the 1990s due to its extensive
library of subroutines. Even today, FORTRAN remains a major computational language in
specific scientific and engineering fields, such as computational fluid dynamics, and is often
used as a benchmark for supercomputers [14].
FORTRAN has evolved over time to incorporate modern programming concepts, with
FORTRAN 2023 being the latest version available at the time of writing.
This Appendix aims to assist C programmers in reading and making minor modifications
to legacy FORTRAN 77 code. A typical scenario might involve a C programmer who needs
to slightly modify a FORTRAN code written decades ago but does not need to develop new
FORTRAN code from scratch. Understanding the basic syntax of FORTRAN, which can
be related to C syntax, is sufficient for this task.
This Appendix provides a basic introduction to FORTRAN for C programmers and is not
intended as a comprehensive reference. Numerous detailed reference books on FORTRAN
are available for those seeking further information.

C.1 FORTRAN Features

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

Fig. C.1 IBM punch card

(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.

C.2 How to Run a FORTRAN Program

As FORTRAN is a compiled language, its execution process is analogous to that of C


programs.

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

This will produce an executable file, a.exe instead of a.out.

C.3 Sketch of Comparison Between C and FORTRAN

Similar to Octave/MATLAB, a C programmer can most effectively learn FORTRAN syntax


by comparing side-by-side examples of programs written in C and FORTRAN.
The following section presents the same programs used in the Octave/MATLAB section,
along with their corresponding FORTRAN implementations.

1. Programs to solve quadratic equations

/* This program computes roots


for quadratic equation. */
#include <stdio.h> C This program computes roots
#include <math.h> C for quadratic equations.
DOUBLE PRECISION A, B, C,
int main() c DISC, X1,X2
{ WRITE(*,*) "Enter three coeffs"
double a, b, c, disc, x1, x2;
READ(*,*) A, B, C
printf("Enter 3 coeffs = ");
scanf("%lf %lf %lf", &a, &b, &c); DISC=B**2-4.0*A*C

disc = b*b - 4*a*c; IF(DISC.LE.0.0) THEN


WRITE(*,*)"Imaginary roots."
if (disc <= 0) STOP
{ ENDIF
printf("Imaginary roots !\n");
return 0;
} X1=(-B+SQRT(DISC))/(2.0*A)
X2=(-B-SQRT(DISC))/(2.0*A)
x1 = (-b + sqrt(disc))/(2*a); WRITE(*,*) "Roots are ", X1, X2
x2 = (-b - sqrt(disc))/(2*a); STOP
END
printf("The roots are %f and
%f. \n", x1, x2);
return 0;
}
174 Appendix C: FORTRAN Tutorial for C Programmers

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.

– A.LE.B A is less than or equal to B (a <= b)


– A.LT.B A is less than B (a < b)
– A.EQ.B A is equal to B (a == b)
– A.GT.B A is greater than B (a > b)
– A.GE.B A is greater than or equal to B (a >= b)
– A.NE.B A is not equal to B (a != b)
– A.AND.B A and B (a && b)
– A.OR.B A or B (b || b)

2. Programs for series summation


#include <stdio.h>
#include <math.h> DOUBLE PRECISION SUM
INTEGER I
int main() SUM=0.0
{
int i; DO 10 I=0,999,1
double sum = 0.0; SUM=SUM+(-1)**I/
for (i = 0; i < 1000; i++) c (2.0*FLOAT(I)+1.0)
sum+= pow(-1,i)/(double)(2*i + 1); 10 CONTINUE
printf("approx= %f true value=
%f\n ", 4*sum, 4*atan(1.0)); WRITE(*,*) "Approx and
return 0; c exact values =", 4.0*SUM,
} c 4.0*ATAN(1.0)
STOP
END

• There is no FOR statement available in FORTRAN. For iterations, DO and CONTINUE


are used.
• DO 10 I=0,999,1 means to repeat the statements between the DO line and the
line that has the line number 10 with the iteration variable, I, from 0 to 999 with the
increment of 1. The CONTINUE statement is to literally keep going doing nothing
else.
Appendix C: FORTRAN Tutorial for C Programmers 175

3. Programs to handle arrays

#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

for (i = 0; i < N; i++) sum+= x[i];


DO 10 I=1,N
average = sum/N;
SUM=SUM+X(I)
10 CONTINUE
for (i = 0; i < N; i++)
variance += pow(x[i] - average, 2);
AVE=SUM/FLOAT(N)
variance = variance/(N - 1);
DO 20 I=1,N
printf("avg.= %f std. dev. = %f \n ",
VAR=VAR+(X(I)-AVE)**2
average, sqrt(variance));
20 CONTINUE
return 0;
VAR=VAR/(FLOAT(N)-1.0)
}
WRITE(*,*) "Average=",
c AVE,
c ’ Standard deviation =’,
c SQRT(VAR)
STOP
END

• PARAMETER can specify a constant.


• FLOAT converts an integer variable to a floating number.

4. Programs to use functions


176 Appendix C: FORTRAN Tutorial for C Programmers

#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

5. Programs to handle external files


#include <stdio.h>
int main() REAL A, B, C
{ B=3.14
FILE *fp1, *fp2; C
float a,b,c; OPEN(UNIT=1,
fp1 = fopen("data11.dat", "r"); c FILE=’data1.dat’,
fscanf(fp1, "%f", &a); c STATUS=’old’)
fclose(fp1) READ(1, *) A
WRITE(*,*) A
fp2 = fopen("data2.dat", "w"); CLOSE (1)
fprintf(fp2, "This is the first file.\n"); C
fclose(fp2); OPEN(UNIT=2,
c FILE=’data2.dat’,
return 0; c STATUS=’new’)
} WRITE(2,*) ’This is
c the first file.’
WRITE(2,*) B
CLOSE(2)
C
STOP
END

• 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

(a) Translate the following C code to a FORTRAN code:

#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

308.003, 244.414, 376.182};


float xysum = 0.0, xsum = 0.0, ysum = 0.0, x2sum = 0.0;
float a, b;
int i;

for (i = 0; i < N ; i++)


{
xsum+= x[i];
ysum+= y[i];
xysum+= x[i]*y[i];
x2sum+= x[i]*x[i];
}
a = (xysum*N - xsum*ysum)/(x2sum*N - xsum*xsum);
b = (x2sum*ysum - xysum*xsum)/(x2sum*N - xsum*xsum);
printf("%f %f \n", a, b);
return 0;
}

(b) Translate the following C code to a FORTRAN code:

#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;
}
References

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

You might also like