東京工芸大学 工学部  電子機械学科 2年 前期 

基礎プログラミング 第7回
 2017.5.23
 
藤木 文彦

http://fujiki.tv/t-kougei/kisoprog/
fujiki.kougei@google.com  

 0.ポインタの復習


 ポインタの使い方の中で、初期値を設定することのできるものとできないものについて、また、設定できても、その後変更することができるかどうかについて、まとめてみました。
(この項目は、提出課題はありません。)

下記を考えるポイント

  *a というのは、 「アドレスの置き場所を用意した」だけで、実際のデータをどこに置くか決まってないので、そのまま
 *a=123 等とすると、データをどこに置いて良いのか分からず、エラーとなる。

アドレスを直接自分で決めることは出来ないので、他の変数として指定したもののアドレスを
 a=&b
のようにコピーする。



 データ 「123」を、実体のある置き場所に b=123 のように入れてから、そのアドレスを a に受け渡す a = &b
 これで、a が b のアドレスを表し、 *a が、 b の中身を表す。

※文字列の初期化だけは特別なことが行われている。
 プログラム上に置かれた「文字列データ」の置き場所が、 s に入る。

 参考:ポインタとして使える書き方とだめな書き方  
char *s = "文字列の初期設定";
// int *a=999; // これはエラーになる。
int *a;
int b = 123;

printf("%s\n", s);
// printf("%d\n", *a); // これは、コンパイル時に初期化されていない変数エラーとなる
// *a = 123; // これもエラーになってしまう。
// *a = b; // これもエラー
a = &b; // これは通る

printf("%d\n", *a);

//*s = "新しい文字列の入力がこれです。";


 1.アドレスを取り出す&、ポインタの示すアドレスの内容を取り出す*

  (例題1) 

Project0701


整数変数 a に値を代入し、それをポインタ型変数 p に代入したあと、p の内容を変更します。

&a は、aという変数のあるメモリ上のアドレス。
*p は、pというポインタ(アドレス)の示す場所にあるデータの内容を表すことを思い出してください。


int _tmain(int argc, _TCHAR* argv[])
{
    int a=777;
    int *p;
    p = &a;
    printf("p=%x, *p=%d\n",p, *p);

    *p = 333333;

    printf("p=%x, *p=%d\n",p, *p);
    printf("&a=%x, a=%d\n",&a, a);

    a = 101010;

    printf("p=%x, *p=%d\n",p, *p);

    getchar();
    return 0;
}

実行結果
( "13ff60" の部分は、他の数値(アドレス)となっていると思われる。)

p=13ff60, *p=777
p=13ff60, *p=333333
&a=13ff60, a=333333
p=13ff60, *p=101010

  【演習問題1】 


 上記プログラムの実行結果で、
最後の行が、  p=13ff60, *p=123456

 となるようにプログラムを書き換えなさい。
( アドレスを示す "13ff60" の部分は、他の数値(アドレス)となっていると思われる。)

 2.配列への初期値の代入

  (例題2) 

Project0702

大きさ20の配列 a[ ]を宣言し、それに、 100,200,300。。。2000 を初期値として代入します。
整数型へのポインタ p を宣言し、 p に配列 a の先頭の値を入れることで、ポインタを用いて、配列の内容を操作できるように準備します。
そのあと、この配列の内容を、ポインタ p を用いたprintf文で出力しなさい。


#define MAX 20

int _tmain(int argc, _TCHAR* argv[])
{
    int a[MAX];
    int *p;
    int n;

    for( n=0; n<MAX; n++){
        a[n]=n*100;
        printf("%d: addr=%x a[%d]=%d\n", n, &a[n],n, a[n]);
    }
//    p=&a[0];
//    for( n=0; n<MAX; n++){
//        printf("%d: addr=%x *p=%d\n", n, p, *p);
//        (ここに p を操作する命令が入る )
//    }
    getchar();
    return 0;
}



実行結果

  addr=13ff10 a[0]=0
