2011年8月7日日曜日

[C/C++] マシンイプシロン

マシンイプシロンを求めるプログラム例

#include <stdio.h>

int main(void)
{
  double deps=1.0//Machine epsilon, type: double
  double dtmp;      //Working variable, type: double
  float feps=1.0;   //Machine epsilon, type: float
  float ftmp;       //Working variable, type: float

  //Compute machine epsilon of type double
  //During dtmp = deps + 1.0, divide dtmp by 2

  for(dtmp = deps + 1.0; dtmp > 1; deps /= 2.0, dtmp = deps + 1.0);
  printf("Machine Epsilon of type double is %-16g\n", 2.0*deps);
  printf("Unit roundoff of type double is %-16g\n", deps);

  //Compute machine epsilon of type float
  //During ftmp + 1 > 1, divide ftmp by 2

  for(ftmp = feps + 1.0; ftmp > 1; feps /= 2.0, ftmp = feps + 1.0);
  printf("Machine Epsilon of type float is %-16g\n", 2.0*feps);
  printf("Unit roundoff of type float is %-16g\n", feps);

  return 0;

}

上記のプログラムをコンパイル,実行すると以下のようになります.
Machine Epsilon of type double is 2.22045e-16     
Unit roundoff of type double is 1.11022e-16     
Machine Epsilon of type float is 1.19209e-07     
Unit roundoff of type float is 5.96046e-08     

2011年8月3日水曜日

[C/C++] std::ofstream,std::ifstream,std::string

std::ofstream, std::ifstreamは,C言語でいうとFILE*とfprintf()関数群,fscanf()関数群に相当します.

std::ofstream
std::coutのファイル出力版となり,使い方はstd::coutとほぼ同じです.
テキストファイルのオープンとクローズは,以下のようになります.

#include <fstream>

int main()
{
  {
    std::ofstream ofs("ex.txt");  //Open file in output mode
    ofs << "output sample \n";
  }                               //Point!!
  return 0;

}

上記のソースの,"//Point!"の行がポイントです.C言語では,ブロック内で定義された変数は,そのブロックの終わりで消滅します.C++の場合も同様なのですが,更にその変数が削除される時に実行される関数(デストラクタ)を定義できます.
std::fstream型にはファイルがオープンされていたらクローズするというデストラクタが定義されているので,"//Point!"の行で"ex.txt”ファイルは自動的にクローズされます.なので,ファイルのクローズ漏れを心配しなくて済みます.

std::ifstream
std::coutの入力版で,>>で入力します.
以下は,std::string(後述)を使った文字列の入力例です.std::stringはバッファのサイズを気にせずに使うことができます.

std::string
C言語では,文字列処理が難しくなります.例えば,文字列を読み込むまで必要なバッファ・サイズが分からないので,1バイトづつ読み込む度にrealloc()で1バイトづつ拡張しますが,効率的とは言えません.また,その際にバッファの拡張を(自分で)設定したバイト毎に行うなどの工夫が必要となります.
しかし,std::string型はそのようなバッファの拡張処理を内部に隠蔽しているので,簡単に使用することができます.
なお,下記のstd::cinはstd::coutの標準入力版です.

      std::string temp;
      std::cin >> temp;

また,以下の一覧表のように,C言語の標準ライブラリの文字列操作関数のほとんどの機能をカバーしています.

C標準ライブラリ(string.h)
C++標準ライブラリ(string)
C++での書き方
std::string foo;
foo=”dummy”;          // “dummy”
foo += “+str1”;        // “dummy+str1”
foo.append(“+str2”); // “dummy+str1+str2”
foo.compare(“dummy”);                 // false
foo.compare(“dummy+str1+str2”); // true
(foo == “dummy”)                        // false
(foo == “dummy+str1+str2”)        // true
他多数
foo.find(‘m’);    // 2
foo.find(“str”);  // 6
foo.find_first_of(“0123”); // 9
foo.c_str() + foo.find_first_of(“0123”);
foo.rfind(“str”);  // 11
foo.find_first_not_of(“dmuy”) // 5
foo.size();  // 15
C言語文字列形式へ変換
foo.c_str();  // char*型で出力される
無し
std::istringstream iss(foo);
std::string token;
while (std::getline(iss, token, ‘+’))
{
std::cout << token << ” “;
} // dummy str1 str2
無し
地域(locale)を考慮した比較
無し
地域(locale)を考慮したコピー
無し
C言語のエラー処理用

