Еще один запрос точки последовательности: как работает * p ++ = getchar ()?

§5.1.2.4.16

ПРИМЕР 7 Группировка выражения не полностью определяет его оценку. В следующем фрагменте:

#include <stdio.h>
int sum;
char *p;
/*
...
*/
sum = sum * 10 - '0' + (*p++ = getchar());

оператор выражения сгруппирован, как если бы он был записан как

sum = (((sum * 10) - '0') + ((*(p++)) = (getchar())));

но фактическое приращение p может происходить в любое время между предыдущей точкой последовательности и следующей точкой последовательности (;), а вызов getchar может происходить в любой момент до того, как потребуется его возвращаемое значение.

В основном я понимаю это как неопределенное поведение - либо *p = getchar(); p++; ИЛИ p++; *p = getchar(). Обратите внимание, что ; подразумевает точку последовательности, но во всем выражении нет других точек последовательности.

Так что этот синтаксис бесполезен. И, в значительной степени, ++ и - для присвоения указателя бесполезны. Верно?


person Vorac    schedule 29.04.2013    source источник
comment
Не имеет значения, когда происходит постинкремент p++, потому что p вычисляется только один раз в выражении.   -  person Paul R    schedule 29.04.2013
comment
в (*p++ = getchar()) p++ является лишь побочным эффектом, p никогда не упоминается во втором месте того же оператора, поэтому p++ не может вызывать ошибки точки последовательности. (кроме случаев, когда он указывает на sum или где-то внутри буфера ФАЙЛА stdin)   -  person wildplasser    schedule 29.04.2013
comment
изменить p* = getchar() на *p = getchar()   -  person Rüppell's Vulture    schedule 29.04.2013


Ответы (4)


либо *p = getchar(); p++; ИЛИ p++; p* = getchar() [sic]

Ну на самом деле нет. Это либо:

*p = getchar(); p++

or

old_p = p++; *old_p = getchar()

В вашей интерпретации вы можете написать в *p или *(p + 1), что неверно. В обеих интерпретациях вы пишете туда, куда изначально указывал p, независимо от того, когда компилятор решает поместить инструкцию, изменяющую p. Если он решит поместить его перед записью в *p, он должен будет убедиться, что он сохраняет старое значение для последующей записи в (*old_p).

Операторы суффиксов -- и ++ на самом деле являются довольно хорошими ярлыками. Возьмем этот пример:

size_t strlen(const char *str)
{
    size_t len = 0;
    while (str[len++]);
    return len - 1;
}

Это очень краткая реализация strlen с использованием суффикса ++.


Из C11, 6.5.2.4.2 (выделено мной):

Результатом постфиксного оператора ++ является значение операнда. В качестве побочного эффекта значение объекта операнда увеличивается (то есть к нему добавляется значение 1 соответствующего типа). См. Обсуждение аддитивных операторов и составного присваивания для получения информации об ограничениях, типах и преобразованиях, а также о влиянии операций на указатели. Вычисление значения результата выполняется последовательно перед побочным эффектом обновления сохраненного значения операнда. Что касается вызова функции с неопределенной последовательностью, операция postfix ++ представляет собой однократное вычисление. Postfix ++ для объекта с атомарным типом - это операция чтения-изменения-записи с семантикой порядка памяти memory_order_seq_cst.

