配列(2)

目次

多次元配列

多次元配列の定義

一次元,二次元の配列だけではなく,三次元以上の配列を作ることもできる.

int a3[10][3][4];

これは,3×4 の 二次元配列が 10 個,計120個の整数が利用できる.

さらに配列の次元を増やして,

int a3[10][3][4][5][10][3];

のような記述も可能であるが,工学上の問題の性質を考えれば,せいぜい2∼3次元程度が実用的である.
いずれの多次元配列も,コンピュータのメモリ上では1次元配列として格納されている.

関数の引数に多次元配列を渡す

多次元配列を引数として,関数に渡す場合は注意が必要である.
関数の仮引数に多次元配列を渡す場合は,各次元の要素数が確定している必要があるため,引数として要素数を渡す方法ではエラーとなる.
(正確には,最初の次元の要素数[]のみ,省略が許される.)

したがって,2次元以上の配列を関数に渡す場合は,以下のように配列の要素数 M, N をグローバル変数として定義しておき,仮引数で明示しておくと良い.
あるいは,別の方法として,要素数 M * N の1次元配列を確保して,添え字の変換を自分で管理するやりかたもある.

#include <stdio.h>

// 配列(行列)サイズをグローバル変数としておく.
const int M = 4;
const int N = 3;

//
// 2D配列を表示する関数
// void disp_2d_matrix(int mat[][]) はコンパイルエラーとなる.
//
void disp_2d_matrix(int mat[M][N])
{
    int i, j;

    for(i=0; i<M; i++) {
        for(j=0; j<N; j++) {
            printf("%d ", mat[i][j]);
        }
        printf("\n");           // 1行分表示したら改行
    }
}

int main(void)
{
    int mat[M][N] = {{ 1,  2,  3},
                     { 4,  5,  6},
                     { 7,  8,  9},
                     {10, 11, 12} };

    disp_2d_matrix(mat);
    return 0;
}

文字とは

注意:以下の説明では,「文字」 と 「文字列」という用語を,厳密に区別して使用している.

コンピュータでの文字の処理においては,アルファベット文字や漢字,数字,ひらがな,カタカナ,記号などに数値を割り当て,文字と数値とを対応づけて処理が行われる.
この「文字」⇔「数値」の対応は「文字コード」と呼ばれ,言語や国によって様々な種類が規定されている.

アスキーコード表(文字コード)

一番単純な文字コード表は「ASCII(アスキー)コード」と呼ばれるものである.
アスキーコードは英数字や記号に1バイト分の数値を割り当てたの最も基本的なものであり,プログラムで扱いやすい.

以下の表は,文字コード表の一例である.
8ビットの16進数(頭に0xをつけて表す)のうち,上位4ビットを横軸に,下位4ビットを縦軸で表している.
例えば,大文字の 'A' の文字コードは 0x40 + 0x01 = 0x41,小文字の 'z'の文字コードは 0x70 + 0x0A = 0x7A である.

詳細なASCIIコード表はこちら(外部サイト)

左側から2列の最初の32文字(0x00 - 0x1F)は,「改行」や「タブ」などの「画面制御用コード」と呼ばれ,表示可能ではないため空白になっている.
また,0x20は何も印字されていないように見えるが,「スペース文字」である.

ソースコード中での文字と数値の表現

C言語では,文字(1文字)を格納するための変数が用意されており,char 型 (=character,チャー型,キャラ型,文字型など)と呼ばれる.

文字はコンピュータ内部ではすべて文字コードによる「数値」として管理されているため,ソースコード中において,以下の三つの表現は全く同じ意味となる.

  1. シングルクォーテーション ’ で括られた1文字 例:'A'
  2. 16進数表記の整数 0x41
  3. 10進数表記の整数 65

char 型は「文字を扱うための変数」と考えれば良いが,その実体は8ビット符号なし整数であり,0∼255(16進数で0x00-0xff)の整数である.

練習問題

