Как можно передать выражение с побочными эффектами в getc?

"Расширенное программирование в среде UNIX, 3-е издание", стр. 151:

Разница между getc и fgetc заключается в том, что getc можно реализовать как макрос, тогда как fgetc нельзя реализовать как макрос. Это означает три вещи:

  • Аргумент getc не должен быть выражением с побочными эффектами.
  • Поскольку fgetc гарантированно является функцией, мы можем взять ее адрес. Это позволяет нам передать адрес fgetc в качестве аргумента другой функции.
  • Вызовы fgetc, вероятно, занимают больше времени, чем вызовы getc, так как обычно требуется больше времени для вызова функции.

Какое "выражение с побочными эффектами" может возникнуть для сигнатур функций с указателем потока в качестве параметра?

#include<stdio.h>
int getc(FILE* stream);
int fgetc(FILE* stream);

person Andrey Bushman    schedule 17.12.2013    source источник
comment
getc(foo()) где FILE* foo() {/* side effects*/}. getc(strptr++) где FILE* strptr. getc(*pstrptr++) где FILE** pstrptr. и т.д. Очень легко придумать примеры.   -  person Jim Balter    schedule 17.12.2013
comment
Спасибо, BLUEPIXY, Джим Балтер.   -  person Andrey Bushman    schedule 17.12.2013


Ответы (2)


Вероятно, существуют сотни способов передать выражение с побочными эффектами, но «надежным» будет что-то вроде:

FILE *files[NUM_FILES];
...
int rc = getc(files[counter++]);

Если макрос getc плохо реализован, выражение files[counter++] может быть вычислено более одного раза, что приведет к непредвиденному поведению.

person Mat    schedule 17.12.2013
comment
плохо? Сомневаюсь, что какая-нибудь вменяемая библиотека C? Вы понятия не имеете, о чем говорите. Первоначальная библиотека stdio и многие ее потомки реализовали getc(f) как что-то вроде --(f)->ptr < (f)->end? *(f)->ptr++ : __getc(f) для скорости, поэтому getc разрешено быть макросом, который переоценивает свой аргумент. - person Jim Balter; 17.12.2013
comment
-1 re @JimBalter примечание о том, что если getc плохо реализован как макрос, файлы выражений [counter++] могут быть оценены более одного раза, что приведет к неожиданному поведению. Я сомневаюсь, что какая-либо здравомыслящая библиотека C допустила бы такую ​​​​ошибку. Это опасная дезинформация для распространения. - person Cheers and hth. - Alf; 17.12.2013
comment
Сделай это --(f)->cnt > 0? *(f)->ptr++ : __getc(f) - person Jim Balter; 18.12.2013

Например, не пишите

FILE* foo() { puts( "Bah!\n" ); return stdout; }

void advance() { getc( foo() ); }
person Cheers and hth. - Alf    schedule 17.12.2013
comment
@Bush: отсутствие гарантии = возможность неисправности, а не гарантированная неисправность - person Cheers and hth. - Alf; 17.12.2013
comment
@Bush Насколько актуален твой ответ? Вы спросили, как выражение с побочными эффектами может быть передано в getc, и это явно пример. В приведенном вами тексте говорится, что getc можно реализовать как макрос. Я понимаю, что английский не является вашим родным языком, но вы наверняка понимаете, что означает can? Если вы позвонили заранее и не получили более одного Bah! напечатано, то используемая вами версия getc не является макросом. (Вы можете проверить, просмотрев stdio.h или макрос, расширяющий ваш исходный код, например, с помощью gcc -E) - person Jim Balter; 18.12.2013
comment
Да, Ба! был напечатан один раз для моего gcc (версия 4.8.1). Спасибо. - person Andrey Bushman; 19.12.2013