2011年8月2日火曜日

[C/C++] 倍精度型浮動小数点数フォーマットを出力

キーボードから入力された数の倍精度型浮動小数点フォーマットを出力するプログラム例を以下に示します.

#include <stdio.h>
#include <string.h>

int main(void)
{
  double a;
  char c[sizeof(a)];  // The data size of char type is 1 byte.
  int i, j, k;

  printf("Please input a number ---> ");
  scanf("%lf", &a);

  memcpy(c, &a, sizeof(a)); // Copy the contents of a to array c.

  printf("sEEEEEEE EEEEdddd dddddddd... \n");

  for( i = sizeof(a) - 1 ; i >= 0; i--)
  {
    for ( j = sizeof(c[i]) * 8 - 1; j >= 0; j--)
    {
      k = c[i] & (1 << j); //Logically multiply j by one by one.
      printf("%d", k ? 1 : 0); //If k = 0, 1 is displayed. 0 is displayed if k = 0.
    }
    printf(" ");
  }
  printf("\n");

  return 0;

}

実行結果は以下のようになります.
Please input a number ---> 777
sEEEEEEE EEEEdddd dddddddd... 
01000000 10001000 01001000 00000000 00000000 00000000 00000000 00000000 

上記のプログラムでは,1バイトごとにビット演算を行うため,char型の配列を用意しています(char型のデータサイズは1バイト,int型のデータサイズは4バイト).
文字配列 c の各要素 c[i] のサイズは1バイトなので,64ビットの倍精度型変数を格納するには c の配列サイズを8とする必要があります.
ここでは,sizeof関数を使って配列サイズをsizeof(a)=8としています.

memcpy関数は,メモリに格納されている内容をコピーするための関数で,例えばmemcpy(a, b, n)とすると,バッファbのnバイトの内容をバッファaにコピーします.
aとbには領域の先頭アドレスが入ります.
プログラムでは,データサイズが8バイトであるdouble型変数aの内容を文字配列cにコピーするために,aの先頭アドレス&a,配列cの先頭アドレスcおよびデータサイズsizeof(a)=8をmemcpy関数の実引数としています.

2011年8月1日月曜日

std::cout について

std::coutは,
    #include <iostream>
で使えるようになります.
std::coutはC言語のstdoutに相当し,使い方は簡単で,以下のようにして用います.

    std::cout << "Hello. " << std::endl;

std::cout は,数値型や文字列型など多くの型に対応しています.出力の際に改行したい場合は,以下のように“\n”;を用います.

    std::cout << “parameter a =” << a << “parameter b =” << b << “\n”;

ただし,std::cout よりも printf() 系の方が高速らしく,高速でログを出力する場合等は,printf() 系関数を使った方が良いと言われています.
上記の printf() では変数の型も適切に指定する必要がありますが,std::cout は書式の指定が不要であるため,書式の指定ミスが発生しません.
注意が必要なのは std::cout では,char型変数は文字として出力されるので,例えば
    char x=48; std::cout << x;
とすると”48″ではなくて”0″(’0’のASCIIコードは0x30=48)が出力されます(デバッグ用ならstd::cout << (int)x;などとしておけば良い).

文字列として取り出したい場合には,std::coutの代わりに
    std::stringstream ss;
が使えます(ex. ss << Hello;).
ファイルへの出力も同様で,
    std::ofstream(ファイル名) ofs;
とします(ex. ofs << file01).
最初の宣言が異なるだけで,他は同じです.

std::coutでも書式指定が可能ですが,その際には
  #include <iomanip>
が必要となります.例えば,
  std::setw(n)
はそれ以降に出力されるフィールド幅の指定が可能です(std::setw(n)の詳細はこちらを参照のこと).

2011年7月1日金曜日

[C/C++] 数値計算のための初歩的な計算プログラム例 その1

このポストは,計算物理のためのC/C++言語入門を自習した際のメモです.

整数の足し算プログラム例

// sample01.cpp
#include <iostream>

using namespace std;

int main()
{
  cout << "Computer in Physics" << endl;
  return 0;

}