以下のコードを実行し,ソースコード中での文字の扱いを確認しよう.

  1. ASCIIコード表を参照して,下記の変数 c,n の値を変更し,画面に表示される文字が変化することを確認しよう.
    (また,変数 c に整数を加減算をしてみよう.)
  2. 下のプログラムを変更し,改行コード '\n'や,タブ'\t' の文字コードが,10進数および16進数でいくつになるか,調べてみよう.
#include <stdio.h>

int main(void)
{
    int n = 0x5A;    // 整数型
    char c = 'k';    // 文字型

    // %d : 整数(10進数)として表示
    printf("n = %d , c = %d \n", n, c);

    // %X : 整数(16進数,大文字)として表示
    printf("n = 0x%X , c = 0x%X  \n", n, c);

    // %c : 文字として表示
    printf("n = %c , c = %c \n", n, c);

    return 0;
}

文字列とは

ここでは,「文字」ではなく「文字列」の説明を行う.

char型は,文字1つ分を格納する変数であるが,では「abc」 や 「Meiji」 など,2文字以上からなる単語や文章などの「文字列」をどう扱えばよいだろうか. 実はC言語には文字列を直接扱う「組込み型(プリミティブ型という)」が存在しない.
ではどうするかと言うと,char配列を用いることとなっている.
即ち,「文字列」=「文字型 char」の「配列」ということである.

ここで問題となるのは,C言語の配列サイズはコンパイル時に決めておかなければならないが,実際の文章や単語を扱う場合は,単語や文章に必要な文字数が状況に応じていろいろと変化するのが普通である.

そこで,以下のような工夫をする.

  1. 処理で使用が想定される文字数よりも,大きめのサイズの char 配列を準備.
  2. 配列中において,先頭から文字を入れていき,文字列の終端に「ここで終わり」を表す特別な記号を置く.
  3. この記号を文字列の「終端」とし,終端記号以降の配列データは無視する.

この特別な記号のことをヌル文字または終端文字と呼ぶ.(ナルと呼ばれることもある.)

ヌル文字(終端文字)

ヌル文字はソースコード中で '\0' と表され,ASCIIコードでは文字コード 0x00 が割り当てられている.
ソースファイル中では,以下のように記述され,すべて等価である.

NULLは × 処理系によってはNULLが使えてしまう場合があるが,正しくない.これはヌルポインタ(nullptr)と呼ばれ,用途が異なる.ただし,ヌルポインタにも数値の0が割り当てられることが多いため,結果としてヌル文字として使えてしまうが正しくはない.

文字列の表現

文字列のは,以下のようにダブルクォーテーションで囲まれた区間で表される.

"Meiji"

このダブルクオーテーションで括られた部分を「文字列定数」という.
この例では,配列の定義と同時に初期化しており,配列の要素数を省略している.(c.f. 配列)

さて,Meiji の文字数は 5 文字であるが,実際には最後の文字 i の後にヌル文字が自動的に追加されて 5+1=6 文字分の配列が確保される.
このため,文字列のサイズを指定するときには,ヌル文字分を含めて確保する必要がある.

char str[6] = "Meiji";   // 要素数は 6 以上であればOK. 暗黙に str[5]=='\0' が設定される.
char str[] = "Meiji";    //  ヌル文字含め,6文字分の配列が設定される

とする必要がある.

もちろん,余分に配列を確保しておこくとは問題ない.
以下の場合,str[0]=='M', str[1]=='e', ... , str[4]=='i', str[5]=='\0' であり,str[6]以降str[99]までは不定である.

char str[100] = "Meiji";   // str[5]=='\0' となる.

キーボードから文字列を入力

キーボードからchar型配列に文字列を入力する例を以下に示す.
scanf() 関数において,書式指定文字列に %s を使用する.

例:キーボードから文字列の入力
#include <stdio.h>

