12月 21日


第11章

文字列とポインタ
教科書 p.248

文字列リテラルを評価すると、その文字列リテラルの先頭文字へのポインタになる (List 11-1)。

    /* どちらも可能 */ 
    char str[] = "ABC";    /* 配列… */
    char *ptr  = "123";    /* ポインタ… */

    /* 読み出すだけならどちらも同等 */
    printf("str = \"%s\"\n", str);
    printf("ptr = \"%s\"\n", ptr);

配列とポインタの共通点
教科書 p.249

添字を使って読み出すときも、違いはない(List 11-2)。

    for (i = 0; str[i]; i++) {
        putchar(str[i]);    /* str[i]は、先頭からi個後ろの要素 */
    }
    putchar('\n');

    for (i = 0; ptr[i]; i++) {
        putchar(ptr[i]);    /* ptr[i]は、先頭からi個後ろの要素 */
    }
    putchar('\n');
配列とポインタの違い
教科書 p.250
代入をしようとすると違いが現れる(List 11-3)。
    str = "DEF";   /* コンパイル時エラーになる ← 配列は直接代入できない */
    ptr = "456";   /* エラーにならない */
    str[0] = 'A';   /* エラーにならない、配列の各要素は書き込み可 */
    ptr[0] = '4';   /* 間違い: 結果は処理系により異なるができないと思った方がよい */ 
文字列の配列
教科書 p.252
List 11-5
   char st[3][6] = {"Turbo", "NA", "DOHC"};
   char *pt[3]   = {"12345", "12", "1234"};
どちらも可能だがメモリ中の配置が異なる(Fig.11-5参照)。
コマンドラインパラメータ

プログラムを起動する時にコマンドラインパラメータを渡すことができる。

コマンドラインパラメータは Cプログラムの中では “ポインタで実現する文字列の配列” (char *[]型、あるいは char **型)として表現されている (argvexample.c)。

/* ↓の行は int main(int argc, char **argv) と書いても同じ */
int main(int argc, char *argv[]) 
{
    int i;

    for (i = 0; i < argc; i++) {
        printf("%s\n", argv[i]);
    }

    return 0;
}

argv[0]に“プログラムを起動したコマンド名”が入る。 argcにはコマンドラインパラメータの個数(argv[0]も含む)が入る。

実行例: (の部分は実行環境により異なる。)
> argvexample hello! 1 2
\argvexample.exe
hello!
1
2 
文字列の長さを調べる(ポインタ版)
教科書 p.254
p.216のプログラムを ポインタを使って書き換える。 (List 11-6
int str_length(const char *s) {
    int len = 0;
    
    while (*s++) {
        len++;
    }

    return len;
}
文字列のコピー
教科書 p.256
List 11-7
char *str_copy(char *d, const char *s) {
    char  *t = d; 

    while (*d++ = *s++)  /* ==ではない */
        ;
	
    return t;
}
現在のコンパイラなら、
    while (d[i] = s[i]) {  /* ==ではない */
        i++;
    }
と書いても効率が極端に悪くなるようなことはない。 しかし、前者のようなポインタを使った書き方が簡潔なため好まれる傾向がある。
文字列の不正なコピー
教科書 p.258

文字列リテラルを書き換えてはいけない (List 11-8)。 文字列リテラルを書き換えた時の振る舞いは処理系に依存する。

ポインタを返す関数
教科書 p.259

str_copy関数がchar *を返しているのは、 この関数を利用する部分を簡潔に書けるようにするため。

    str_copy(s2, s1);
    printf("s2 = %s\n", s2);
    printf("s2 = %s\n", str_copy(s2, s1));
と書くことができる。

文字列操作関数
教科書 p.260

いくつかの文字列操作関数がstring.hヘッダーに宣言されている。

名前と型説明実現例
size_t strlen(const char *s) 文字列の長さ List 11-9
char *strchr(const char *str, int c) 文字列中の文字の出現位置 strchr.c
char *strcpy(char *s1, const char * s2) 文字列のコピー List 11-10
char *strncpy(char *s1, const char * s2, size_t n) 文字列のコピー* List 11-10
char *strcat(char *s1, const char *s2) 文字列の連結 List 11-11
char *strncat(char *s1, const char *s2, size_t n) 文字列の連結* List 11-11
int strcmp(const char *s1, const char * s2) 文字列の比較 List 11-12
int strncmp(const char *s1, const char * s2, size_t n) 文字列の比較* List 11-12
*のついている関数は操作する文字数に制限をかけることができる。
空ポインタ
教科書 p.260

“何も指さない”ポインタ定数として NULLというマクロが用意されている。 NULLはエラーや例外的な状況を表現するのに利用される。 NULLは実は定数 0なので、if文や while文の 条件式として使用すると偽(false)を意味する。 (NULL以外のポインタは必ず0以外、つまり真(true)になる。)

NULLはstddef.hに定義されている。 (stdio.h, stdlib.h, string.h, time.hのいずれかをインクルードしても良い。)

文字列変換関数
教科書 p.264

"144""3.14"などの文字列を数値に変換する関数が、 stdlib.hに宣言されている。 (使用例: List 11-13
名前と型説明
int atoi(const char *nptr) int型に変換する
long atol(const char *nptr) long型に変換する
double atof(const char *nptr) double型に変換する


戻る


Koji Kagawa(kagawa@eng.〜)