joho1-2016の日記

情報処理実習1の解説ブログです.皆さんが課題を解く時の助けになれば幸いです.

第10回 ポインタ1

更新が遅くなってしまって大変申し訳ありません.

今回の授業では,ポインタを扱いました.
更新が遅くなってしまったので,取り急ぎポインタのイメージだけ話して,課題の解説にはいりたいと思います.

ポインタとは,簡単にまとめると「変数等のメモリアドレスのこと」を言います.
イメージは,ホテルや旅館等の部屋番号がポインタ(アドレス)でその部屋の中身(お客さんなど)が変数の数字だと思ってください.

例えば,今の例を下のようなソースコードに当てはめて考えると,

int a = 0;    //int型の変数aを定義
int *p = &a;    //ポインタ変数を定義

と宣言した場合は,ある旅館で「0」さんが「int」というグレードの「&a(仲居さん達には「*p」の部屋と呼ばれている)」号室の部屋を「a」という名前で予約した.というイメージです.かなり乱暴ですがこのようなイメージを持っているとポインタについて理解しやすいと思います.
その他,授業で取り扱った内容は細かいテクニック的な部分が多いので,おいおい覚えて行くようにしてください.

では,課題の解説を行います.
1. 以下のルールに沿ってある文字列(「TCznemhs!^lsngehJmAjr^UEbdadfkp 」)を解読しその答えを表示するプログラムを作成しなさい.
main文は変更してはならない.
void型の関数decode用いること.
関数decodeは受け取った文字列の偶数番目の文字のみ文字コードをインクリメントして表示する.

void decode(const char *s){ 
    //ここを作成 
} 

int main(void){ 
    decode("TCznemhs!^lsngehJmAjr^UEbdadfkp "); 
}
//実行結果 
Dont_think_Feel!

この問題を解くポイントは,

  1. 偶数番目をどのように判定するか?
  2. 文字コードのインクリメントはどのようにやるか?

の2点です.
まず,偶数番目の判定は「2で割ったあまりに注目して判定すると良いでしょう」.
文字コードのインクリメントに関しては,例えば

char str[] = "meiji";
char *p = str;

のようにある文字列strがポインタ変数pを用いて表現できるとき,

*p++;
printf("%c", *p);

(*p)++;
printf("%c", *p);

の違いについて理解していれば解けるかと思います.


2. 入力した文字列の中から英数字以外の文字(記号)のみを表示するプログラムを作成せよ. 但し,配列の要素を示す括弧( [ ] ) は定義のとき以外で使用しないこと.

void input(char* st){ 
    printf("文字列を入力: "); 
    scanf("%[^\n]",st); 
} 

int main(void)
{
    char str[100];
    input(str);
    search_symbols(str); 
    return 0;
}
//実行例 
文字列を入力: C++ & 'Oh-o!' "Meiji!" No.1; 
記号: ++ & '-!' "!" .;

この問題のポイントは,

  1. 文字列を配列として扱わないこと
  2. 文字とそれ以外の記号をどのように区別するか

という2点です.
まず,文字列を配列として扱わないことに関してですが,これは「ポインタ変数」を利用することで解決できます.
次に,文字列とそれ以外の記号の区別の仕方ですが,これはアスキーコードを利用しましょう.

アスキーコードでは,大文字が16進数において「0x41から0x5a」に該当し,小文字が「0x61から0x7a」に該当します.この範囲外の文字を表示すれば問題は解けるはずです.


3. 整数値を3つキーボードより入力して配列に収め, それぞれを3乗して返す関数 cubic を作りなさい. ただし関数 cubic は void型とし,任意の大きさの配列を引数にとれるようにすること. main関数は変更してはならない.

void cubic(....) 
{
   //ここを作成 
} 

int main(void) 
{
    const int N = 3;
    int data[N]={0,0,0}; 

    printf("整数値を%dつ入力して下さい\n", N); 
    for(int i=0; i<N; i++){ 
        printf("data[%d]:", i); 
        scanf("%d", &data[i]); 
    } 
    
    cubic(data,N); 
    printf("計算結果\n"); 
    for(int i=0; i<N; i++){ 
        printf("data[%d]:%d\n",i, data[i]); 
    }
} 
//実行例: 
整数値を3つ入力して下さい 
data[0]:2 
data[1]:3 
data[2]:4 
計算結果 
data[0]:8 
data[1]:27 
data[2]:64

この問題のポイントは,

  1. 関数によって値を返さずにどのように元の変数の値を変更させるか?

という点だと思います.
これは,配列をポインタ変数として扱うことで元の配列の中身を直接いじることが出来るようになります.

ポインタ変数を利用して三乗を計算する方法は,単純ですが,

int list[3] = {1, 2, 3};
int *p = list;
for(int i=0;i<3;i++){
    *p = (*p) * (*p) * (*p);
    p++;
}

のようにして計算することが出来ます.この処理を上手く関数へと書き換えることで,void型の関数でも元の配列を直接いじることが出来るようになります.

更新が遅くなってしまいましたが,課題提出をがんばってください.