int main(void)
{
    char str[20];         // ヌル文字を含めて 20 文字分の文字列

    printf("Input a string : ");
    scanf("%s", str);    // %s は文字列用の書式指定
                        // scanfは,入力された文字の最後に自動的にヌル文字を追加する.
                        // str は配列(=アドレス)なので,& は不要

    //  ここで,キーボードから Hello と入力

    printf("%s \n", str);    // printf関数は自動的にヌル文字の手前まで表示.ヌル文字自体は表示しない.

    return 0;
}

実行例:
input a string : ABC
ABC
input a string : 100
100

「あれ,scanf()%dを使って,数値の100と入力する場合と同じでは?」と思うかもしれません.
見かけは同じですが,実は違います.

この例では%sを使用して,文字列として入力していますので,例えば「100」と入力した場合は「'1'の文字コードに相当する整数」「'0'の文字コードに相当する整数」「'0'の文字コードに相当する整数」(と,ヌル文字)が配列strに格納されます.
画面に表示するだけであれば文字でも数値でも同じですが,四則演算や大小比較をを行うためには数値でなければならず,文字列のままでは計算ができません
一方,scanf()関数のフォーマット文字列に%d%fを使用すると,入力された文字列をscanf()関数が文字列を解析して整数や実数に適切に変換してくれています

scanf()関数によらないで,文字列を数値に変換する方法(atoi(), strtol())は別途説明します.

練習問題

  1. 上記のコードを実行し,まずはひと単語の文字列を入力してみよう.例:abc など.
  2. スペースを含む文字列を入力するとどうなるか.例:Meiji University など.
    scanf()のフォーマット指定を,"%s" から "%[^\n]%*c" に変更するとどうなるか.
  3. 文字列str[]のサイズ(=20文字)よりも長い文字列をキーボードから入力するとどうなるか試してみよう.
読み込む文字数の制限.

キーボード入力ではどのような文字・数値が入力されるかわからない.
特に,文字配列を超える文字数の文字列が入力されるとメモリの不正アクセス(バッファーオーバーフローという)になるので,scanf()では入力される文字数を制限することができる.
このテキストの説明では文字数の制限についてはあまり考慮しないが,実用的なプログラムではこのような入力文字数の制限は重要である.

#include <stdio.h>

int main(void)
{
    char str[21];         // 21 文字分の文字列(ヌル文字含む)

    printf("Input a string : ");
    scanf("%20s", str);    // %20s は,20文字で打ち切り

    //  ここで,キーボードから文字列を入力

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

    return 0;
}

文字列のメモリ上での並び

C言語の配列は,連続したメモリ領域に配置されることが保証されています. 配列は添字 0 が先頭アドレスに配置され,添字順にアドレス値が順次増加します.
従って,以下のような文字列の設定が可能です.

#include <stdio.h>

int main(void)
{
    char a[50];         // 50文字分の文字列

    int i;
    for(i=0; i<5; i++) {      // 5文字分の値を書き込む
        a[i] = 'A'+i;         // 'A' の実体は数値! 文字コード表参照
    }

    a[5] = '\0';       // 最後にヌル文字を置く.a[5] = 0x00; と書いてもOK.

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

    return 0;
}

この例では,文字列 a の,メモリ中の配置は,

となる.(最後に必ずヌル文字が置かれるが,それ以降は不定と決められている.)

練習問題

  1. 上記のソースコードを実行してみよう.
  2. 上記のソースコードの printf 関数以降にコードを追加し,配列の各要素をすべて整数として画面に順次表示せよ.
    ヌル文字以降に何が入っているでしょうか?
実行例:

a[0] = 65
a[1] = 66
a[2] = 67
 .
 .
 .
a[48] = ????
a[49] = ????

いくつかの応用例

文字列の最後に必ずヌル文字が入る事を利用して,以下のように文字列要素の全てを1文字ごとに走査(スキャン)できる.

#include <stdio.h>

int main(void)
{
    char str[] = "Hello Meiji!";     // 文字列
    int i=0;

    while( str[i] != '\0' ) {

        if(str[i] == 'M') {
            printf("Found character M!  %d th character = %c", i+1, str[i]);    // %c は,1文字表示
        }

        i++;
    }

    printf("\n");
    return 0;
}

