Может memcpy или memmove возвращать другой указатель, чем dest?
Функция memmove
определяется следующим образом:
void *memmove(void *dest, const void *src, size_t n);
На странице руководства Linux говорится:
ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
Функция memmove() возвращает указатель на dest.
Почему не функция, определенная как void memmove(…)
, когда она всегда возвращает один из входных параметров? Может ли возвращаемое значение отличаться от dest
?
Или это возвращаемое значение всегда всегда dest
, и только что сделано, чтобы иметь возможность составлять функцию в некоторых творческих способах?
Ответы
Ответ 1
memmove
никогда не вернет ничего, кроме dest
.
Возвращение dest
, в отличие от создания memmove
void, полезно, когда первый аргумент является вычисленным выражением, поскольку он позволяет избежать вычисления одного и того же значения вверх и хранения его в переменной. Это позволяет сделать в одной строке
void *dest = memmove(&buf[offset] + copiedSoFar, src + offset, sizeof(buf)-offset-copiedSoFar);
что вам в противном случае нужно было бы сделать на двух строках:
void *dest = &buf[offset] + copiedSoFar;
memmove(dest, src + offset, sizeof(buf)-offset-copiedSoFar);
Ответ 2
Согласно 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
.
Таким образом, функции всегда возвращают указатель на буфер назначения, который по дизайну.
Теперь, перейдя к части почему, многие функции разработаны таким образом, чтобы сделать цепочку вызовов функций возможной. Таким образом, вы можете вызвать вызов memmove()
в качестве аргумента другой функции, где скопированное значение (т.е. Указатель на dest
) будет полезным.
Например, вы можете написать более короткий
puts(memmove(dest_buffer, src_buffer, welcome_message_size));
вместо более длинного
memmove(dest_buffer, src_buffer, welcome_message_size);
puts(dest_buffer);
Ответ 3
Идиома возврата точного значения одного из аргументов (типа указателя) существует для поддержки "цепных" вызовов функций (также см. 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));
Даже если вам не нравится этот стиль организации кода и предпочитает делать то же самое с помощью нескольких операторов, накладные расходы на возврат значения [ненужного] указателя практически отсутствуют.
Можно даже сказать, что значение этой идиомы немного увеличилось после введения сложных C-образных литералов. С составными lterals эта самая идиома позволяет писать один и тот же код без введения именованной промежуточной переменной
printf("%s\n", strcat(strcat(strcpy((char [1024]) { 0 }, "Hello"), " "), "World!"));
some_function(memcpy(&(struct UserData) { 0 }, &original_data, sizeof original_data));
что имеет смысл, поскольку в большинстве случаев, когда именованная переменная должна быть недолговечной, впоследствии не нужна и только загромождает пространство имен.