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

基礎プログラミング 第6回
 2017.5.16
 
藤木 文彦
http://ffujiki.web.fc2.com/t-kougei/

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

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


 普通に宣言した変数の、アドレスを取り出すときに使うのが、 ”&” です。

int a=12;
printf("%x",&a); // a のアドレスが表示される。

これを、ポインタ型変数に代入することができます。

int a=12;
int *p;
p=&a;
printf("%x",p);

ポインタ型変数の示すアドレスにある内容を取り出すのが、”*” です。

int a=12,b=34;
int *p;
p=&a;
b=*p;
printf("%d",b);

ポインタの示すアドレスの中身を直接書き直すときは次のようにします。

*p=9876;

上記のようにして、ポインタの内容を変えると、a の内容も変わっています。

  (例題1) 

Project0601


int _tmain(int argc, _TCHAR* argv[])
{
        int a=12,b=34;
        int *p;
        p=&a;
        b=*p;
        printf("%d,%d\n",a,b);
        *p=56;
        printf("%d,%d\n",a,b);

        getchar();
        return 0;


}


  【演習問題1】 


 Projext0601 に変更を加えて以下のようなプログラムにして動作を確認しなさい。
 ただし、以下のプログラムは、ただ写しただけでは動作しません

// 【自分で入れる】 変数 p をポインタとして宣言しなさい

のように書かれているところは、自分で考えてプログラムを入れます。
( //以下の分は消しても消さなくても構いません。)


int _tmain(int argc, _TCHAR* argv[])
{
        int a=123,b=456;

// 【自分で入れる】 変数 p をポインタとして宣言しなさい

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

// 【自分で入れる】 p の示すアドレスに、数値 999 を代入しなさい。


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

// 【自分で入れる】 a に数値 55555 を代入しなさい。

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

解答例へのリンク

 2.配列の先頭アドレス  &a[0]


配列要素の一番はじめのアドレスを取り出すには、

p = &a[0];

のようにすれば良い、ということは、今までの例からわかると思います。
ところで、これと同じことが次のように書けます。

p = a;

a は、配列の名前ですが、単独で使うと、その配列の先頭アドレスを示すことができます。つまり、a とは、配列の先頭を示すポインタなのです。

  (例題2) 

Project0602


#define MAX 10

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

        a[0] = 0; a[1] = 111; a[2] = 222;
        p = &a[0];
        printf("%d: addr=%x a[%d]=%d\n", n, &a[n], n, a[n]);
        n = 1;
        printf("%d: addr=%x a[%d]=%d\n", n, &a[n], n, a[n]);
        n = 2;
        p = &a[n];
        printf("%d: addr=%x *p=%d\n", n, p, *p);

        printf("--------\n");
        p = a;
        printf(" addr=%x *p=%d\n", p, *p);
        getchar();
        return 0;

}


  【演習問題2】 

 例題を次のように書き換えて実行しなさい。


#define MAX 10

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]);
        }
        for( n=0; n<MAX; n++){

// ポインタ p が配列の n番目の要素のアドレスを示すようにしなさい。
                printf("%d: addr=%x *p=%d\n", n, p, *p);
        }

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

        getchar();
        return 0;
}


 3.配列の a[n] は、 *(p+n) と同じ


p = a;

とすると、配列 a の先頭アドレスが、 ポインタ p に入ることが分かりました。
このポインタ p の示す配列の、n 番目の要素は、 *(p+n) として取り出すことができます。

  (例題3) 

Project0603


#define MAX 10

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

        a[0] = 0; a[1] = 111; a[2] = 222;
        p = a;
        for (n = 0; n <= 2; n++){
                printf(" addr=%x *p=%d\n", p + n, *(p + n));
        }
        getchar();
        return 0;
}


  【演習問題3】 

 例題の配列要素を、 a[0]=0 から a[9]=999 まで設定し、これらの値を、ポインタ p を用いて出力するように書き直して実行して見なさい。


 4.配列の p++ の働きと、型による p++ の結果の違い


 ポインタの値は、 p++ とすることで、「一つ増やす」ことができますが、これは、単純に数値を1つ増やすということではなく、

 「型の示すアドレス分増加させる。」という意味です。

 次のように使います。

  (例題4) 

Project0604


#define MAX 10

int _tmain(int argc, _TCHAR* argv[])
{
        int a[MAX], aaa;
        float b[MAX], bbb;
        int *p;
        float *q;

        for (int i = 0; i < MAX; i++){
        a[i] = i * 1111;
        b[i] = i*0.0011;
        }
        p = a;
        for (int n = 0; n < MAX; n++){
                aaa = *p;
                printf(" addr=%x *p=%d\n", p ,aaa);
// 出力後に、次のアドレスを示すように、ポインタを1つインクリメントする。
                p++;
        }

// 演習で、 float b[ ], bbb , *q を用いた部分を、上の5行と同じように書いて入れる。

        getchar();
        return 0;
}


  【演習問題4】 

 例題の赤字のコメント部分、 float b[ ] など 順に出力する部分を作成して実行して見なさい。

 表示されるアドレスを良く見た上で、メールの先頭に、
 int 型へのポインタのアドレスは、何バイトずつ増加するか。
 float 型へのポインタのアドレスは、何バイトずつ増加するか。
の2点を、メールの最初に記述しなさい。


 5.ポインタを増加させる *p++ と  *++p


 p++ は、 ポインタを一つ増やす(次の位置にずらす)ということでしたが、値を取り出してから、内容を増やすときには、次のように使います。

