Spring 2015: C Programming for chemists
(with elements of C++)
Davide Ceresoli ([email protected])
Instituto di Scienze e Tecnologie Molecolari (CNR-ISTM)
March 29, 2015
Outline
1
Functions
2
Let’s do it
Functions/subroutines
Examples
v o i d c l e a r _ s c r e e n (); // t h i s is a d e c l a r a t i o n
s t r i n g i n t 2 w o r d (int i ) { ... } // t h i s is a d e f i n i t i o n
v o i d j a c o b i _ s w e e p (c o n s t m e s h 1 d & u_in , c o n s t m e s h 1 d & f , m e s h 1 d & u_out , d o u b l e o m e g a = 1 . 0 ) { ... }
d o u b l e c a l c _ e r r o r (c o n s t m e s h 1 d & u , c o n s t m e s h 1 d & u _ e x a c t );
int m a i n (int argc , c h a r * a r g v []) { ... }
Containers of code, make them simple, split large code into small
pieces
Encourage code
modularization
and code
reuse
Collection of functions are called
libraries
main
is a very special function!
More on functions
Functions cannot access variables declared
inside
other functions
(including
main
).
If you need to use global variables, declare them before the functions.
By default, variables are
passed-by-value
to functions, i.e. they are
copied
to the function.
As a consequence, a function cannot modify its parameters unless
they are
passed-by-reference
.
Functions can return only one value. If you need to return more, use
pass-by-reference
parameters.
Variables inside functions are
local
to each function invocation.
Of course, functions can contain for, if, do, while, ... etc as well as,
Functions
Local variables
v o i d f u n c () {
int a = 1 0 ;
c o u t < < " i n s i d e f u n c (): a = " < < a < < e n d l ; }
int m a i n () {
int a =7;
c o u t < < " b e f o r e c a l l i n g f u n c (): a = " < < a < < e n d l ; f u n c ();
c o u t < < " a f t e r c a l l i n g f u n c (): a = " < < a < < e n d l ; r e t u r n 0;
}
O u t p u t :
b e f o r e c a l l i n g f u n c (): a =7 i n s i d e f u n c (): a =10 a f t e r c a l l i n g f u n c (): a =7
Important
One fundamental rule of C/C++ is that variables live (i.e. exist) inside
the
{
...
}
they are declared and are destroyed right after.
Functions
More complex variable scode
int m a i n () {
int a = 10;
c o u t < < " h e r e a = " < < a < < e n d l ; {
d o u b l e a = 7 . 4 ;
c o u t < < " h e r e a = " < < a < < e n d l ; }
c o u t < < " h e r e a = " < < a < < e n d l ; r e t u r n 0;
}
O u t p u t : ?? ?? ??
Important
Functions
More complex variable scode
int m a i n () {
int a = 10;
c o u t < < " h e r e a = " < < a < < e n d l ; {
d o u b l e a = 7 . 4 ;
c o u t < < " h e r e a = " < < a < < e n d l ; }
c o u t < < " h e r e a = " < < a < < e n d l ; r e t u r n 0;
}
O u t p u t : h e r e a =10 h e r e a = 7 . 4 h e r e a =10
Important
Each
{
...
}
block can have local variables.
Corollary
Why this code doesn’t work?
int m a i n () {
int a = 10;
if ( a != 1 0 ) ; {
c o u t < < " a is d i f f e r e n t f r o m 10 " < < e n d l ; }
r e t u r n 0; }
O u t p u t :
a is d i f f e r e n t f r o m 10
Important
Corollary
Why this code doesn’t work?
int m a i n () {
int a = 10;
if ( a != 1 0 ) ; {
c o u t < < " a is d i f f e r e n t f r o m 10 " < < e n d l ; }
r e t u r n 0; }
O u t p u t :
a is d i f f e r e n t f r o m 10
Important
if (...);
becomes
if (...)
{}
.
Functions
Global variables
int a =7; v o i d f u n c () {
a = 1 0 ;
c o u t < < " i n s i d e f u n c (): a = " < < a < < e n d l ; }
int m a i n () {
c o u t < < " b e f o r e c a l l i n g f u n c (): a = " < < a < < e n d l ; f u n c ();
c o u t < < " a f t e r c a l l i n g f u n c (): a = " < < a < < e n d l ; r e t u r n 0;
}
O u t p u t :
b e f o r e c a l l i n g f u n c (): a =7 i n s i d e f u n c (): a =10 a f t e r c a l l i n g f u n c (): a =10
Important
A global variable must be declared outside every function, including
main
Functions
Pass by value
v o i d t w i c e (int a ) {
// h e r e a is t h e c o p y of a in m a i n ()
a = a *2;
c o u t < < " i n s i d e t w i c e (): a = " < < a < < e n d l ; }
int m a i n () {
int a =7;
c o u t < < " b e f o r e c a l l i n g t w i c e (): a = " < < a < < e n d l ; t w i c e ( a );
c o u t < < " a f t e r c a l l i n g t w i c e (): a = " < < a < < e n d l ; r e t u r n 0;
}
O u t p u t :
b e f o r e c a l l i n g t w i c e (): a =7 i n s i d e t w i c e (): a =14 a f t e r c a l l i n g t w i c e (): a =7
Important
By default variables are passed-by-value, i.e. copied to the function. You
can’t modify the passed variable.
Functions
Pass by reference
v o i d t w i c e (int & a ) {
// h e r e a is t h e s a m e a as in m a i n ()
a = a *2;
c o u t < < " i n s i d e t w i c e (): a = " < < a < < e n d l ; }
int m a i n () {
int a =7;
c o u t < < " b e f o r e c a l l i n g t w i c e (): a = " < < a < < e n d l ; t w i c e ( a );
c o u t < < " a f t e r c a l l i n g t w i c e (): a = " < < a < < e n d l ; r e t u r n 0;
}
O u t p u t :
b e f o r e c a l l i n g t w i c e (): a =7 i n s i d e t w i c e (): a =14 a f t e r c a l l i n g t w i c e (): a =14
Important
Functions
Pass by pointer
v o i d t w i c e (int * a ) {
// h e r e a is t h e p o i n t e r to a in m a i n ()
* a = (* a ) * 2 ;
c o u t < < " i n s i d e t w i c e (): a = " < < * a < < e n d l ; }
int m a i n () {
int a =7;
c o u t < < " b e f o r e c a l l i n g t w i c e (): a = " < < a < < e n d l ; t w i c e (& a );
c o u t < < " a f t e r c a l l i n g t w i c e (): a = " < < a < < e n d l ; r e t u r n 0;
}
O u t p u t :
b e f o r e c a l l i n g t w i c e (): a =7 i n s i d e t w i c e (): a =14 a f t e r c a l l i n g t w i c e (): a =14
Important
Equivalent (but C-style) to pass-by-reference.
Functions
Type conversion
v o i d t w i c e _ b y _ v a l u e (d o u b l e a ) { a = a * 2; }
v o i d t w i c e _ b y _ r e f (d o u b l e & a ) { a = a * 2; }
int m a i n () {
d o u b l e a = 7 . 0 ; int b = 1 0 ;
t w i c e _ b y _ v a l u e ( a );
t w i c e _ b y _ v a l u e ( b ); // i n t is c o n v e r t e d i n t o d o u b l e
t w i c e _ b y _ r e f ( a );
t w i c e _ b y _ r e f ( b ); // c o m p i l e r e r r o r : c a n n o t c o n v e r t i n t & to d o u b l e &
r e t u r n 0; }
Important
Iterative vs recursive
Factorial
int f a c t o r i a l _ i t e r a t i v e (int n ) {
if ( n < 0) r e t u r n -1; // e r r o r
if ( n == 0 || n == 1) r e t u r n 1; int f a c t = 1;
for (int i = 1; i <= n ; i ++) {
f a c t *= i ; // f a c t = f a c t * i
}
r e t u r n f a c t ; }
int f a c t o r i a l _ r e c u r s i v e (int n ) {
if ( n < 0) r e t u r n -1; // e r r o r
if ( n == 0 || n == 1) r e t u r n 1; r e t u r n n * f a c t o r i a l _ r e c u r s i v e ( n - 1 ) ; }
int m a i n () {
c o u t < < f a c t o r i a l _ i t e r a t i v e ( 1 2 ) < < e n d l ; c o u t < < f a c t o r i a l _ r e c u r s i v e ( 1 2 ) < < e n d l ; r e t u r n 0;
}
Iterative vs recursive
The recursive version is very elegant!
It works because
N
! =
N
·
(
N
−
1)! and 0! = 1! = 1.
Termination must be in head, recursion in the tail!
How does it work?
f a c t o r i a l _ i t e r a t i v e (5)
- > r e t u r n 5 * f a c t o r i a l _ i t e r a t i v e (4)
- > r e t u r n 3 * f a c t o r i a l _ i t e r a t i v e (2)
- > r e t u r n 2 * f a c t o r i a l _ i t e r a t i v e (1)
- > r e t u r n 1
Iterative vs recursive
Let’s do it
Can we do the same thing with the sum of integers from 1 to
N
? Yes,
because:
S
(
N
)
≡
X
1
≤
i
≤
N
i
S
(
N
) =
N
+
S
(
N
−
1)
,
S
(1) = 1
Important
Recursive functions are more elegant but consume more resources (CPU
and memory).
Functions
More difficult: passing functions to functions
d o u b l e f i r s t _ d e r i v a t i v e (d o u b l e (& f )(d o u b l e) , d o u b l e x , d o u b l e h =1 e -6) {
r e t u r n ( f ( x + h ) - f ( x ))/ h ; }
int m a i n () {
c o u t < < f i r s t _ d e r i v a t i v e ( sin , 0 . 0 ) < < e n d l ; r e t u r n 0;
}
Important
Passing functions to functions
Let’s do it
Modify the code above and using a loop from 0 to 360
◦
and verify that
the derivative of the
sin
function calculated this way, is very close to the
cos
function. The argument of the sin and cos functions must be in
radians. Write an auxiliary function that converts between degrees and
radians (
π
=
M PI
)
Finding zeros of functions
The bisection method is the most robust to find the zero of a function in
an interval.
1
Start from the interval bound
a
and
b
, evaluate the function values
f
(
a
),
f
(
b
). Make sure that
f
(
a
)
·
f
(
b
)
<
0
2
Calculate the mid-point:
c
= (
a
+
b
)
/
2 and evaluate
f
(
c
).
3
If
f
(
a
)
·
f
(
c
)
<
0, the zero is in the [
a
,
c
] interval. Set
b
=
c
and go
back to step 2.
4
If
f
(
b
)
·
f
(
c
)
<
0, the zero is in the [
b
,
c
] interval. Set
a
=
c
and go
back to step 2.
The bisection method
Let’s do it
1
Solve: exp(
x
2
−
2)
−
3 = 0 for
x
∈
[0
,
2].
2
Using the temperature conversion functions of before, find which
temperature is the same value in
◦
C and
◦
F (i.e. solve
x
= 32 +
9
5
x
).
Hints
1
double bisection(double (&f)(double), double a, double
b, double precision=1e-6)
{...}
2
the modulus of a real number is given by the:
double fabs(double
x)
function.
Easter assignment: integral of a function
Trapezoid rule
The definite integral of a function
f
(
x
) can be approximated as:
Z
b
a
f
(
x
)
dx
'
h
2
N
X
k
=1
(
f
(
x
k
+1
+
f
(
x
k
))
where
x
k
=
a
+
k h
,
h
= (
b
−
a
)
/
N
and
N
is the number of points for the
discretization.
1
Calculate
R
10
0
(exp(
x
) +
x
)
dx
using
N
= 10
,
100 and 1000.
Hint
Recap
Functions
Local vs global variables
Pass-by-value vs pass-by-reference
Iterative vs recursive
Questions?
Next lecture
int
main ()
{
cout <<
" H a p p y E a s t e r "
<< endl ;