<プログラムの解説>
int a,b,c; の文は変数宣言文と呼ばれます.
この例では,a b cの三文字を 整数の値を記憶する変数として使うという宣言です.
この場合,変数の宣言と同時に,変数が値を記憶することが できるようにメモリが確保されます.

次の
  •  a=1; で aに 1が代入される
  •  b=2; で bに 2が代入される
ことになります.そして ' c=a+b;  'で 1+2 が計算されて,その結果の 3が cに代入されます.

最後のprintf文は" "で包まれた部分のみを画面に表示しますが,' %d 'はそのまま画面に出力されるのではなく" "の後ろにある変数 の値がその' %d 'のある場所に置換されて画面に出力されます.' %d 'が複数ある場合は" "の後ろの変数がその並んだ順に置換されて表示されます.

' = 'は代入演算子と呼 ばれていて,' = 'の右側の式を計算した結果を左の変数に代入する 演算子です.したがって' a+b=c; 'のようなことはできません.
また変数には最初から何かしらの値が入っているのですが,代入をすることによって その値を書き換えられます.数学のように「等しい」という意味はありません.

コンパイル,実行例:
$ g++ sample02.cpp
$ ./a.out
 1 + 2 = 3 
$

2次方程式の解を計算するプログラム例

// sample03.cc
#include <iostream>
#include <cmath>

using namespace std;

int main()
{
  double a, b, c;
  double xp, xm;

  a =  1.0;
  b = -4.0;
  c =  2.0;

  xp = (-b + sqrt(b*b-4.0*a*c))/(2.0*a);
  xm = (-b - sqrt(b*b-4.0*a*c))/(2.0*a);

  cout << "The solutions are " << xp << " and " << xm << " . " << endl;
  return 0;

}

<プログラムの解説>
double a, b, c; と, double xp, xm; の宣言文により,これらの文字は実数の値を記憶しておく変数になります.
a, b, cは2次方程式の係数の値を表すことにして適当な値を代入します.
xp, xmは解の複合のplusのとminusのを表すことにして,解の公式通りに計算式を立ててxp, xmに代入します.

' sqrt() 'は,平方根を計算する関数です.' sqrt() 'のような数学関数を使用するには,プログラム冒頭に ' #include <math.h> 'と記載しておく必要があります.

sqrt()関数は,負の値の平方根は計算できません(関数に与える値が負の場合,sqrt()関数を実行すると内部エラーが生じて,プログラムは異常終了します).
また,割る数' a 'が 0であっても内部エラーでプログラムは異常終了します.

printfでdoubleで宣言した変数の値を表示させるには,表示したいところに' %lf 'を置きます.' %d 'とすると,とんでもない値が表示されます.

コンパイル,実行例:
$ g++ sample03.cpp
$ ./a.out
The solutions are 3.414214 and 0.585786
$

1から10までの和を計算するプログラム例

ループ機能を用いて,1から10までの整数の総和を求めるプログラムです.

// sample04.cpp
#include <iostream>
#include <cmath>

using namespace std;

int main(void)
{
  int n;        //Declaring variables
  int sum = 0//Declaring  variables and initializing

  //for loop
  for (n = 1; n <= 10; n++)
    {
      sum += n; //Sum up
    }
  cout << "The sum is " << sum << ". " << endl;
  return 0

}

<プログラムの解説>
  int sum = 0;
は,変数の宣言と代入を同時に行う方法です(宣言と同時の代入を初期化という).

forループの書式は以下の通りです.

    for( 初期化文 ; ループ条件文 ; ループ変更文)
    {
        ループ実行文(複数可);
    }

 for文では,まず初期化文が実行されます(上記のプログラムでは,n=1 ).次にループ条件文が評価されます(上記のプログラムでは, n <= 10) .そして,もしこれが 真ならループ実行文が実行されます.偽ならこの for 文を終了して次の命令(上記のプログラムでは, printf文)に移ります.
ループ実行文がすべて実行されるとループ変更文が実行されます(上記のプログラムでは, n++ . n++ は整数 nの値をひとつだけ増やす 命令).
ループ変更文が実行された後,再び頭に戻って,ループ条件文が評価されます.
このようにして,ループ条件文が偽になるまで{ }で囲まれた文が繰返し実行されます.

    sum += n;