for文を用いる場合は,以下のように書くこともできる.

for(i=0; str[i] != '\0'; i++ ) { ...

ヌル文字'\0' の文字コードは 0x00 = 0 であり,これは「偽」と等価であることを利用して,while, for の条件判定部はさらに省略できる.

while( str[i] ) { 
    // ヌル文字(=0x00)に到達したら「偽」になるので,このループが終了する
    i++;
}

この方法は便利であるが,手動で文字列を生成した場合など,文字列の最後にヌル文字を追加し忘れるとループ処理が正しく動かないので注意を要する.

文字列についてまとめ

ひらがな・漢字などの全角文字の扱い.

半角英数字を表すには,ASCIIコードで充分であるが,日本語で使用する漢字やカタカナ,ひらがな等の全角文字の場合は文字種類が非常多いため,char型 = 8bit = 256通り,では到底不足する.

いろいろな歴史的経緯があるが,現在日本語コードとしては,JISコード(ISO-2022-JP)(電子メールで使用),Shift JISコード(以前のWindows標準),EUCコード(おもに旧式のUNIX)が使用されてきたが,最近では,世界共通のUNICODE(最近のOSや,このWebページ)など,多くの体系が現在も混在して利用されている.

ここでは扱わないが,漢字などの全角文字では複数バイトで1文字を表すワイド文字型・マルチバイト文字型が使用されている. 以下のプログラムで全角文字の内部表現を確認してみよう.
(.cppファイル自体の文字エンコードによっては,異なる結果となる可能性がある.)

#include <stdio.h>

int main(void)
{
    const int N = 30;

//  char str[N] = "AAAbcdefg";     // 半角文字
    char str[N] = "あああいうえお";     // 全角文字

    int i;
    for(i=0; i<N; i++) {
        int k = (unsigned char)str[i];  // 一旦,別の変数にキャストして代入
        printf("str[%d] = %d , 0x%X\n", i, k, k);   // 10進数,16進数で表示
    }
    return 0;
}

実行結果はOSやPCの環境に依存するが,おそらくchar配列数個分で,全角1文字を表現する結果となるはずである.
このようなマルチバイト文字の文字コード判定や使い分け,文字列の比較・変換処理には高度で複雑な処理が必要となるため,ここでは半角の英数字+記号のみ取り扱う.

関数に文字列を渡す

char 型の配列である「文字列」を関数に渡す場合は,通常の数値配列と同様の手順で可能である.
ただし,文字列の場合には最後にヌル文字が含まれていることを利用して,引数を減らすことができる
(つまり,配列の要素数を渡す必要がない.)

// 文字列を関数に渡す方法
#include <stdio.h>

void disp_str( char str[] )   // 文字列を引数にとる.要素数は不要.
{
    printf("string = %s \n", str);   // この例では,単に表示するだけ.
}


int main(void)
{
    char a[] = "Hello world! My name is Meiji.";      // 文字列

    disp_str(a);    // 文字列を引数として,関数呼び出し.要素数は不要

    return 0;
}

文字列を関数に渡す場合は,「最後に必ずヌル文字がある」という約束のもとで,このように配列のみを渡せば良い.
逆に,この約束を破ってヌルで正しく終端されていない文字列を関数に渡すと,さまざまな事故の原因になる.

練習問題

上記サンプルの disp_str 関数を参考に

  1. 文字列の奇数番目の文字(=配列の添え字が偶数)のみ表示する関数 disp_odd() を作り,動作を確認せよ.
  2. 文字列を逆順に表示する関数 disp_rev() を作り,動作を確認せよ.
    ヒント:まずヌル文字の位置を探そう.

引数の型や個数,名前などは,各自で適切に設定せよ.

文字列操作ライブラリ関数

既に述べたように,C言語ではプリミティブな「文字列型」は存在せず,「文字型」の「配列」を使用することで対応しているため,文字列の操作によくある「複製」や「連結」,「検索」などが面倒である.

そこで,文字列操作に特化した便利なライブラリ関数が用意されている. そのうちのいくつかを実際に使用してみよう.
詳しくは,C言語ランタイムライブラリ関数のリファレンスを参照.https://learn.microsoft.com/ja-jp/cpp/c-runtime-library/string-manipulation-crt?view=msvc-170

以下は,string.h で定義されている文字列操作用のライブラリ関数である.
(ここに出てくるchar* とは,「文字列の先頭アドレス」を指すポインタ型であり,配列をそのまま渡すことができる.)

文字列操作関数は非常に多岐にわたるが,ここでは4種類のみ紹介する.

  1. strlen()・・・文字列の長さ(Length)を返す.
  2. strcpy()・・・文字列を,別の文字列のコピー(copy)する
  3. strcat()・・・2つの文字列を連結(catenate)する.
  4. strcmp()・・・2つの文字列を比較(compare)する.

練習問題

strlen

int strlen(const char* s);
このプロトタイプ宣言は,「引数は文字列1つ」「戻り値は整数」を表す.

文字列の長さを返す.string lengthの略.
このとき,最後のヌル文字は長さに含まない

#include <stdio.h>
#include <string.h>        // 文字列操作関数の使用に必要

int main(void)
{
    char str[] = "Abcdefg";

    printf(" string = %s \n", str );
    printf(" length is %d \n", strlen(str) );

    return 0;
}

strcpy

char* strcpy(char* dst, const char* src);
このプロトタイプ宣言は,「引数は文字列2つ」を表す.

上記の関数プロトタイプ宣言は,「引数は文字列2つ」「戻り値は文字列の先頭(アドレス)」を表す.以下同様.

文字を src(sourceの意) から dst(destinationの意) にコピーする関数である.string copy.
第1引数dst に既に文字が入っている場合は,上書きされる.
第2引数srcにはconstがつけられているため,こちらはstrcpy()によって変更されることはない.

#include <stdio.h>
#include <string.h>

int main(void)
{
    char str1[] = "Meiji university";
    char str2[100] = "Oh No!";     /*  100文字分の文字列. */

    /*  str1 から str2 へ文字列をコピー */
    strcpy(str2, str1);

    printf("str1 = %s\n", str1);
    printf("str2 = %s\n", str2);

    return 0;
}

strcat

char* strcat(char* dst, const char* src);
このプロトタイプ宣言は,「引数は文字列2つ」を表す.

文字列を連結する関数.string catenate.
dst に入っている文字列の末尾に,文字列 src を連結(追加)する.

#include <stdio.h>
#include <string.h>

int main(void)
{
    char str1[100] = "Meiji";
    char str2[100] = " university";

    /*  str1の最後尾にstr2を追加. */
    strcat(str1, str2);

    printf("str1 = %s\n", str1);
    printf("str2 = %s\n", str2);

    return 0;
}

strcmp

int strcmp(const char* s1, const char* s2);
このプロトタイプ宣言は,「引数は文字列2つ」「戻り値は整数」を表す.

2つの文字列を比較する.string compare.
戻り値は

である.
興味があれば,ゼロ以外の値としてどのような値が返ってくるか,自分で調べてみよう.

#include <stdio.h>
#include <string.h>

int main(void)
{
    char str1[100], str2[100];

    printf("str1 = ?"); scanf("%s", str1);
    printf("str2 = ?"); scanf("%s", str2);

    if( strcmp(str1, str2) == 0) {
        printf("str1 = %s, str2 = %s are identical.\n", str1, str2);
    } else {
        printf("str1 = %s, str2 = %s are different.\n", str1, str2);
    }

    return 0;
}

文字列から数値への変換

文字列は,あくまで文字列なので,「100」や「-0.5」などの数値を入力するには,工夫が必要である.

例えば,文字列としての「100」(文字コード 0x31 0x30 0x30)は,文字として1,0,0(イチ,ゼロ,ゼロ)がchar型配列3個分に格納されているだけなので,数値の「百」として四則演算などを行うためには,まず文字列を数値に変換する必要がある

文字列を整数および実数に変換するライブラリ関数( stdlib.h )は,atoi(), atof() であり,それぞれ,ascii to int, ascii to float の略である.
より高度な変換処理を行う strtol(), strtof() もある.
これはstring to long, string to float の略.

scanf()でキーボードから入力する際には,この文字→数値の変換が自動的に行われている.)

練習問題

下記の例で,キーボードから数値(100, -20, 3.14 など)だけでなく,文字(A, ZZZ など)を入力してみよう.
どのような動作をするか.

#include <stdio.h>
#include <stdlib.h>        // atoi用

int main(void)
{
    char str[100];
    scanf("%s", str);      // %s = 文字として入力

    int n = atoi(str);     // ここで文字列を数値に変換.変換できない場合は0が返る.

    printf("character : %s \n", str);     // 文字列として表示
    printf("number    : %d \n", n);       // 整数として表示

    return 0;
}

以下の例は,キーボードから繰り返し数値を入力し,"end"が入力されたら終了する. 無限ループで文字列をキーボードから入力し,"end"であれば終了. それ以外は数値に変換し,その値を2倍した数値を表示する.

#include <stdio.h>
#include <string.h>        // strcmp用
#include <stdlib.h>        // atof用

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

    while(1) {

        printf("Input a number(type [end] to finish):");
        scanf("%s", str);           // 文字として入力

        if( strcmp(str, "end") == 0 ) {
            printf("Finish!\n");
            break;
        }

        float r = atof(str);               // 文字列を数値に変換.
        printf("number*2 = %f\n", r*2);       // 数値として,2倍して表示
    }

    return 0;
}

練習問題

