法大奥山研究室

 previous  contents

9.3. 文字列の操作


 初期化子 {定数}char型配列の場合には文字列リテラルも)を使った初期化,例えば,

char str[8] = "ABCD";
char x[] = {0x41, 0x42, 0x43};
int n[10] = {89, 67, 93};

は可能ですが,単純代入演算子にこれらを使うことはできません。例えば,

char str[8];
str[8] = "ABCD";   // 文法エラー

の場合,2つ目は宣言でなく,単純代入演算子を使った式(代入式)を実行させる文(式文)です。左辺側 str[8] は単なる char型ですが,文字列リテラルは配列型です。異なるものを代入しようとしているため,エラーが出ます。文字列リテラルや {89, 100} は,宣言における初期化子には使えますが,単純代入演算子のオペランドには使えません。

 単純代入演算子を使った代入は,個別要素には可能です。例えば,

char str[8];
str[0] = 'A';

の場合,第1要素 str[0]'A' が代入されます。

 配列は要素型のオブジェクトが要素数分連なったデータなので,要素毎の代入となります。したがって,文字列を配列に「代入する」には,配列の各要素にその文字列の一文字ずつを代入してゆく作業となります。これを「コピー」と言います。文字列をコピーする関数はライブラリ string.h に幾つかあります。

#include <string.h>
strcpy(str1, str2);
strncpy(str1, str2, size);
memcpy(str1, str2, size);
memset(str, c, size);

memset を除き,いずれも str2str1 へコピーします。size はコピーする文字数を指定します。memset は文字 csize分コピーします。また,次の関数は str2str1 の末尾に連結します。

#include <string.h>
strcat(str1, str2);
strncat(str1, str2, size);
/* Example 9.4 */

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

int main(void)
{
       char str[] = "ABCD";
       char buf[20];

       memset(buf, 0, sizeof(buf)); // memset(buf, '\0', 20) と同じ。
       strcpy(buf, str);
       printf("%s\n", buf);
       strcat(buf, "EFGH");
       printf("%s\n", buf);

       return 0;
}

変換指定子 %s は文字列を出力します。(正確な用法は後程を示します。)実行結果です。

ABCD
ABCDEFGH

配列 buf に文字列 ABCD がコピーされ,文字列 EFGH が末尾に連結されています。

Q バッファ・オーバーフロー
当初のメモリ領域を超えてメモリにデータが書き込まれることを「バッファ・オーバーフロー」(Buffer Overflow)とか「バッファ・オーバーラン」(Buffer Overrun)という。例えば,
    char buf[4];
    strcpy(buf, "ABCDEFG");
とすると,buf には7文字がコピーされ,3バイト分オーバーフローする。オーバーフローした部分はそのままメモリに書き込まれ,実行すると segmentation fault となる。main関数以外の関数内でバッファ・オーバーフローが起こる場合,main関数へのリターンアドレスを書き換えることが可能となり,そこへシェルコマンドの機械語(シェルコード)を送ることでそのシェルコマンドを実行させることができる。バッファ・オーバーフローを起こさせないためには,strcpy の代わりに
    memcpy(str1, str2, sizeof(str1)-1);
    strncpy(str1, str2, sizeof(str1)-1);

また,strcat の代わりに
    strncat(str1, str2, sizeof(str1)-strlen(str1)-1);
とすると良い。strlen関数はライブラリ string.h で定義されている関数で,文字数を size_t型で返す。


 previous  contents