Thinkers'Studio
JavaとC言語の自習ツール
連続した空白をひとつの空白にする関数 (文字列を関数に渡す例)

文字列を関数に渡すには?

 今回は、文字列を受け渡しして操作するプログラム例を紹介します。 次のような関数をつくります。
【 用意する関数 】
 void uniteBlanks( char *s, const char *t )
 機能 : 連続した空白をひとつの空白にしながら t を s にコピーする
    (unite "..を合わせて一つにする")

【 連続する空白をひとつの空白にする関数例 】
 元の文字列と結果を入れる文字列を渡して、uniteBlanks関数を呼び出します。
#include <stdio.h>

void uniteBlanks( char *s, const char *t )
{
    while( (*s++ = *t++) != '\0' ) {   // tからsへコピーする
        if( *(t-1) == ' ' ) {          // もし空白文字だったら
            while( *t == ' ' ) t++;    // 続く空白を読み飛ばす
        }
    }
}

main()
{
    char str[] = "Tomorrow   is another     day.";
    char result[128];

    uniteBlanks( result, str );
    printf( "result = %s\n", result );
}
 実行結果
result = Tomorrow is another day.
 t の指す文字 *t を sの指すところ *s に代入し、それぞれ ++ して指す場所を進めます。
 代入した文字が '\0' なら文字列の終わりなので、繰り返しを抜けます。 そうでないときは、if 文に進みます。
 コピーしたのが空白文字で、いま t が指している *t も空白文字なら、空白が連続しています。 内側の whileループで、空白でない文字に行きあたるまで t++ して、t を読み進めます。

文字列を関数と受け渡しする際の取り扱い

関数の引数宣言はポインタか配列

 配列を関数に渡すときは、アドレスを渡すことになります。
 上の例ではポインタとしましたが、次のように配列の形で書くこともできます。
 void uniteBlanks( char s[] , const char t[] )
 関数内の処理もポインタの操作、配列の操作のどちらで書いても構いません。 引数宣言と統一する必要はありません。

アドレス渡しなので書き換えに注意

 アドレスを渡すので、関数の中で値を書き換えると元の値まで変わってしまいます。
 変更させたくない引数は、安全のため上の例のように const をつけて宣言します。

呼び出すときは配列名を渡す

 配列名だけ書くと、C言語では配列の先頭アドレスのことです。 result は &result[0]、str は &str[0] と同じ意味です。

(今回のクイズです)
 上記プログラムの関数の引数宣言を、次のように明らかに小さなサイズを指定した配列に変えたとします。
 void uniteBlanks( char s[5] , const char t[5] )
 プログラムのコンパイルや実行はどうなるでしょうか? (3択です)
 (1) コンパイルでエラーになる、 (2) 実行時に Segmentation fault で落ちる、 (3) 実行が正常に終わり上と同じ結果になる
    (答えは、次回のC言語の Tips で ・・・)
前回のクイズの答え: (前回の問題を見る
 printf("小数点以下第2位で切り上げ = %.1f\n", kiriage( d*10.0 ) / 10.0 );
 printf("小数点以下第2位で切り捨て = %.1f\n", kirisute( d*10.0 ) / 10.0 );
10倍した値を関数に渡し、返された値を 10.0 で割って元の位に戻します。 10 でなく 10.0 で割らないと double型で求まりません。 なお、浮動小数点数の演算には誤差が含まれます。この方法でかなり下位の桁での結果を求めようとすると、誤差が大きくなり期待する答えになりません。