は,sumの値をnだけ増やすという命令です.

コンパイル,実行例:
$ g++ sample04.cpp
$ ./a.out
The sum is 55

-----
ループ文にはforループ(繰り返し回数が自明な場合によく使われる)のほかに,繰り返し回数が自明でない場合によく使われる

  • whileループ
  • do whileループ

が あります.
whileループの書式は以下の通りです。

    while( ループ条件文 ){
        ループ実行文(複数可);
    }

whileループでは,ループ条件が満たされる場合に,ループ内の命令を実行する命令です.なので,始めからループ条件が満たされなければループ内の命令を 一度も実行せずに,次に書かれている命令に移行します.

do whileループの書式は以下の通りです.

    do{
        ループ実行文(複数可);
    }while( ループ条件文 );

do whileループでは,ループ内の命令を無条件で一度実行してからループ条件を検査します(最低一度はループ内の命令が 実行される).なので,ループ条件が満たされる場合には,ループ内の命令をもう一度 実行します.
-----



平方根を繰り返し計算で求めるプログラム例
do whileループの使用例として平方根の値を sqrt()関数を使わずに 四則演算の繰り返しだけで計算するプログラム例を以下に示します.

// sample05.cpp
#include <iostream>

using namespace std;

//Calculate y = sqrt (x) for the given x.
int main()
{
  double x, y;
  double a, da;

  cout << "input a possitive number x = " << endl;
  cin >> x;    //Read a positive real number from the key.

  a = x;

  // do while loop
  do {
    da = 0.5*(a - x/a);   //Calculate the decrement da of a
    a -= da;              //Reduce a by da
  } while(da > 0.000001); //Continue the loop while the decrease is large

  y = a;                  //Answer

  cout << "The root of " << x << " is " << y <<". " <<endl; 

}

' scanf 'はキーボードから値を読み込み,指定の変数にその値を代入する命令です.

このループでは変数 a の値を da だけ修正する作業を,修正分 daが 0.000001 よりも大きい間,ループを繰り返します.
修正分の大きさで ループを繰り返すかどうかを判定する場合は' do whileループ 'が 効果的です.

上記のプログラムでは,Newton法を用いており,以下に解説します.
指定したxに対する y = sqrt(x) を計算することは、 y*y - x == 0 となる yを見つけることと同じことです.

  • yの値として試しにある適当な値 a を置いてみて, a*a - x を 計算する(ここで,aの値を微少量 da だけ減らすと a*a - x の値は 2*a*da だけ減る).
  • a*a - x の値が0まで減るにはあと、a*a - xだけ減らせば良いので,大雑把に見積もって,a の値を da = (a*a - x)/(2*a) = 0.5*(a-x/a)だけ減らせばかなり近くなる.

という変更作業を何度も繰り返していくと,解に近づきます(減少分daの値は,常に正とは限らないので, 上記のプログラムのループ条件文 da > 0.000001 は万能ではないことに注意).

コンパイル,実行例:
$ g++ sample05.cpp
$ ./a.out
Input a possitive number = 2
The root of 2.000000 is 1.414214. 

-----
 if文 
ある条件が満たされているときのみに命令を行うという例外処理の際には,if文を用います.書式は以下のようになります.
  
    if(条件文){
        条件文が真のときに実行する命令群;
    }else{
        条件文が偽のときに実行する命令群;
    }

elseより後の部分は,もし偽の命令群が無いのなら省くことができます.
例えば,絶対値を計算したいときは,以下のようにします.
    if (a < 0){
        a = -a;
    }
-----
break文とcontinue文 
ループの中で,何らかの事情でループから抜け出たい場合があります.そういうとき には break命令を使います.
またループで次のステップに強制的に飛びた いときはcontinue命令を使います.
以下に例を示します.

    while (a < b){
        ループ実行文
        if (ループ脱出条件文) break;
    }

    for (a = 1; a < 10; a++){
        if(ループネクスト条件文)continue;
        ループ実行文
    }
-----

配列を用いたデータ処理
2つの点数の平均値を求めるプログラム例
以下に,2つの値の平均値を計算するプログラム例を示します.
各点数はint型の整数で,平均値はdouble型の実数としています.

#include <iostream>
#define N 3

