2016-10-12 2 views
38

Функция memmove определяется следующим образом:Может memcpy или memmove возвращать другой указатель, чем dest?

void *memmove(void *dest, const void *src, size_t n); 

На странице Linux вручную, он говорит:

ВОЗВРАТ СТОИМОСТИ
функция memmove() возвращает указатель на Dest.

Почему функция не определена как void memmove(…), когда она всегда возвращает один из входных параметров? Может ли возвращаемое значение отличаться от dest?

Или это возвращаемое значение действительно всегда dest, и это просто сделано, чтобы составить функцию в некоторых творческих целях?

+5

Отлично, мне придется откладывать обед, чтобы увидеть ответ на этот вопрос ... :) Я подозреваю, что последнее ваше предложение - это случай. – gsamaras

+2

"... вернуть другой указатель, чем dest?" -> С UB все возможно, иначе нет. – chux

+2

Возможный дубликат: http://stackoverflow.com/questions/13720428/whats-the-use-of-memset-return-value –

ответ

42

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); 
+0

@usr, но это причина, по которой большая часть функции строки и памяти возвращает конечный указатель, или что вы можете сделать 'a = b = c = d'. Возвращение значения очень дешево, а BTW 'void' - это новое изобретение. У оригинального K & R C не было 'void'. –

+6

Не возвращай ничего. Это не относится к этой функции. Строковые функции возвращают указатель, потому что указатель вычисляется по методу O (N) (если я ошибаюсь в отношении того, что строковые функции не должны возвращать указатель). – usr

+0

@usr: Было бы разумно, чтобы функции, такие как strcat, возвращали указатель на конец адресата, но это не то, что они делают. Вместо этого они возвращают указатель на начало строки. – supercat

26

Согласно 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() в качестве аргумента другой функции, где скопированное значение (i.e, указатель на dest) будет полезным.

Например, вы можете написать короче один

puts(memmove(dest_buffer, src_buffer, welcome_message_size)); 

вместо уже один

memmove(dest_buffer, src_buffer, welcome_message_size); 
puts(dest_buffer); 
13

идиома возвращения точного значения одного из аргументов (типа указателя) существует для поддержки «цепных» вызовов функций (см. также 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. С компаундом lterals это очень идиома позволяет писать один и тот же код без введения имени промежуточного переменной

printf("%s\n", strcat(strcat(strcpy((char [1024]) { 0 }, "Hello"), " "), "World!")); 

some_function(memcpy(&(struct UserData) { 0 }, &original_data, sizeof original_data)); 

, которая имеет смысл, так как в большинстве случаев, названных переменным должен быть кратковременными, не нужна потом, и только загромождает пространство имен.

+1

Я не очень много делаю с C, но не 'sizeof data_copy' в вашем примере сложных литералов должен быть' sizeof original_data', так как 'data_copy' больше не существует? – Pokechu22

+0

@ Pokechu22: Да, вы абсолютно правы. – AnT

+1

Конечно, пример с strcat будет блестяще неэффективным, так как каждая операция strcat начнется путем сканирования адресата, чтобы найти нулевой байт, затем объединить новые данные и затем вернуть * начало * адресата, чтобы следующий вызов имел для сканирования старых и новых данных, чтобы найти новое местоположение нулевого байта. – supercat

Смежные вопросы