: addr=13ff14 a[1]=100
: addr=13ff18 a[2]=200
: addr=13ff1c a[3]=300
: addr=13ff20 a[4]=400
: addr=13ff24 a[5]=500
: addr=13ff28 a[6]=600
: addr=13ff2c a[7]=700
: addr=13ff30 a[8]=800
: addr=13ff34 a[9]=900
0: addr=13ff38 a[10]=1000
1: addr=13ff3c a[11]=1100
2: addr=13ff40 a[12]=1200
3: addr=13ff44 a[13]=1300
4: addr=13ff48 a[14]=1400
5: addr=13ff4c a[15]=1500
6: addr=13ff50 a[16]=1600
7: addr=13ff54 a[17]=1700
8: addr=13ff58 a[18]=1800
9: addr=13ff5c a[19]=1900
: addr=13ff10 *p=0
: addr=13ff14 *p=100
: addr=13ff18 *p=200
: addr=13ff1c *p=300
: addr=13ff20 *p=400
: addr=13ff24 *p=500
: addr=13ff28 *p=600
: addr=13ff2c *p=700
: addr=13ff30 *p=800
: addr=13ff34 *p=900
0: addr=13ff38 *p=1000
1: addr=13ff3c *p=1100
2: addr=13ff40 *p=1200
3: addr=13ff44 *p=1300
4: addr=13ff48 *p=1400
5: addr=13ff4c *p=1500
6: addr=13ff50 *p=1600
7: addr=13ff54 *p=1700
8: addr=13ff58 *p=1800
9: addr=13ff5c *p=1900

  【演習問題2】 


 上記プログラムのコメント部分 ( // ) のコメントを外し、ポインタを操作する命令を付け加えて完成させなさい。



 3.配列への初期値代入(2) ポインタを使った初期値の代入

  (例題3) 

Project0703


 大きさ10の配列 a と、ポインタ p を用意し、 a に、00、11、22、 、99 を代入します。
 内容の操作に当たっては、配列のポインタを p に代入した上で、pを用いて操作しなさい。
 なお、順番は0番から始まるものとします。


#define MAX 10

int _tmain(int argc, _TCHAR* argv[])
{
    int a[MAX];
    int *p;
    int i;

    for( i=0; i<MAX; i++){
        a[i] = i*11;
        printf("%d: addr=%x a[%d]=%d\n", i, &a[i], i, a[i]);
    }
    p = a;

//    for(i=0; i<MAX; ●){
//        *(p+i)*=10;
//    }

    for( i=0; i<MAX; i++){
        printf("%d: addr=%x *p=%d\n",i, (p + i), *(p + i) );
    }
    getchar();
    return 0;
}

実行結果

0: addr=13ff38 a[0]=0
1: addr=13ff3c a[1]=11
2: addr=13ff40 a[2]=22
3: addr=13ff44 a[3]=33
4: addr=13ff48 a[4]=44
5: addr=13ff4c a[5]=55
6: addr=13ff50 a[6]=66
7: addr=13ff54 a[7]=77
8: addr=13ff58 a[8]=88
9: addr=13ff5c a[9]=99
0: addr=13ff38 *p=0
1: addr=13ff3c *p=11
2: addr=13ff40 *p=22
3: addr=13ff44 *p=33
4: addr=13ff48 *p=44
5: addr=13ff4c *p=55
6: addr=13ff50 *p=66
7: addr=13ff54 *p=77
8: addr=13ff58 *p=88
9: addr=13ff5c *p=99


  【演習問題3】 

 上記プログラムのコメント部分をはずして、偶数番目の出力だけ 10倍となるようにしなさい。(0番目、2番目、、、 以下の出力参照)
 出力は次のようになります。

0: addr=13ff38 a[0]=0
1: addr=13ff3c a[1]=11
2: addr=13ff40 a[2]=22
3: addr=13ff44 a[3]=33
4: addr=13ff48 a[4]=44
5: addr=13ff4c a[5]=55
6: addr=13ff50 a[6]=66
7: addr=13ff54 a[7]=77
8: addr=13ff58 a[8]=88
9: addr=13ff5c a[9]=99
0: addr=13ff38 *p=0
1: addr=13ff3c *p=11
2: addr=13ff40 *p=220
3: addr=13ff44 *p=33
4: addr=13ff48 *p=440
5: addr=13ff4c *p=55
6: addr=13ff50 *p=660
7: addr=13ff54 *p=77
8: addr=13ff58 *p=880
9: addr=13ff5c *p=99


 4.cin cout を用いた、文字列変数への値の代入


  数字、文字列などの入力(キーボードから)
  cin >> s
    ( s は、 char s[100] などとして、文字列を使う配列として宣言されていなければならない。)
 
  数字、文字列などの出力(画面へ)
   cout << a,b,c
 縦横を揃える書式を使うこともできるが、詳細はあとで。 


また、関数、とういものの使い方も含まれています。今回は、その使い方の説明は省略しとりあえずプログラムを動かしてみます。

先頭部分に少し変化があるので注意して下さい


 文字列は、あらかじめ宣言された配列の大きさを超えて入力することは出来ません。
 配列の大きさより大きい(長い)文字列を入力するとエラーになります。
 ただし、大きさは、入力するまで分かりませんから、コンパイルではエラーにならず、実行時にエラーになります。

  (例題4) 

Project0704


 次のファイルを入力し、以下の2つの場合について実行してみなさい。
 1)入力文字列が短いとき。
 2)入力文字列が20文字を越えるとき。