using namespace std;

int main()
{
  int score[N];           //Array declaration
  int n, sum;
  double mean;

  for (n=0; n<N; n++)     //Assign values to each element of the array
  {
    cout << "Input the score of No." << n << ": ";
    cin >> score[N];
  }

  sum = 0;
  for (n=0; n < N; n++)
  {
    sum += score[n];      //Calculation of total points
  }

  mean = (double)sum / N; //Calculation of average points
  cout << "The mean score is " << mean << ". " << endl;

}

<プログラムの解説>
平均値を計算する15行目では,二つの整数値(int型) score1, score2 の和を実数値(double型)となるように,和の計算の前に
        (double)
と書かれています.この記載により,和の値は double型に変換されます.
コンピュータの四則演算は同じ型同士で演算することになっています.なので,割り算は,実数の2.0で割ることになります.
  mean = (score1 + score2) / 2;
とした場合は,分母,分子ともに整数なのでint型の計算が行われます( (1 + 2) /2 = 3 / 2 = 1 となる).
ただし,変数' mean 'はdouble型としているので,結果は double型に変換されて出力されます.

10個の平均値を計算するプログラム例
上記のプログラムの発展型とも言える,10個の平均値を求めるプログラムを作成してみます.先のプログラムと同様にして, score1, score2, ... , score10 の変数を用意しても良いのですが, 宣言や平均値の計算式の記述が面倒になります.

そこで,複数個の変数をまとめて簡潔に扱う配列変数を用います.配列変数は,同じタイプの複数個の変数をひとつのまとまりとして, その個々の要素の読み書きは,配列名に要素の番号を表す数字を添えることで行います.

具体的には,以下のようになります.
配列変数の宣言は
  int score[N];
のようにします.ここでは,' score 'という配列を作成し,その中身として int型の変数が10個入っていることになります.
配列の中のある変数に値を代入 したり,値を取り出したりするには,
  score[n] = 90;           // 配列変数の要素 n に90を代入する(書き込む)
  a = score[n];             // 配列変数の要素 n の値を変数 a に代入する(読み込む)
として,' n 'で要素の番号を指定(配列のどの要素かを指定)することによって実現されます.
この数字を添え字(index)と呼び,その範囲は 0からその配列変数を宣言した時の[ ]の中の数字 - 1の数字までとなります(Fortranでは配列の添字は1からとなりますが,C/C++では 0 からとなる).例えば' int score[100] 'とした場合は,' score[0] 'から'  score[99] 'までの変数を指定します.上記の例で,' score[100] ',' score[-1] 'とした場合はエラーとなります.

以下に,配列変数を用いて10個の平均値を計算 するプログラム例を示します.

#include <stdio.h>
#define N 10

int main(void)
{
  int score[N];           //Array declaration
  int n, sum;
  double mean;

  for (n=0; n<N; n++)     //Assign values to each element of the array
  {
    printf ("input the score of No.%d = ", n);
    scanf("%d", &score[n]);
  }

  sum = 0;
  for (n=0; n < N; n++)
  {
    sum += score[n];      //Calculation of total points
  }

  mean = (double)sum / N; //Calculation of average points
  printf ("The mean score is %lf\n", mean);

}

<プログラムの解説>
2行目で
#define N 10
としています.この定義によって,コンパイラーが N の部分を自動的に 10 に置き換えてくれます(必要に応じて,Nの値を変えてやれば,取り扱う値の数が変わる).
上記のプログラムでは,for文と配列変数を用いることにより,シンプルなプログラムでと大量のデータを扱うことができるようになっています.
配列変数を初期化には,
        int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
のように,配列変数の宣言時に行うことができます.ただし,初期化ができるのは宣言時のみで,宣言後にはできません.

関数の設計
無限等比級数の総和を求めるプログラムを作ることで,関数を設計(自作)する例を以下に示します.

第1段階:関数化しないプログラム例:

// sample08.cpp
#include <iostream>

using namespace std;

int main()
{
  double r, term = 1.0, sum = 0.0;        // r: proportionality constant

  cout << "Input a number below 1.0: ";   //Input of proportional constant r
  cin >> r;

  while (term > 0.0)
  {                     //Repeat while incrementing term is greater than 0
    sum += term;                          //Increase of item
    term *= r;                            //Multiply by the multiplier to obtain the value of the next term
  }

  cout << "The total of this series is " << sum << ". " << endl;

  return 0;

}                                         //End of function definition

