C プログラミング (C Programming)

[2020/06/20, H.Aman]

[11] ポインタ(1)演習課題

〆切は 6/26(金)の 14:10 です.

  • 演習 1

    サンプルプログラム①を別名(ex01.c)にコピーし, それを次のように編集しなさい:
    • int 型の変数 x だけでなく,char 型の変数 c,double 型の変数 d も宣言する.
    • scanf による入力部分は削除し,代わりに x には 123, c には 'a',d には 3.14 をそれぞれ代入する.
    • x, c, d のそれぞれの内容,バイト数,アドレスを表示する.

    実行例 ※以下の例では,バイト数をあえて * と書いている.* で表示せよという意味ではないので誤解しないこと.
    なお,表示されるバイト数,ならびにアドレスとその形式は各自の環境で異なる可能性がある.(特に Mac や Linux 環境の人)
     変数 x の
       内容 = 123
       バイト数 = 4 バイト
       アドレス = 0061FF18 番地
     変数 c の
       内容 = a
       バイト数 = * バイト
       アドレス = 0061FF17 番地
     変数 d の
       内容 = 3.140000
       バイト数 = * バイト
       アドレス = 0061FF08 番地
  • 演習 2

    まず,このプログラム(ex02.c)をダウンロードしなさい.
    そして,これを編集してサンプルプログラム②と同様のことを 整数だけでなく,文字と実数についても行うようにしなさい.

    なお,文字の入力については少し工夫がいるので, ダウンロードしてもらったプログラムにはあらかじめ書き込んである.
    (いったん長さ 2 の char 配列 str に文字列として読み込み,その 1 文字目 str[0] を char 変数 c へ代入している.)

    入出力例(キーボードからの入力は赤色
     整数を一つ入力してください:123
     変数 x のアドレス(0061FF10)を px に代入します
     0061FF10 番地の内容は 123 です
     0061FF10 番地の内容を +1 します
     0061FF10 番地の内容は 124 です
    
     文字を一つ入力してください:a
     変数 c のアドレス(0061FF0F)を pc に代入します
     0061FF0F 番地の内容は a です
     0061FF0F 番地の内容を +1 します
     0061FF0F 番地の内容は b です
    
     実数を一つ入力してください:3.14
     変数 d のアドレス(0061FF00)を pd に代入します
     0061FF00 番地の内容は 3.140000 です
     0061FF00 番地の内容を +1 します
     0061FF00 番地の内容は 4.140000 です
  • 演習 3

    まず,このプログラム(ex03.c)をダウンロードしなさい.
    そして,この中の関数 round1 を完成させなさい. ただし,main 関数の内容は一切変更しないこと.
    • 関数 round1 は,引数として int 型変数のアドレス(ポインタ)を受け取り,
      その int 型変数の内容(整数)を「1 の位を四捨五入した値」に書き換える.
      (ヒント)
      仮に元の値を x とすると,x % 10 が 1 の位の値となるので,これを d とおく.
      そこで,
      • d < 5 ならば 1 の位は切り捨てとなり, x - d が求める値になる.
      • そうでなければ,切り上げとなり,x + 10 - d が求める値になる.

    入出力例(キーボードからの入力は赤色
    整数を一つ入力してください:53
    1 の位を四捨五入した値 = 50
    整数を一つ入力してください:55
    1 の位を四捨五入した値 = 60
  • 演習 4

    まず,このプログラム(ex04.c)をダウンロードしなさい.
    そして,この中の関数 sort を完成させなさい. ただし,swap と main の内容は一切変更しないこと.
    関数 sort は,三つの double 型変数の内容を大きい順(降順)に並べ替える働きをする.

    これを実現する方法はシンプルであり,以下の 3 個の if 文と swap の呼出しを行うだけで完了する.
    ただし, 以下のやり方はあくまでも main 関数で書いた場合の方法になっているので少し工夫が必要である:
      if ( x < y ){
         swap(&x, &y);     ← x < y ならば x と y を入れ替え
      }
    
      if ( x < z ){
         swap(&x, &z);     ← x < z ならば x と z を入れ替え
      }
    
      if ( y < z ){
         swap(&y, &z);     ← y < z ならば y と z を入れ替え
      }
    まず,x と y の値の入れ替えは,main 関数の中であれば上のように swap(&x, &y); で実行できるが,
    関数 sort の中ではこのままでは動かない
    なぜならば,x や y は main 関数の中でのみ有効な名前だからである.

    関数 sort の中では,ポインタを使った (*px)x の別名となっている.
    つまり,&x と書きたいところは &(*px) と書けばよいことになるが,
    もともと &xx の番地 を指定したかったわけなので, &(*px) と書く必要はなく, px だけでここは十分である.
    つまり,swap(&x, &y); は関数 sort の中では swap(px, py); でよいことになる.

    以上の説明を参考にして sort を完成させなさい.

    入出力例(キーボードからの入力は赤色
     実数を三つ入力してください:3.14 2.71 8.8
    
     関数 sort 実行前
     (x, y, z) = (3.140000, 2.710000, 8.800000)
    
     関数 sort 実行後
     (x, y, z) = (8.800000, 3.140000, 2.710000)

余裕のある人は

  • 演習 3【発展課題】

    演習 3 のプログラムを別名(ex03e.c)にコピーし,これを改良して, 1 の位だけでなく,10 の位や 100 の位といった桁での四捨五入も可能なようにしなさい.
    その際には関数 round1 を round_n とし,次のように宣言しなさい (n には 10 や 100 を指定する):
     void round_n ( int *px, int n ){ 
    • 関数 round_n での四捨五入は,演習 3 で作った round1 を一般化したものになっている.
      (ヒント)
      まずはイメージをつかんでもらうために, 元の値 x を 12345 とし, n を 100 とした場合を考える.
      この場合,100 の位を四捨五入したいので, 100 の位とそれ以下の部分だけを取り出す.これを d とおくことにする.
      具体的には d が 345 となるが,これは d = 12345 % 1000; で求まることになる.
      つまり,これを変数で書くと d = x % (n*10);
      そこで,
      • d < (n*10)/2 ならば n の位は切り捨てとなり, x - d が求める値になる.
      • そうでなければ,切り上げとなり,x + n*10 - d が求める値になる.

    (実行例)
    整数を一つ入力してください:12345
    どの位を四捨五入しますか:100
    100 の位を四捨五入した値 = 12000
    整数を一つ入力してください:86302
    どの位を四捨五入しますか:1000
    1000 の位を四捨五入した値 = 90000
  • 演習 5【発展課題】

    まず,このプログラム(ex05.c)をダウンロードしなさい.
    そして,この中の関数 update を完成させなさい. ただし,main の内容は一切変更しないこと.
    関数 update は,√n の近似値 を求めることを目的としている.

    この関数の引数は,n (int 型)と px(double 型変数へのポインタ)となっていて,
    *px が √n へ近づくように *px の内容を更新する

    具体的な手順は以下の通りである:
    1. 求める近似値を x とする.x の初期値は n としておく.(main 関数で記述済み)
    2. update(n, &x); を所定の回数繰り返して x の値を更新していく.(main 関数で記述済み)
    3. 関数 update の中では次の手順で x の値(*px)を更新する.
      • まず誤差を求める: diff = (*px)*(*px) - n;
      • diff > 0 ならば x の値は実際の√n よりも大きいので x の値を減らすようにする.
        そうでなければ x の値は実際の√n と等しいか小さいので x の値を増やすようにする.
        つまり,x の値を -diff に比例する量だけ増やせばよい.(もしも誤差が 0 ならば増減はないことになる)
        そこで今回は (*px) = (*px) - diff * 0.1; として徐々に調整するようにする.
        なぜ 0.1 をかけるのかというと,上で求めた誤差はあくまでも 2 乗して得られた誤差なので, そのまま使うと収束しない可能性があるからである.

    (実行例)Mac や Linux 環境の人は,計算結果が少し変わるかもしれません.
    整数を一つ入力してください:2
    
    √2 の近似値を求めていきますので
    繰り返しの回数を決めてください:20
    1 回目 = 1.8000000000
    2 回目 = 1.6760000000
    3 回目 = 1.5951024000
    4 回目 = 1.5406672334
    5 回目 = 1.5033016810
    6 回目 = 1.4773100866
    7 回目 = 1.4590655774
    8 回目 = 1.4461783415
    9 回目 = 1.4370351619
    10 回目 = 1.4305281563
    11 回目 = 1.4258870757
    12 回目 = 1.4225716804
    13 回目 = 1.4202006618
    14 回目 = 1.4185036698
    15 回目 = 1.4172884037
    16 回目 = 1.4164177618
    17 回目 = 1.4157938342
    18 回目 = 1.4153466161
    19 回目 = 1.4150260117
    20 回目 = 1.4147961503