|
Программирование >> Решение нетривиальных задач
Но теперь макрос все еще необъяснимо модифицирует str, а нормальная функция Си не может работать таким образом. (Функция Си++ может, но не должна. Я объясню почему в той главе книги, которая посвящена Си++). Для модификации строки str в функции вы должны передать в нее ее адрес, поэтому то же самое должно быть применимо к макросу. Вот третий (наконец-то правильный) вариант, в котором макрос end() попросту заменен функцией с таким же именем. В заголовочном файле: #define end(p) while(*(*p)) \ ++(*p) и в файле .c: char *f( char *str ) end(&str); ... return str; Вместо end(&str) будет подставлено: while(*(*&p)) ++(*&p) и *&p - это то же самое, что и p, так как знаки * и & отменяют друг друга - поэтому макрос в результате делает следующее: while(*(p)) ++(p) Вторая проблема с макросом в роли функции возникает, если вы желаете выполнить в макросе больше, чем одно действие. Рассмотрим такой макрос: #define two things() a();b() if( x ) two things(); else something else(); который будет расширен следующим образом (тут я переформатировал, чтобы сделать происходящее неприятно очевидным): if ( x ) a(); b(); else something else(); Вы получаете сообщение об ошибке у else отсутствует предшествующий оператор if . Вы не можете решить эту проблему, используя лишь фигурные скобки. Переопределение макроса следующим образом: #define two things() { a(); b(); } вызовет такое расширение: if( x ) a(); b(); else something else(); Эта вызывающая беспокойство точка с запятой - та, что следует после two things() в вызове макроса. Помните, что точка с запятой сама по себе является законным оператором в Си. Она ничего не делает, но она законна. Вследствие этого else пытается связаться с этой точкой с запятой, и вы получаете то же самое у else отсутствует предшествующий оператор if . Не нужно говорить, что, несмотря на то, что макрос выглядит подобно вызову функции, его вызов может не сопровождаться точкой с запятой. К счастью, для этой проблемы имеется два настоящих решения. Первое из них использует малоизвестный оператор последовательного вычисления (или запятую): #define two things() ( a(), b() ) Эта запятая - та, что разделяет подвыражения в инициализирующей или инкрементирующей частях оператора for. (Запятая, которая разделяет аргументы функции, не является оператором последовательного вычисления). Оператор последовательного вычисления выполняется слева направо и получает значение самого правого элемента в списке (в нашем случае значение, возвращаемое b() ). Запись: x = ( a(),b() ); означает просто: a(); x = b(); Если вам все равно, какое значение имеет макрос, то вы можете сделать нечто подобное, используя знак плюс вместо запятой. (Выражение: a()+b(); в отдельной строке совершенно законно для Си, где не требуется, чтобы результат сложения был где-нибудь сохранен). Тем не менее, при знаке плюс порядок выполнения не гарантируется; функция b() может быть вызвана первой. Не путайте приоритеты операций с порядком выполнения. Приоритет просто сообщает компилятору, где неявно размещаются круглые скобки. Порядок выполнения вступает в силу после того, как все круглые скобки будут расставлены по местам. Невозможно добавить дополнительные скобки к ((a()) + (b())). Операций последовательного вычисления гарантированно выполняется слева направо, поэтому в нем такой проблемы нет. Я должен также отметить, что операция последовательного вычисления слишком причудлива, чтобы появляться в нормальном коде. Я использую ее лишь в макросах, сопровождая все обширными комментариями, объясняющими, что происходит. Никогда не используйте запятую там, где должна быть точка к запятой. (Я видел людей, которые делали это, чтобы не использовать фигурные скобки, но это страшно даже пересказывать). Второе решение использует фигурные скобки, но с одной уловкой: #define two things() \ do \ a(); \ b(); \ } while( 0 ) if( x ) two things(); else something else(); что расширяется до: if( x ) do a(); b(); } while ( 0 ) ; <== точка с запятой связывается с оператором while ( 0 ) else something else(); Вы можете также попробовать так: #define two things() \ if( 1 ) \
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |