- memcpyの使い方と実装
memcpyは、指定されたバイト数分のデータをソースからデスティネーションへコピーする関数です。特に文字列終端文字'\0'を特別扱いせず、純粋にバイト単位でコピーを行います。
使用時の注意点
コピー元とコピー先のメモリ領域が重複している場合、挙動は未定義です。 バイト単位でコピーするため、型の違いによる影響はありません(ただし無効なアドレスや境界外アクセスには注意が必要)。
自我実装例((my_memcpy))
void* my_memcpy(void* dst, const void* src, size_t n) {
void* head = dst;
while (n--) {
*(unsigned char*)dst = *(const unsigned char*)src;
dst = (unsigned char*)dst + 1;
src = (const unsigned char*)src + 1;
}
return head;
}
メモリ重複時の問題点 前から順にコピーすると、既に上書きされたデータを参照してしまうため、重複領域へのコピーには適していません。例えば、配列の先頭5要素(1~5)を、3番目以降へコピーする場合、2番目までコピー後に、3番目と4番目が既に1, 2になるため、以降のコピーで予期せぬ値(1, 2, 1, 2, 1…)が生成されます。
この問題に対応するため、memmoveが用意されています。
- memmoveの使い方と実装
memmoveは、コピー元とコピー先のメモリ領域が重複している場合でも正しく動作するように設計されています。内部挙動として、重複方向を判定し、コピー方向(前→後 or 後→前)を動的に切り替えます。
自我実装例(my_memmove)
void* my_memmove(void* dst, const void* src, size_t n) {
unsigned char* d = (unsigned char*)dst;
const unsigned char* s = (const unsigned char*)src;
if (d < s) {
// 前からコピー(重複がない・または後ろ側が重複)
for (size_t i = 0; i < n; ++i) {
d[i] = s[i];
}
} else if (d > s) {
// 後ろからコピー(前側が重複)
for (size_t i = n; i > 0; --i) {
d[i - 1] = s[i - 1];
}
}
return dst;
}
補足 規格上、memcpyは非重複コピー専用とされていますが、一部の実装では内部的にmemmove相当の処理をしている場合があります。明確に意図を伝えるためにも、重複する可能性がある場合はmemmoveを使いましょう。
- memsetの使い方
memsetは、メモリ領域を指定したバイト値で埋める関数です。主にメモリ領域の初期化(0クリアなど)に使われます。
注意点: 引数valueはint型ですが、実際には下位1バイトのみが使用されます。
int arr[10];
// 全要素を0で初期化
memset(arr, 0, sizeof(arr));
// ※誤:memset(arr, 1, sizeof(arr)); → 各要素が0x01010101(多くの環境で1にはならない)
- memcmpの使い方
memcmpは、2つのメモリ領域をバイト単位で比較する関数です。
戻り値の意味
0:比較範囲すべてが等しい 正の値:第1引数が第2引数より大きい(最初に異なるバイトで比較) 負の値:第1引数が第2引数より小さい
使用例
const char* str1 = "abcXYZ";
const char* str2 = "abcDEF";
int res = memcmp(str1, str2, 3); // 先头3文字比較 → 0
int res2 = memcmp(str1, str2, 6); // 全長比較 → 負の値('X' < 'D' ではないが、バイト比較では'X'(0x58) > 'D'(0x44) → 正の値)