上記のプログラムを実行して動作を確認してみよう.

まとめ

Q and A

Q and A
ヌル文字と0の違いが判りません.
コンピュータ上での内部表現は,どちらも数値のゼロと等価です.ただし,「終端を表す文字です」とソースコード中に明示するために '\0' と書いておくことが望ましいです.
終端文字として NULL と書いてはだめですか?
NULLポインタはポインタ型で0番地を指すよう定義されている場合が多いので,結果的に問題なく動く場合が多いですが,本来は型が異なるので,コンパイラによってはエラーだ出る場合があります. ライブラリリファレンスには,「NULL は、多くのポインター操作と関数で使用される Null ポインター値です。 これは 0 と同じです。 」と書かれています.
文字列の終端にヌル文字が無い場合はどうなりますか?
scanf()str???() 系の関数では,文字列に自動的にヌル文字を付加しますが,自分で文字列を設定した場合にヌル文字を忘れると,関数が思いがけない動作をします. 例えば,printf("%s", ...) では,ヌル文字を探してそこまで表示しようとするので,ヌル文字が無いと配列の境界を飛び越えて読み込もうとし,アプリケーションが不正終了する(落ちる)場合があります. 逆に,わざと終端文字を抜いて関数呼び出しして,不正アクセスさせるようなクラッキング方法(バッファーオーバーフロー)もあるので,十分注意が必要です.
また,このような不正アクセス対策のために,例えば strcpy() ではなく,文字数を制限できる strcpy_s() を使うことが推奨されています. そのほか,printf_s()scanf_s()strcat_s() など,_s がつく関数がいくつかあります. 興味があれば調べてみてください.
文字列の操作に,いちいち関数を呼ぶ出すのは面倒です.
C++言語の標準テンプレートライブラリ(STL)では,std::string という便利なクラスがあります. 詳しく知りたい人は,C++言語を勉強すると良いでしょう. ただし,その場合でも,C言語では文字列がどのように扱われているか,を知っておく必要があります.
日本語(平仮名や漢字)を扱うにはどうしたらよいですか?
1バイトのchar型ではなく,2バイト以上wchar_t型や,char16_t型とchar32_t型を使うことで実現可能です.
また,これに対応した文字列操作関数も用意されています. ただし,これらを正しく使いこなすには様々な文字コード,マルチバイト文字,ワイド文字,ロケールなどの規格の理解が不可欠です.