person Shahbaz    schedule 29.04.2013
comment
Похоже, есть какое-то правило, согласно которому постфиксные операторы вступают в силу после завершения выражения. Это озадачивает. Стандарт говорит, что все побочные эффекты должны быть устранены до следующей точки последовательности. Но здесь, похоже, требуется, чтобы ++ применялся после некоторого события. Какое событие? - person Vorac; 29.04.2013
comment
Вы должны return len-1; Представить, что произошло бы в случае использования пустой строки "" в качестве str аргумента. - person wildplasser; 29.04.2013
comment
@Vorac: такого правила нет. Пожалуйста, поверьте Шахбазу, когда он говорит вам, что значение p++ - это старое значение p. Не имеет значения, когда p изменяется, потому что старое значение одинаково в любом случае, и поэтому *p++ относится к одному и тому же char независимо от того, когда p увеличивается. - person Steve Jessop; 29.04.2013
comment
звучит так, как будто есть какое-то правило, согласно которому постфиксные операторы вступают в силу после завершения выражения - нет, это не так. Почему бы не попробовать прочитать и понять полученные ответы? или, если на то пошло, прочитать стандарт C, который четко и ясно определяет значение p в *p++ как значение до увеличения, независимо от того, когда оно увеличивается. И не смейтесь над правильным комментарием Стива. - person Jim Balter; 29.04.2013
comment
@ Стив Джессоп, пожалуйста, верьте, Шахбаз, хахахаха :) Хороший! - person Vorac; 29.04.2013
comment
Ключевое слово здесь side effect. Пожалуйста, посмотрите мой комментарий к OP. - person wildplasser; 29.04.2013

Лексическая группировка значения не имеет. Важно то, что значение p++.

Это утверждение идентично следующим двум утверждениям:

sum = sum * 10 - '0' + (*p = getchar());
++p;
person Kerrek SB    schedule 29.04.2013
comment
Я до сих пор не понимаю, почему не происходит следующее: 1) оценивать p ++ 2) оценивать getchar () 3) присваивать возвращаемое значение теперь увеличенному указателю p. Когда происходит приращение постфикса? - person Vorac; 29.04.2013
comment
@Vorac Тебе только что сказали. Не имеет значения когда p ++ возникает ... это не влияет на значение p в *p ... это значение по определению является значением p до того, как произойдет приращение. - person Jim Balter; 29.04.2013
comment
@Vorac: оценка p++ дает то же значение, что и оценка p (только p++ является r-значением, а p - l-значением). - person Kerrek SB; 29.04.2013

sum = (((sum * 10) - '0') + ((*(p++)) = (getchar())));

эквивалентно

*p = getchar();
sum = ( ((sum * 10) - '0') + *p );
p++;

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

sum = (((sum * 10) - '0') + ((*(++p)) = (getchar())));

эквивалентно

p++;
*p = getchar();
sum = ( ((sum * 10) - '0') + *p );
person MOHAMED    schedule 29.04.2013
comment
Не могу сказать эквивалент, когда есть два getchar() звонка - person Kos; 29.04.2013
comment
мы не можем сказать, что это всегда правда. - person Arpit; 29.04.2013
comment
Что, черт возьми, с этими двумя комментариями и отрицательными голосами? Есть только один вызов getchar(), и мы можем сказать, что они всегда эквивалентны, потому что стандарт C говорит, что это так. - person Jim Balter; 29.04.2013

Использование - и ++ для присвоения указателей на самом деле бесполезно. Рассмотрим цикл, в котором вы хотите прочитать 10 символов и добавить в конец escape-символ:

int i, sum;
char * p;
p = (char*) malloc(11 * sizeof(char));

for (i = 0; i < 10; i++){
   sum = sum * 10 - '0' + (*p++ = getchar());
}
*p = '\0';
person Evans    schedule 29.04.2013
comment
Что за букле? Во всяком случае, это не касается вопроса ОП. - person Jim Balter; 29.04.2013
comment
@JimBalter bucle = loop (языковая проблема, извините). И в конце своего поста он сказал: Значит, этот синтаксис бесполезен. И, в значительной степени, ++ и - для присвоения указателя бесполезны. Верно? - person Evans; 29.04.2013
comment
Чтобы не потерять p для использования в будущем, вы можете добавить for(; sum; sum/=10) { p--;} после *p = '\0'; ;-) Что не поможет в случае (sum%10 == 0) ... - person wildplasser; 29.04.2013
comment
Это не касается причин, по которым OP думает, что это бесполезно ... вот в чем все дело. - person Jim Balter; 29.04.2013