Что делает while (* p2 ++ = * p1 ++); имею в виду?
У меня есть блок кода:
int main ()
{
char *p1 = "Hello";
char *p2;
p2 = (char*)malloc (20);
memset (p2, 0, 20);
while (*p2++ = *p1++);
printf ("%s\n", p2);
}
Но я не могу объяснить работу линии
while (* p2 ++ = * p1 ++);
Не могли бы вы сообщить мне порядок работы в этой формуле?
Ответы
Ответ 1
Этот классический C-код пытается выглядеть чрезвычайно умным, помещая все в одну строку.
while (*p2++ = *p1++);
эквивалентно
strcpy(p2, p1);
p1 += strlen(p1) + 1;
p2 += strlen(p2) + 1;
Другими словами, он копирует строку с завершающим нулем, при этом p1
заканчивается, указывая на конец исходной строки, и p2
указывает на конец целевой строки.
Ответ 2
Это строковая копия, но вы теряете исходное значение указателя. Вы должны сохранить исходное значение указателя.
int main ()
{
char *p1 = "Hello";
char *p2 = malloc(20);
char *p3 = p2;
memset (p2, 0, 20);
while (*p2++ = *p1++);
printf ("%s\n", p3);
}
Фактическое семантическое объяснение цикла while будет примерно таким:
for (;;) {
char *q2 = p2; // original p2 in q2
char *q1 = p1; // original p1 in q1
char c = *q1; // original *p1 in c
p2 += 1; // complete post increment of p2
p1 += 1; // complete post increment of p1
*q2 = c; // copy character *q1 into *q2
if (c) continue; // continue if c is not 0
break; // otherwise loop ends
}
Порядок сохранения q1
и q2
, а порядок, который p2
и p1
увеличиваются, могут быть изменены. Сохранение *q1
до c
может произойти в любое время после сохранения q1
. Назначение c
- *q2
может возникать в любое время после сохранения c
. На обратной стороне моего конверта это работает по меньшей мере на 40 различных интерпретаций.
Ответ 3
Цикл while
оценивает выражение: *p2++ = *p1++
. Выражение цикла while
:
*p2 = *p1
оценивается с использованием результата *p1
. Однако это значение по-прежнему присваивается *p2
, даже если выражение оценивается как false
или (0)
. Переписывая это:
char c;
do
{
c = *p1; /* read the src byte */
*p2 = c; /* write to dst byte */
p2++, p1++; /* increment src, dst pointers */
}
while (c != 0);
Вы заметите, что чтение/запись произойдет хотя бы один раз. Это нормально, если строка C p1
имеет nul-terminated, а p2
имеет достаточно памяти для строки C. То есть malloc
должен выделять не менее strlen(p1) + 1
байтов. В этом коде это верно.
Как отмечали другие, окончательная итерация будет оставить p1
по адресу one-past-the-end, который по-прежнему является действительным указателем, но имеет undefined результат при разыменовании. Адрес p2
является действительным указателем и действительным разыменованием, поскольку вы выделяете 20 байтов. Однако p2
больше не указывает на копию строки C. То, что вы хотите, эквивалентно:
char *p1 = "Hello";
char *p2, *tmp;
p2 = (char*)malloc (20);
memset (p2, 0, 20);
tmp = p2;
while (*tmp++ = *p1++);
printf ("%s\n", p2);
Большинство операционных систем освободят память на p2
при выходе из main
, но рекомендуется отключить ресурсы с соответствующим вызовом:
free(p2);
в конце. В то время как в отношении хорошей практики вы также должны проверить возвращаемое значение malloc
, чтобы гарантировать, что распределение выполнено успешно.