初期化子 {定数}
(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
を除き,いずれも str2
を str1
へコピーします。size
はコピーする文字数を指定します。memset
は文字 c
を size
分コピーします。また,次の関数は str2
を str1
の末尾に連結します。
#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
が末尾に連結されています。
バッファ・オーバーフロー
当初のメモリ領域を超えてメモリにデータが書き込まれることを「バッファ・オーバーフロー」(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
型で返す。