#c
#c
Вопрос:
В языке программирования C от K amp; R я продолжаю видеть коды, подобные
while(*s = *t ) ;
это всего лишь один пример из главы 5 «Указатели». Это полностью сбивает меня с толку, потому что я думал, что цикл while был следующим:
while (something is true)
do what your assigned to do
Это какой-то особый случай с C или я что-то пропустил? Будучи начинающим программистом на C, это просто не укладывалось у меня в голове. Любое объяснение того, как while(*s = *t ) ;
работает, было бы отличным.
Меня не смущает, что указывает выражение (*s = *t )
. Мне просто интересно, почему в теле цикла while ничего нет.
Комментарии:
1. В каком контексте находится код? Это пример того, как выполнять итерацию по строке? Если это из учебника, то, может быть, они учат вас, как, прежде чем что-то с этим делать?
2.
I'm just wondering why there is nothing in the body of the while loop.
Потому что этот код предназначен только для компактного копирования строки в другую в несколько строк.3. Никакой другой работы не требуется. Условие тоже является оператором, оно может иметь побочные эффекты. Единственное, что требуется, это то, что он должен вычисляться до
bool
.4. Вам не следует читать K amp; R, если вы не разбираетесь в археологии программного обеспечения. Он соответствует устаревшей версии стандарта C, который с тех пор пересматривался в 1995, 1999 и 2011 годах. Помимо этого, это просто страница за страницей проповеди опасной практики, усеянной множеством ошибок .
5. Я не думаю, что вы найдете лучшую книгу для изучения C, чем K amp; R. C — старый язык, и неплохо бы немного изучить историю. Кроме того, вы видите код, подобный приведенному в вопросе, в каждом проекте. Вы не можете сказать, что знаете C, если вам не известны «плохие практики».
Ответ №1:
Вы можете представить себе код примерно так, если разбить его на простейшие команды:
while (1) {
char c = *t;
*s = c;
s ;
t ;
if (!c) {
break;
}
}
Чтобы объяснить, почему это работает, вы присваиваете *t
значение *s
, а затем оцениваете результат ( a=b
вычисляется до присваиваемого значения). Если оно ненулевое, оно продолжается. Если оно равно нулю, цикл завершается.
Комментарии:
1. может быть, изменить
!c
наc ==
? Я думаю, что часть путаницы заключается в том, как цикл выходит из2. Я хотел сохранить там исходное выражение, но да, это сделало бы исходный код более понятным.
3. может быть подкачка
t
иs
, т. е. копии изt
вs
?4. Верно, я просто предположил, что s = source, t = target и написал код, не перепроверяя оригинал.
Ответ №2:
s
и t
наиболее вероятны char *
, что означает, что они завершаются нулевым значением. Поэтому, как только вы выполните итерацию по ним, они вернут 0. 0 равно false в C
Комментарии:
1. Скорее всего, это не так
==
.2. Вы имеете в виду, что это должно быть
==
вместо=
? Это неверно, предполагается, что это присваивание.3. да, изменил это, не увидел
;
в конце
Ответ №3:
Здесь происходит несколько вещей:
- Оператор присваивания
x = y
на самом деле является выражением (равным только что присвоенному значению), которое само по себе может использоваться как часть более сложного выражения. Это полезно для множественных назначений (x = y = z
) и условий цикла, подобныхwhile ((c = getchar()) != EOF)
. - В отличие, скажем, от Pascal или Java, C не является строгим в отношении логических значений. Любое ненулевое значение является истинным (т. Е. вызывает выполнение тела инструкции
if
,while
for
или0
), а нуль (будь то0.0
,''
NULL
или, ,,) является ложным. - Строки представляются в виде массива символов (или указателя на такой массив), заканчивающегося символом NUL (ноль). Итак, если
p
это указатель на строку,*p == 0
это означает, что он находится в конце строки. - Постфиксный
while
Цикл может иметь пустое тело.
Если собрать все это вместе, while(*s = *t );
это допустимый оператор C.
Перемещение приращений в тело цикла дает эквивалентный оператор:
while (*s = *t)
{
s ;
t ;
}
И перемещение присваивания в тело цикла дает:
while (1)
{
if (!(*s = *t))
{
break;
}
s ;
t ;
}
Или, что эквивалентно,
while (1)
{
// Copy one char from string t to string s.
*s = *t;
// If it was the string terminator, exit the loop.
if (*s == '')
{
break;
}
// Move both pointers to the next character.
s ;
t ;
}
Исходное выражение while(*s = *t );
является просто дополненной кодом версией приведенного выше.
Комментарии:
1. Превосходное пошаговое объяснение!!
Ответ №4:
В C каждая строка завершается ''
символом, код ASCII которого является 0
.
Следовательно, когда вся строка в t
копируется в s
. Цикл завершится, потому что последний скопированный символ был, 0
который false
находится в C.
ВНИМАНИЕ: при выполнении таких действий следует позаботиться о точках последовательности.
Ответ №5:
Выражение (*s = *t )
читает
- прочитайте символ, на который указывает
t
- сохраните этот символ в месте, указанном
s
- продвинуть
t
указатель на 1 позицию - продвинуть
s
указатель на 1 позицию - возвращает значение (символ), скопированное
и while();
инструкция является
- пока значение не равно нулю (т. Е. мы еще не скопировали нулевой символ, завершающий строку), ничего не делайте
где ‘ничего не делать’ — это пустая инструкция, представленная единственной точкой с запятой в конце.
Ответ №6:
Другие уже ответили на этот вопрос. Хотя *s = *t
, к сожалению, это довольно часто встречающаяся строка в C, я просто хочу указать на следующее:
-
Использование вместе с другими операторами в одном и том же выражении является опасной практикой. Всегда есть вероятность получить код, который зависит от порядка вычисления, что является неопределенным поведением. И есть также вероятность вызова неопределенного поведения в случае, если вы намеренно или случайно дважды измените одну и ту же переменную без промежуточных точек последовательности.
Кроме того, как мы можем видеть из этого примера, объединение с другими операторами делает код излишне трудным для чтения и заставляет программиста и читателя быть на 100% уверенными во всех правилах приоритета операторов в C.
Оптимальный код написан таким образом, что каждое выражение имеет только один побочный эффект. Следовательно, оптимальный код не смешивает с другими операндами в том же выражении. (Смотрите правила 12.2 12.4 12.13 MISRA-C: 2004, для некоторых примеров того, где может вызывать ошибки.)
-
Использование присваивания внутри выражений управляющих операторов рассматривается как плохая практика. Это затрудняет чтение кода и может привести к путанице = с == . Хорошие компиляторы предостерегают от использования управляющих операторов = inside . (См. MISRA-C:2004 13.1)
Итак, что вам следует сделать, так это переписать такой опасный, нечитаемый код во что-то более понятное и безопасное. Например:
*s = *t
while(*t != '')
{
s ,
t ;
*s = *t;
}
Комментарии:
1. не будет ли цикл do ..while лучше в вашем примере?
2. @mch Да, do-while так же хорош. Лично у меня нет проблем с do-while, но по какой-то причине есть много людей, которым это трудно читать. Поэтому я специально создал цикл while.
Ответ №7:
Из стандартов C99
An assignment expression has the value of the left operand after the assignment
Когда значение, указанное t
, равно NULL
. он будет назначен в ячейку памяти, указанную s
. И общее значение этого выражения становится NULL
. Это приведет к разрыву цикла while.
Комментарии:
1. Переменная,
t
вероятно, никогда не получаетNULL
значения, и код никогда не проверяет его. Кроме того, даже еслиt
равно NULL, оно НЕ будет присвоеноs
.2.
Variable t probably never gets NULL value, and the code never tests it.
Согласен с этим. Это условие должно быть проверено.even if t is NULL it will NOT be assigned to s
. Не согласен. Он всегда будет присваиваться s (предполагается, что s и n являются указателями).3. Нет. Указанные символы копируются, указатели — нет. Они просто увеличиваются «параллельно», но никогда одно не копируется в другое.
4. Да, верно, тогда никогда не копируется в другой. Потому что мы откладываем указатель, используя
*
. Обновите ответ, чтобы он был более понятным. Спасибо!