前回のブログでは文字列関数を学習し、いくつかの文字列に対して一連の操作を実行できるようにしました。次に、いくつかのメモリ変更関数 (#inlcude<string.h>) を学び、mempy、memmove、memcmp 関数について一緒に見ていきましょう。
目次
メンプシー関数
関数プロトタイプを通して、戻り値と一部のポインター パラメーターの型が void* 型であることがわかります。これは、この関数が文字列に限定されず、整数配列、構造体配列などに対して実装できることを意味します。単位はバイトです。前のポインターの型が void* であるため、受信配列の要素の型がわかりません。そのため、3 番目のパラメーターの単位はバイトであり、多くのデータ型をコピーできます。
memcpy 関数はメモリ ブロックをコピーする関数で、コピー元の位置からコピー先のメモリ位置に num バイトのデータを逆方向にコピーします。
1. パラメータ: 宛先ポインタは、ターゲットとして使用される任意の型の配列の最初の要素のアドレスを受け取ります。
ソース ポインターは、コンテンツのコピーに使用される同じタイプのポインター変数も受け取ります。
size_t num はコピーするバイト数です。
2. 戻り値: ターゲットのポインタを返します。
3. 関数は '\0' で停止せず、ソースで終了する null 文字をチェックしません。常に正確なバイト数をコピーします。
4. コピー元とコピー先の間に重複がある場合、コピーの結果は不定になります。
次に memcpy 関数を参照します。
int main(void)
{
int arr1[] = {1,2,3,4,5,6,7,8,9,10};
int arr2[20] = { 0 };
memcpy(arr2, arr1, 40);
int i = 0;
for (i = 0; i < 20; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
arr1 の 40 バイトのメモリ内容を arr2 にコピーします。arr2 にはゼロが 20 個ありますが、arr1 の要素は 10 個だけコピーされるため、arr2 の最初の 10 要素は arr1 の内容で上書きされます。結果は次のようになります。 次に、memcpy 関数の実装をシミュレートします 。
模拟实现
void* my_memcpy(void* dest, const void* src, size_t num)
{
void* ret = dest;
assert(src && dest);
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
int main(void)
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[20] = { 0 };
my_mempy(arr2, arr1, 40);
for (int i = 0; i < 20; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
カスタム関数プロトタイプを作成するために、memcpy 関数プロトタイプをモデルにした my_memcpy 関数を作成します。戻り値はターゲット配列の最初の要素ポインターであるため、最初にターゲット配列をマークする void* ポインターを作成します。次に、while ループを使用してバイトごとに値を割り当てます。memcoy 関数はあらゆる種類の配列をターゲットにできるため、すべての種類の配列と互換性を持たせるために、すべての void* 型ポインターを最小単位の char* 型のポインターにキャストする必要があります。代入が完了したら、ポインタに 1 を加えます (void* 型のポインタの場合、++ 演算を実行できないため、dest = (char*)dest + 1; の形式でしか記述できません)。num_ が 0 に達すると、すべての割り当てが完了したことが証明され、ループから抜け出して元のポインタに戻ります。
実行の結果は次のとおりです。
最初に memcpy 関数で得られた結果と同じです。
次に別の質問があります。memcpy 関数を使用して配列の内容を自分自身にコピーできますか? もっと簡単に言うと、arr1 Put には整数配列 int arr1[] = {1,2,3,4,5,6,7,8,9,10}; 1, 2, 3, 4, 5 があります。 3、4、5、6、7の位置ですか?
int main(void)
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
memcpy(arr1 + 2, arr1, 20);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
arr1 の最終内容: 1,2,1,2,3,4,5,8,9,10 を考えますが、結果はどうなるでしょうか。
では、なぜそうなるのでしょうか?
では、今の内容を完成させるためにはどうすればいいのでしょうか?これは memmove 関数を使用して行われます。!
メモリ移動関数
memmove 関数は、メモリのブロックを移動し、ソースが指す位置から宛先が指すメモリ ブロックに バイト数の値をコピーする関数です。コピーは中間バッファを使用しているかのように行われ、コピー先とコピー元を重複させることができます。
1. 戻り値とパラメータは mempy 関数と同じです。理解するには上記を参照してください。
2.ソースポインタと宛先ポインタが指すオブジェクトの基礎となる型はこの関数とは無関係で、結果はデータのバイナリ コピーになります。この関数は、ソースに終端の null 文字があるか
どうかをチェックしません。常に正確なバイト数をコピーします。オーバーフローを回避するには、宛先引数とソース引数が指す配列のサイズは少なくともバイト数である必要があります。3. memcpy との違いは、memmove 関数によって処理されるソース メモリ ブロックとターゲット メモリ ブロックが重複できることです。ソース空間とターゲット空間が重なっている場合は、memmove 関数を使用して対処する必要があります。
次に、memmove 関数を使用して上記の問題を解決しましょう。
int main(void)
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(arr1 + 2, arr1, 20);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
先ほどの質問は非常にスムーズに終わりました。
次に、memmove 関数の実装をシミュレートしましょう。
void* my_memmove(void* dest,const void* src, size_t num)
{
void* ret = dest;
if (dest < src)
{
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
else
{
while (num--)
{
*((char*)dest + num) = *((char*)src + num);
}
}
}
int main(void)
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr + 2, arr, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
my_memmove 関数を作成し、memmove プロトタイプに従ってカスタム関数のパラメーターを完成させます。しかし、関数本体をどのように完成させればよいのでしょうか?
最も基本的な前から後ろへのコピーだけでは目的の結果は得られませんが、後ろから前へコピーすると目的の配列が得られます。文章の場合は前から後ろに写す必要があるので、以下にまとめます。
そこで、dest ポインター < src ポインター、内部のコンテンツが my_memcpy コンテンツと同じである場合に、カスタム関数本体を完成させるために状況をスコアリングします。dest ポインタ > src ポインタの場合、while ループを使用し続け、後ろから前にコピーして完了します。判定条件が num-- の間、逆参照の前に nun バイト数を加算することでポインタを後ろから前にアクセスでき、終了判定時にポインタを移動することもできるので一石二鳥です石。最後に、ターゲット配列の最初の要素のアドレスを返します。my_memcpy 関数との唯一の違いについては、別途説明する必要があります。
操作の結果は次のようになります。
これで機能シミュレーションは完了です。
上記 2 つの関数について、memcpy 関数でできることを memmove 関数でもできるかどうかという疑問が生じます。それは確かだ。したがって、memmove 関数の役割は mempy 関数よりも上位にある必要があります。!!
memcmp関数
memcmp 関数は 2 つのメモリ ブロックを比較します。これは strcmp 関数に似ており、2 つの配列が同じかどうか、および戻り値が同じかどうかを比較します。ただし、違いは、memcmp がより多くの型を比較できることです。この関数は null 文字を見つけても比較を停止しませんが、strcmp は停止します。
PTR1
メモリのブロックへのポインタ。
PTR2
メモリのブロックへのポインタ。
番号
比較するバイト数。
メモリに保存されているデータを比較することにより、バイトごとの比較が行われます。
memcmp 関数を使用した例を次に示します。
int main(void)
{
int arr1[10] = { 1,2,1,4,5,6 };
int arr2[10] = { 1,2,257 };
int ret = memcmp(arr1, arr2, 9);
printf("%d\n", ret);
return 0;
}
arr1 と arr2 の最初の 9 バイトが同じかどうかを比較しますか?
arr1 の最初の 10 バイトの記憶内容
arr2 ストアのコンテンツの最初の 10 バイト
最初の 9 バイトの内容を比較しているため、それらはすべて同じであり、出力値は 0 になるはずです。 最初の 10 バイトを比較した場合、出力は -1 になるはずです。
上記はメモリ変更機能に関する私の理解のすべてです。コメント欄で貴重なアドバイスをいただければ幸いです。