#include "stdafx.h"
#include "iostream"     //cin, cout を使うために、この3行を追加する必要がある。
#include "string"
using namespace std;


// これも追加
#define MAX 20


int _tmain(int argc, _TCHAR* argv[])
{
char s[MAX];


printf("\n Input string>>");
cin >> s;
printf("[ %s ]\n",s);

getchar();  //2回
getchar();
return 0;
}


 20文字以上入力しても、結果は表示されるようですが、その後、次のようなエラーが出されるはずです。





 そこで、「中断」を選びますが、それでもちゃんとは終われないので、
       
「デバッグ」−>「デバッグの停止」を選択して、プログラム実行を中止します。

  【演習問題4】 



 上記プログラムの printf 部分を、 cout を使った出力に変えて実行してみなさい。cout に変えたプログラムをメールに添付して提出しなさい。

【参考】
    cout << "[ " << s << " ]\n";


 5.cin cout を用いた、配列の入出力


 配列の入出力は、次のように、繰り返し入力する方法を使います。
(ファイルから入力したり、ウインドウを用いたプログラムをするようになったら、もっと使いやすい方法を紹介しますが、とりあえず、1行ずつ入力していきます。)

  (例題5) 

Project0705


#include "stdafx.h"
#include "iostream"
#include "iomanip"
#include "string"
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
    int a[10];
    for( int i=0; i<10; i++){
        cout << "Input Data No." << i <<" : " ;
        cin >> a[i];
    }

    for ( int i=0; i<10; i++){
        cout << a[i] << '\n' ;
    }
    getchar();
    getchar();
    return 0;
}


 【演習問題5】 



 上記プログラムを次のように変更しなさい。
 上記では、配列の大きさを 10 として、各所で、10という数値を入れていますが、最初に、 #define を用いて、定数 MAX を設定し、それを用いたプログラムに変更しなさい。MAX の値は、 20 とします。
 最後に、入力した数値を全部加えて出力しなさい。


 【演習問題6】 

Project0706

 新しくプロジェクトを作りますが、その際、プログラムの主要部分は、上の Project0705 を流用すると楽なので、あらかじめ、プログラム部分をコピーして、別のエディタなどで保存しておくと、プログラム作りが楽になります。


 配列を、MAX 100 まで用意し、前のプロジェクトと同じようにキーボードから入力するようにします。
 ただし、100個入力しないと終わりにならないのでは大変です。
 数値として、 負の数が入力されたら、その時点で入力を終了し、

 そこまでに入力された数値の 個数 と 合計 と 平均(浮動小数点) を出力するプログラムを作成しなさい。


 繰り返しの途中で打ち切るには、次のようにします。break というのは、for while などの、繰り返し制御ループから脱出する命令です。これが実行されると、繰り返しの途中での値を保持したまま、繰り返し部分の次の部分に制御が移ります。


    for( int i=0; i<MAX; i++){
        cout << "Input Data No." << i <<" : " ;
        cin >> a[i];
        if( a[i] < 0 ) break;
    }


 このとき、入力された最後の数は、負の数ですから、これは計算から除外しないとなりません。入力された数値の数と i との関係も考えて計算してみましょう。(1つ入力し、2つめに マイナスの数値を入れたとき、 i の値はどうなっているでしょう?)

 7.cout での出力書式


cout で、出力の幅などを指定できます。指定しないと、連続して出力されてしまって区切り目がわからなくなります。
setw(n) は、幅 n 文字をとって、次の文字、数値を出力する、ということです。
そのままでは、出力は左寄せなので、数値などでは、右寄せにするために、 right を指定します。

setw(n) の指定は、直後の1出力だけなので、続けて出力するときには、毎回指定しないとなりません。
改行は、 "\n" の代わりに、  endl と書くことができます。

  (例題7) 

Project0707

#include "stdafx.h"
#include "iostream"
#include "iomanip"

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
int a = 123, b = 45,c=-89;
cout << a << b << c << "\n";
cout << setw(5) << right << a << b << c;
cout << endl;
cout << setw(5) << right << a;
cout << setw(5) << right << b;
cout << setw(5) << right << c;

getchar();
return 0;
}


 【演習問題7】 

 次のような出力を行うプログラムを、cout を用いて書きなさい。上の演習問題の main 部分を書き換え、 Project0707 として保存、提出します。なお画面の出力も、コピーしてメールに貼り付けるとなお良い。(Word の キャプチャー機能を使うと便利。)




プログラムの一部は次のようにします。

  cout << setw(5) << right << y * 10 + x;
}
cout << endl; // endエル です。
}