第2段階:比例乗数の入力と結果の出力を関数化したプログラム例:

// sample09.cpp
#include <iostream>

using namespace std;

void CalcSeries(void)                    //Definition of function CalcSeries ()
{
  double r, term = 1.0, sum = 0.0;        //r: proportionality constant

  cout << "Input a number below 1.0: ";
  cin >> r;                               //Input of proportional constant r

  while (term > 0.0){                     //Repeat while incrementing term is greater than 0
    sum += term;                          //Increase of item
    term *= r;                            //Multiply by the multiplier to obtain the value of the next term
  }

  cout << "The total of this series is " << sum << endl;
}                                         //End of function definition

int main(void)
{
  CalcSeries();                           //Execution of function CalcSeries () Part 1
  CalcSeries();                           //Execution of function CalcSeries () Part 2

}

第一段階のプログラム(sample08.cpp)の,比例乗数の入力から結果の出力までの部分を,新しい一つの関数にまとめて,この新しい関数の名前を' CalcSeries 'としました.
このように,ある処理を関数化して,その関数名を記載するだけで処理を実行することが可能になります(上記の例では,' CalcSeries(); 'と記載して,比例乗数の受け付け,総和の計算,出力が行われる).

<プログラムの解説>

void CalcSeries(void

の行が,新たな関数の宣言となり,続く中括弧' { } 'の範囲内が関数の作業内容の定義となります.プログラムは,

int main(void)

から実行が開始されます.処理を関数にしたので,' CalcSeries() 'と書くだけで処理が実行され,何度も同じ計算を行いたい時には非常に便利です.
特に,大きなプログラムを作成する際には,上記のように関数化すると,プログラムのどこで何が行われるのかがわかるので,プログラムを作成しやすくなります。

main関数
main(void)にも中括弧' { } 'があり,関数の格好をしています.main(void)は,メイン関数と呼ばれるプログラムの実行時に自動的に実行される特別な関数です.

関数のprototype宣言
main()関数の定義の前で' CalcSeries()関数 'を定義しましたが,定義の順序は逆でも構いません.ただし,その際には以下のように,プログラムの冒頭の #include 文の直後に' CalcSeries() 'が関数であることの宣言をしなければなりません.

#include <stdio.h>
void CalcSeries(void

この宣言を,関数の' prototype宣言 'と呼びます.prototype宣言はコンパイラーに,それが関数であることや,関数の正しい呼び出し方を伝えます.

変数のスコープ
' CalcSeries()関数 '内では3つの変数 r, term, sum が宣言かつ定義されています.この変数は' CalcSeries()関数 '内でしか利用することができません.

例えばmain関数内で' r=0.5; 'としても,代入を行うことはできません.このように,変数には利用できる範囲があり,それをスコープと呼びます.スコープはその変数の宣言を囲む最も内側の中括弧{ }の範囲と決まっています.
上記の例では,' CalcSeries()関数 '内の変数はその関数内がスコープとなります.

スコープが,関数内など狭い範囲に限定された変数のことを' local変数 (局所変数) ''と呼びます.
関数の外側で変数を宣言すると,それを囲む中括弧が無いので,その変数のスコープはそれ以降のプログラム全体となります.つまりプログラムの冒頭の,関数の外で変数を宣言すれば,その変数はどこからでも読み書きができます.このような変数のことを' global変数 (広域変数) 'と呼びます.global変数 (広域変数)の利用は慎重にすべきとされています.

変数の寿命
変数には,スコープ以外に' 寿命 'があります.変数に寿命がくる(消滅する)と,変数に記憶されていた値は利用できなくなります.
変数の寿命を理解するための例を以下に示します.変数をforループの中括弧内で宣言したとします.

void func( void )
{
  for(int i=0 ; i<10 ; i++){
    int j=0;
  }
}

この変数jのスコープの範囲はこのforループの中括弧内となります.forループでは,ループの一回転が終ると for の直後の()の中の作業をするので,中括弧から出ることになります.
よって,変数 j はこのとき(中括弧から出たとき)に消滅します.そして,次の一回転の冒頭で再び宣言(誕生)され(上記の例では,宣言と同時に0で初期化されます),ループの毎回において変数の誕生と消滅が繰り返されます.注意が必要なのは,上記の例のように宣言した変数は,ループの次の回まで値が保存されている保証がないということです.

関数の外で定義した広域変数の寿命はプログラムの開始から終了まで(スコープと同じ)となります.

関数内の変数の寿命を長くする(括弧から出ても消滅しないようにする)ことが可能です.

void Visit( void )
{
  static int times = 0;
  times++;
  printf("You have visitted this function %d times.\n", times );
}

上記の例は,関数が呼び出されるたびに呼ばれた回数を表示する例です.
変数' times 'の宣言の前に' static 'と記されています.こうすると,この変数は関数が終了しても消滅せずに残るので,再度この関数を呼び出した際に,保存されていた変数の値が用いられます.' times 'の値を0に初期化する作業が行われるのは,この関数を最初に呼び出した時だけとなり,次回に呼び出された際にはこの初期化は行われません.
よって,上記の例では,呼び出された回数が表示されることになります.このような変数をstatic変数と呼びます.なお,スコープが関数内であることには変わりありません.

第3段階:値を取り引きする関数を設計する
先のプログラムで設計したCalcSeries()関数は入出力を行いますが,機能を削減して,無限等比級数の総和を計算するだけの関数に変更します.
その場合には,この関数を呼び出す際に,比例乗数の値を渡す必要があります.また計算結果をこの関数から受け取る必要もあります.すなわち,

int main(void)
{
  double r, sum;
  sum = CalcSeries(r); 
}

のように関数を呼び出します(数学関数と同じような扱い方).関数をこのように設計するには次のようにします.

//sample10.cpp
#include <iostream>

using namespace std;

double CalcSeries(double);  //Prototype declaration of function CalcSeries()

int main(void)
{
  double r, sum;

  cout << "Input a number bellow 1.0: ";
  cin >> r;                 //Input acceptance of proportional multiplier r -Part 1-
  sum = CalcSeries(r);      //Execution of function CalcSeries () -Part 1-
  cout << "The total of this series is " << sum << ". " << endl;

  cout << "Input a number bellow 1.0: ";
  cin >> r;                 //Input acceptance of proportional multiplier r -Part 2-
  sum = CalcSeries(r);      //Execution of function CalcSeries () -Part 2-
  cout << "The total of this series is " << sum << ". " << endl;

  return(0);

}

double CalcSeries (double r)  //Definition of function CalcSeries ()
{
  double term = 1.0, sum = 0.0; //Declaration and definition of local variables

  while(term > 0.0){          //Repeat while incrementing term is greater than 0
    sum += term;              //Increase of item
    term *= r;                //Multiply by the multiplier to obtain the value of the next term
  }

  return(sum);                //Return total value to caller

}

<プログラムの解説>
prototype宣言を用意したので,関数の定義をmain関数の後ろに置くことができます.

CalcSeries()関数の定義の所では関数の名前の直前に double が付いています.これは,この関数が計算結果としてdouble型の値を呼び出し,元に戻すことを意味しています.これに対応して関数の最後に
  return(sum); 
として確かに double型の値を返しています.このような戻す値を戻り値と呼びます.

関数の名前の直後の (double r) はこの関数が呼び出される際に,double型の値ひとつを受け取って,その値をこの関数のlocal変数rに代入することを意味しています.このような受ける値のことを引数と呼びます.

prototype宣言の方では関数の名前の直後は (double) だけで r がありません(prototype宣言では,受け取った値を代入する変数の名前までをコンパイラーに伝える必要は無い).

このように関数を定義すると、この関数の呼び出し元であるmain関数で
  sum = CalcSeries(r); 
とすると,main関数内の local変数 r の値が,CalcSeries()関数内の引数としてlocal変数 r にコピーされます. 両者の変数は名前は同じでも別物の変数です.CalcSeries()関数内で r を上書きしてもmain関数内の r の値は不変です.そして CalcSeries()関数の計算結果 sum を return(sum) とすることで,この変数の値が関数の呼び出し元へ戻り値としてコピーされます.そして,最後にそれがmain関数内のlocal変数sumに代入されます.