練習問題

以下の問それぞれに対応するプログラムを作成しなさい.
  1. 以下の 6×6 の表に従い,「科目」の平均点と,「個人」の平均点を計算して,表全体を画面に表示するプログラムを作成せよ.
    表の初期値は,例のとおり二次元配列の初期化を用いてソースコード中に記入してある(右端と下段の 0 の部分).
    科目名には順に添字番号(0=語学, 1=材力, ..., 5=個人平均など)を割り当てることとする.

    ヒント:配列の添え字の範囲をよく考えて,上手にfor文の範囲を決めよう.

    /* ヒント:添え字は[出席番号] [科目]の順となる. */
    
                        /*  語学  材力  熱力  機力  流力  個人平均*/
    float score[6][6] = { {100,   71,   74,   82,   80,   0},   /* 出席番号1番 */
                          { 64,   83,   74,   94,   62,   0},   /* 出席番号2番 */
                          { 70,   70,   48,   80,   78,   0},   /* 出席番号3番 */
                          { 93,   95,   57,   92,  100,   0},   /* 出席番号4番 */
                          { 21,   45,  100,   70,   10,   0},   /* 出席番号5番 */
                          {  0,    0,    0,    0,    0,   0} }; /* この行に科目平均を計算して代入 */
    
    /* 注:整数定数は,自動的に実数型に変換される */
    /* 表の右端の列と,最下行の 0 の部分に平均を計算 */
    

  2. strlen関数を自作してみよう.関数名は My_strlen()とする.もちろんライブラリ関数の strlen() を使用しないこと.
    ヒント:ヌル文字の位置を探そう.整数を返す際,ヌル文字は文字数に含まない.

    #include <stdio.h>
    
    ??? My_strlen(...)
    {
        ....
        return ????;
    }
    
    int main(void)
    {
        char str[100] = "Hello Meiji Univ.";
    
        printf("Length = %d\n ", My_strlen(str));
    
        return 0;
    }
    
    
    実行例
    Length = 17
    
    

  3. キーボードから文字列を入力すると,その最初の文字と最後の文字を表示するプログラムを作成せよ.
    ヒント:strlen() 関数に文字列を渡すと,配列中の文字数がわかる.

    #include <stdio.h>
    #include <string.h>        // strlen()用
    
    int main(void)
    {
        char str[100];       /* 100文字分の文字列 */
    
        printf("Input a string: ");
        scanf("%s", str);      /* 文字列入力.strは配列なので,&は不要. */
    
        int n = strlen (str);   /* nに文字数が入る */
    
        ...
        
        return 0;
    }
    
    
    実行例
    Input a string: ABCDEFG
    The first char is A
    The last char is G
    
    Input a string: Z
    The first char is Z
    The last char is Z
    
    

  4. 上の本文中で表示したアスキーコード表と同じものを書いてみよ.
    この時,画面に印字されない制御コードを書いてしまうと表示が乱れるので,ライブラリ関数isprint()関数を用いて表示可能なものだけ表示せよ.
    isprint()関数の使用法をまず各自で調べること.

    Microsoft社のCライブラリ関数リファレンス(アルファベット順)
    https://msdn.microsoft.com/ja-jp/library/634ca0c2.aspx

    ヒント:isprintは、文字 c を渡すと,c が表示可能な文字 (0x20 – 0x7E) である場合,0 以外の値を返す.
    つまり,isprint('A') != 0 ,isprint('\n') == 0となる.

    isprint()関数の使用例.変数cの値を'A'や'\n'に変えてみよ.
    
    #include <stdio.h>
    
    /* これを追加すること. */
    #include <ctype.h>
    
    int main(void)
    {
        char c='a';  /* 何か文字 */
    
        if( isprint(c) != 0) {
            printf("表示可能です\n");
        } else {
            printf("表示できません\n");
        }
        
        return 0;
    }