joho1-2016の日記

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

第11回 ポインタ2

5組
今回の課題を解くにあたって,おそらく学生の皆さんが理解しにくい,あるいは混乱しやすいと思うことをまずは解説したいと思います.
今週の講義のメインであるポインタは,アドレス(住所)を表す変数です.今まで皆さんが扱ってきたintやfloat,charといった変数はコンパイル時に自動的にPCメモリのどこか(その場所のことをアドレスと言う)に値が格納されます.
ポインタはこのアドレスを値として持つ変数ということです.
つまり,

char  c='Z';    //文字型変数
char* p;        //変数pをポインタ変数として宣言
p = &c;         //変数cのアドレスをpに代入
p*;             //pのアドレス内に格納された値

というように,ポインタ変数pそのものが示すのはアドレス,そしてpのアドレスに格納された中身を示すのは*pであるということに注意が必要です.以上の知識を持って課題に取り組んでみましょう!

1.以下のルールに沿ってある文字列(「TCznemhs!^lsngehJmAjr^UEbdadfkp 」)を解読しその答えを表示するプログラムを作成しなさい.

  • main文は変更してはならない.
  • 関数 decode を作成する.
  • 関数 decode は受け取った文字列の偶数番目の文字のみ文字コードをインクリメントして表示する.
void decode(const char *s)
{
    /* ここを作成 */
}

int main(void)
{

    decode("TCznemhs!^lsngehJmAjr^UEbdadfkp ");

    return 0;

}
//実行結果
Dont_think_Feel!

この課題のポイントは,
 ①文字列の偶数番目の認識
 ②文字のインクリメント
です.文字列をdecode関数でみていくので,繰り返し処理をすると思います.その時には1文字ずつ見ていくので,現在のループ処理で何文字目を見ているのかを知るためには,カウンタ変数を使えば分かりますよね.
何ループ目の処理か=何文字目の解読か,と考えると,カウンタ変数の値自体が文字数に対応しますね!
②は,前回までの課題をしっかりやってきた皆さんなら文字自体のインクリメントは+1すればいいだけなのはわかりますよね.ここで注意するのは,ポインタ変数で示されたアドレス内の値に対してのインクリメント処理であることです.以下の2つに対して,実際にコンパイルして違いを確認してみましょう.

*(p++);
(*p)++;

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

void search_symbols(char* p)
{
...
}

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

int main(void)
{
    char str[100];

    input(str);
    search_symbols(str);

    return 0;
}
//実行例
文字列を入力: C++ & 'Oh-o!' "Meiji!" No.1;
記号: ++ & '-!' "!" .;

この課題のポイントは,
 ①英数字以外の探索
 ②次の文字のアドレスへのアクセス方法
です.問題の最終文の意味は,文字の探索の際に配列を使って探索するのではなく,ポインタを使って探索するという意味です.そのため,search_symbols( )の中で配列を扱うのはNGです.
①は,アスキーコード表から対応するもの以外の場合を表示するようにすれば,簡単に表示はできます.
②に関してですが,皆さんは,配列は隣り合った要素はアドレスも隣り合っていることを覚えていますか?(全く知らないぞ!という人,先生は確かに授業中に教えていましたよ!もう一度配列の講義ページを復習しましょう!)
つまり,次の文字のアドレスへアクセスしたければ,現在のポインタに+1すればアクセスできます.

3.整数値を3つキーボードより入力して配列に収め,それぞれを3乗して返す関数 cubic を作りなさい.
ただし関数 cubic の戻り値の型は void 型とし,N = 3 以外でも正しく動作すること.
すなわち,下記でNの宣言時に値を3以外に変更してもそのまま動作するようにすること.

void cubic(....)
{
    //ここを作成
}
                
int main(void)
{
    const int N = 3;
    int data[N];
                
    printf("整数値を %d 個入力して下さい\n", N);

    for(int i=0; i<N; i++){
        printf("data[%d]:", i);
        scanf("%d", &data[i]);
    }

    cubic(data, N);    /* 3乗の計算 */

    printf("計算結果\n");

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

    return 0;
}
実行例:

整数値を 3 個入力して下さい
data[0]:2
data[1]:3
data[2]:4
計算結果
data[0]:8
data[1]:27
data[2]:64

この課題のポイントは,
 ①関数への引数
です.この課題は,実はポインタを使わずにプログラムできます.
ここでは,配列をそのまま引数として渡し,cubic関数内では配列を計算すればいいわけです.

4.入力された文字列を全て小文字に変換するmy_tolower関数を作成せよ.
ライブラリ関数(string.h内にある)tolower関数を使用しないこと.

void my_tolower(char* p)
{
    /* ここを作成 */
}

int main(void)
{
    char str[100];

    printf("文字列を入力:");
    scanf("%[^\n]", str);

    printf("変換前:%s\n", str);

    my_tolower(str);

    printf("変換後:%s\n", str);

    return 0;
}
実行例
文字列を入力:ABCdef12345!
変換前:ABCdef12345!
変換後:abcdef12345!

この課題のポイントは,
 ①大文字の認識
 ②次の文字のアドレスへのアクセス方法
です.①は「第9回 中間テスト」の記事で同じような問題の考え方のヒントを書いています.それを参考にしましょう.
②は,本記事の4.の②で考えたのと同じ方法でアクセスできるので,ここでは省略します.

5.2つの文字列をキーボードからそれぞれ str1 と str2 に入力し,

  • str1 からは大文字のみを抜き出し(find_capital関数を作成)
  • str2 からは小文字のみを抜き出し(find_small関数を作成)
  • それぞれを結合したものを文字列変数 str に順に結合し,

最後に str を表示するプログラムを作成せよ.
但し,配列へのアクセスは必ずポインタを用いること.
また,string.h に用意されたライブラリ関数を使用しないこと.

... find_capital(...)
{
    ...
}

... find_small(...)
{
...
}

void input(char* str)
{
    printf("文字列入力:");
    scanf("%[^\n]%*c",str);
}
                    
int main(void)
{
    const int N = 100;
    char str1[N], str2[N], str[2*N]; // 空の文字列を用意.

    input(str1);
    input(str2);
                    
    // ... ここで自作の関数を呼に出す.
                    
    printf("===> %s\n", str);
}
//実行例
str1入力:kaMjdEIsjfuaJusfhIjauxx
str2入力:KmeJACBAiVOADAKjACYBiAQ
===> MEIJImeiji

この課題のポイントは,
 ①大文字,小文字の認識
 ②各関数の型および引数
です.①おなじみアスキーコード表の大文字,小文字に対応する値の時にのみ処理をおこなえば良いだけです.
②は,まず①の処理を行うために,input( )で代入した文字列を格納した配列が引数で必要ですね.そして,①で認識した文字を格納する配列も必要です.つまり,各関数には最低2つの引数が必要です.そして,各関数の目的は配列への文字の格納です.つまり,返り値は必要ありません.ということは,関数の型はもう何を選択すればいいかわかりますね.