Программирование >>  Решение нетривиальных задач 

1 ... 30 31 32 [ 33 ] 34 35 36 ... 77


Но теперь макрос все еще необъяснимо модифицирует 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 ) \



1 ... 30 31 32 [ 33 ] 34 35 36 ... 77

© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки.
Яндекс.Метрика