関数memmove
は次のように定義されています:
_void *memmove(void *dest, const void *src, size_t n);
_
Linuxのマニュアルページには、次のように書かれています。
戻り値
memmove()関数は、destへのポインターを返します。
関数が常に入力パラメーターの1つを返すのに、関数がvoid memmove(…)
として定義されていないのはなぜですか?戻り値はdest
と異なる場合がありますか?
または、戻り値は本当に常にdest
であり、関数をいくつかの独創的な方法で構成できるように行われているだけですか?
memmove
がdest
以外のものを返すことはありません。
dest
をvoidにするのではなく、memmove
を返すことは、最初の引数が計算式である場合に役立ちます。これにより、同じ値を事前に計算して変数に格納する必要がなくなります。これにより、1行で実行できます
void *dest = memmove(&buf[offset] + copiedSoFar, src + offset, sizeof(buf)-offset-copiedSoFar);
それ以外の場合は2行で行う必要があること:
void *dest = &buf[offset] + copiedSoFar;
memmove(dest, src + offset, sizeof(buf)-offset-copiedSoFar);
_C11
_に従い、§7.24.2.1および§7.24.2.2の章
void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
[...]
memcpy
関数は、_s1
_の値を返します。
そして、
void *memmove(void *s1, const void *s2, size_t n);
[...]
memmove
関数は、_s1
_の値を返します。
したがって、関数は常に宛先バッファへのポインタを返します。これは仕様によるものです。
whyの部分に進むと、多くの関数はこのように設計されており、関数呼び出しの連鎖を可能にします。こうすることで、別の関数への引数としてmemmove()
を呼び出すことができ、コピーされた値(ie、つまりdest
)は、ある程度役立つでしょう。
たとえば、短い方を書くことができます
_ puts(memmove(dest_buffer, src_buffer, welcome_message_size));
_
長い方の代わりに
_ memmove(dest_buffer, src_buffer, welcome_message_size);
puts(dest_buffer);
_
(ポインタ型の)引数の1つの正確な値を返すイディオムは、「連鎖」関数呼び出しをサポートするために存在します(strcpy
、strcat
なども参照)。反復的なコードを複数のステートメントに分割する代わりに、単一の式ステートメントとして記述することができます。例えば
char buffer[1024];
printf("%s\n", strcat(strcat(strcpy(buffer, "Hello"), " "), "World"));
struct UserData data_copy;
some_function(memcpy(&data_copy, &original_data, sizeof original_data));
このコードの編成スタイルが気に入らず、複数のステートメントを使用して同じことを行う場合でも、[不要な]ポインター値を返すオーバーヘッドは事実上存在しません。
C99で複合リテラルが導入された後、このイディオムの値が少し増加したと言うこともできます。複合フィルターを使用すると、この非常に慣用的な表現により、名前付き中間変数を導入せずに同じコードを記述できます。
printf("%s\n", strcat(strcat(strcpy((char [1024]) { 0 }, "Hello"), " "), "World!"));
some_function(memcpy(&(struct UserData) { 0 }, &original_data, sizeof original_data));
これは、ほとんどの場合、その名前付き変数は存続期間が短いはずであり、後で必要になることはなく、名前空間を乱雑にするだけなので意味があります。