joho1-2017の日記

joho1-2017の日記

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

ポインタ1

今回の授業では,ポインタを扱いました.

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

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

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

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

では,課題の解説を行います.

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

  • main文は変更してはならない.
  • 関数 decode を作成する.
  • 関数 decode は受け取った文字列の偶数番目の文字のみ文字コードをインクリメントして表示する.

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

int main(void)
{

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

    return 0;

}
//実行結果 
Dont_think_Feel!

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

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

の2点です.

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

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

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

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

(*p)++;
printf("%c", *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;
記号: ++ & '-!' "!" .;

この問題のポイントは,

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

という2点です.

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

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


 

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回 中間テスト」の記事で同じような問題の考え方のヒントを書いています.それを参考にしましょう.
②は,本記事の2.の②で考えたのと同じ方法でアクセスできるので,ここでは省略します.

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つの引数が必要で す.そして,各関数の目的は配列への文字の格納です.つまり,返り値は必要ありません.ということは,関数の型はもう何を選択すればいいかわかりますね.

 

解説は以上です.課題提出をがんばってください.

Remove all ads