プログラムにおいて、メモリを操作するための関数というものはC言語でもたくさん用意がされています。
メモリコピーができる関数
C言語において、メモリコピーができる代表的な関数として、以下が用意されています。
●指定されたサイズ分、メモリをコピーする
#include <string.h> void *memcpy(void *dest, const void *src, size_t n);
(src のデータメモリを dest へ nバイト分コピーする)
●こちらも同じく、指定されたサイズ分のメモリをコピーする
#include <string.h> char *strncpy(char *dest, const char *src, size_t n);
(src のデータメモリを dest へ nバイト分コピーする)
●同じようにメモリをコピーする関数で、サイズを指定しないものもあります。
#include <string.h> char *strcpy(char *dest, const char *src);
この関数の場合、「src のデータメモリを dest へコピーする」ですが、サイズの指定はありません。
文字列を正しく扱おう
strcpyのようなコピーするサイズが指定できない場合、srcからdestへ、どれだけのサイズのデータがコピーされるのか?というと、
src で「NULL(\0)」が検出されるまで、1バイトずつ dest へコピーを続ける
という条件になります。
こういった時、src のメモリは事前にコピーしたいデータの末尾にNULL(\0)をセットしておかないと、どこまでもコピーを続けてしまい、
しまいには destの全データエリアを超えてコピーされ続けます。
◆間違いのケース
#include <string.h> char src[5] = {'H', 'E', 'L', 'L', 'O'}; char dest[5] = {0,}; //5バイトの配列を\0埋めして初期化しておいただけ strcpy(dest, src);
このようなコードを記述した場合の動きは下記のようになる。
この結果何が起きるのかというと、メモリオーバーフロー と呼ばれる現象で、大抵のプログラムは意図した動きではなくなり、上図でいう「変数 x」と「変数 q」には全く関係ない値がコピーされてしまい、最終的にプログラムは演算がおかしくなり、暴走します。
これが文字列操作でよくある問題です。
◆良いケース
srcを文字列としてコピーしたい時には、データの末尾に必ずNULL(\0)を入れてあげよう。
そのために、NULL(\0)をセットするためのサイズも1バイト多く確保しておくこと。
(例1)
#include <string.h> char src[6] = {'H', 'E', 'L', 'L', 'O', '\0'}; char dest[5] = {0,}; //5バイトの配列を\0埋めして初期化しておいただけ strcpy(dest, src);
(例2)memset()、memcpy()関数で src のデータを整えることもできる
#include <string.h>
char src[6];
char dest[5];
memset(src, 0x00, sizeof(src)); //6バイトの配列を\0埋めして初期化しておいただけ
memcpy(src, "HELLO", 5); //srcに「HELLO」と5バイト格納する
memset(dest, 0x00, sizeof(dest)); //5バイトの配列を\0埋めして初期化しておいただけ
strcpy(dest, src);
この処理のイメージは、こうなる。
\0 の前までしかコピーしないので、元々他の場所で割り当てられていた変数x、変数qにも全く影響せずメモリコピーできます。
文字列コピー以外には標準出力関数でも同じ条件
これらの話は、標準出力関数である「printf()」や「sprintf()」でも関係します。
printf()、sprintf() は、文字列を「%s」と指定して出力することが多いと思います。
この「%s」も同じで、NULL(\0)までを読み込みながら出力するので、いつまでもNULL(\0)が存在しないメモリを出力しようとすると、破綻します。
このように、文字列を操作する際には、プログラムが暴走しないよう、NULL(\0)を上手に利用しましょう。
C言語の基礎をイメージしながら勉強できます
コメント