*p++

 あらかじめアドレスを増やしてからその内容を表示するには、次のようにします。

*++p

  (例題5) 

Project0605


#define MAX 10

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

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

// 演習で、*p を *++p に変えて実行してみる。
        getchar();
        return 0;
}


  【演習問題5】 

 例題の赤字のコメント部分を作成して実行して見なさい。

 表示されるアドレスを良く見た上で、メールの先頭に、
 aaa = *p++;
で出力した場合のアドレスと内容の関係が、
 aaa = *p;
で出力した場合と違うことを確認し、理由を書きなさい。
アドレスとその内容の対応が正しいのはどちらか。

また、 *++p での出力では、最後におかしな値が表示されると思う。なぜそうなるのか理由を考察して書きなさい。

 6.配列への初期値の設定

 配列を宣言すると同時に、その値を代入するには、次のようにします。
 2次元配列の場合は、例題では出力まで書いてありませんが、演習で出力部分を作ってください。

  (例題6) 

Project0606


#define MAX 3   // 最大値を3にしたから注意

int _tmain(int argc, _TCHAR* argv[])
{
      int a[MAX] = { 100, 101, 102 };
      int b[MAX][MAX] = { { 200, 201, 202 },
                               { 210, 211, 212 },
                              { 220, 221, 222 } };


      for (int i = 0; i < MAX; i++){
            printf("a[%d] = %d ", i, a[i]);
      }
      printf("\n");

      for (int x = 0; x < MAX; x++){
// 演習では、ここに、y 方向の変化を書く
// printf(" b[%d][%d] = %d  ", ● );

      }
        getchar();
        return 0;
}


  【演習問題6】 

 例題の赤字のコメント部分を作成して実行して見なさい。
 次のような出力になるようにしなさい。






 7.文字列の扱い


  (例題7) 

Project0607


#define MAX 100

int _tmain(int argc, _TCHAR* argv[])
{
    char s[]="Hello";
    int n=0;
    printf("%s\n",s);
    while( s[n] !='\0' )
    {
        printf(" s[%d]=%c\n", n, s[n]);
        n++;
    }

    printf("nagasa = %d\n",n);

// 以下の部分は、演習問題で入力して実行して見なさい。
// char m[MAX];
//    for( int i=0; i<n; n++)
//    {
//        m[●]=s[i];
//    }
//    m[n]='\0';
//    printf("%s\n",m);
//

        getchar();
        return 0;
}



実行すると次のようになります。


  【演習問題7】 

文字列を逆順にして表示するために、上のプログラムのコメント部分を変更して、実行し、次のようになるようにしなさい。



 プログラム中の  ● のところには、数式が入りますが、ここを間違えると、おかしなことになり、たいていの場合、プログラムがとまらなくなります。

● の部分に入る数式は、次のような形のもののどれかです。試して見ましょう。ただし、間違ったものを入れると、表示が正しく出ないだけでなく、プログラムの実行がとまらなくなるなどのおかしなことが起きます。

 n
 n-i
 n-1
 n-i-1
 i-n




 プログラム実行中に、どうしてもとまらなくなってしまったときには、以下の場所から、「デバッグの停止」を選んで終了してください。




 8.文字列の扱い(ポインタを用いた方法)(要注意)


  (例題8) 

Project0608


#define MAX 100

int _tmain(int argc, _TCHAR* argv[])
{
    char *s = "文字列の初期設定";

    printf("%s\n", s);
   

// 以下の部分は、演習問題で入力して実行して見なさい。エラーになるはずです。
// *s="新しい文字列"
//   
//    printf("%s\n",s);
//

        getchar();
        return 0;
}


  【演習問題8】 

 上記のコメントをはずして、実行して見なさい。
 文字列が置き換えられずにエラーになるはずです。

 理由を考察し、メールに書きなさい。(この理由は難しいので、分からなければかまわないが、できるだけいろいろ、ネットなどで調べて、どこを調べたかなどを記述しなさい。)







 以下は昨年の講義でのコンピュータの構造(アーキテクチュア)とメモリ、ポインタの関係を記述したものを写したものです。























 2限のはじめに、理解度を確認する小テストを行う予定です

(進行状況により変更するかもしれません。)

【修正版】
番号 氏名
問題1 
int x=1234;
int *p;
p=&x;
*p=5678;
としたとき、 printf("%d",x); を実行したらどのような表示が出るか。
問題2
 問題1に続けて、
 x=3333;
としたとき, 3333 と表示されるのは、次のうちどれか(中にはエラーになるものもある)
1) printf("%d",*x);
2) printf("%d",&x);
3) printf("%d",p);
4) printf("%d",*p);

問題3
 問題1,2 の実行後、次のコマンドを実行したとき、 変数 x のアドレスが表示されるものはどれか。
(複数解答あり)

1) printf("%x",x);
2) printf("%x",*x);
3) printf("%x",&x);
4) printf("%x",p);
5) printf("%x",*p);
6) printf("%x",&p);



問題4
 問題1,2、3 の実行後、次のコマンドを実行したときに、それぞれ、実行できるか、エラーになるかを書け。
     ( ○ × 表示でよい)

1) x=1111;
2) *x=2222;
3) &x=3333;
4) p=4444;
5) *p=5555;
6) &p=6666;
7) p=&x;
8) *p=x;
9) &p=x;