Często oprócz dostarczania deklaracji funkcji, standardowe nagłówki C mogą zapewniać "makro maskowania", dzięki czemu rzeczy stają się szybsze. Na przykład, jeśli zawierają ctype.h
plik nagłówek uznaCzy istnieją jakieś ograniczenia standardu C, które umożliwiają implementację funkcji jako makr?
int isdigit(int c);
ale może również maskować deklarację z makro. Wierzę, że to jest przenośnym isdigit
makro zgodnie ze standardem C:
#define isdigit(c) ((c) >= '0' && (c) <= '9')
Oczywiście, to makro jest również niebezpieczne, ponieważ wprowadza niezdefiniowanej zachowanie jeśli to zrobisz, gdy makro jest zdefiniowany:
int c = 'A';
printf("%d\n", isdigit(c++));
Aby uniknąć UB w tym hipotetycznym przypadku, muszę otoczyć nazwę funkcji przez parens: (isdigit)(c++)
. Moje pytanie brzmi: czy są jakieś ograniczenia dotyczące tego, jakie rodzaje makr maskowania może zdefiniować standardowy nagłówek? Czy mają gwarancję, że nie spowodują niezdefiniowanych zachowań, jeśli wyrażenie argumentu wywoła efekty uboczne, czy też technicznie mogą mieć dziwne zachowanie, takie jak widzieliśmy powyżej? Gdzie są granice?
Zaproponowane makro nie jest zgodne. Z wyjątkami (opcjonalnych) implementacji makr funkcji 'getc()' i 'putc()', definicja makra funkcji standardowej może nie oceniać żadnego z jej argumentów więcej niż jeden raz. Jeśli zostanie wywołany jako 'isdigit (C++)' twoje makro zachowuje się nieprawidłowo, ale instrukcja działa poprawnie z zgodnymi makrami i funkcją. –
Przepraszam, co rozumiesz przez "standardowe makra"? W każdym razie, kiedy powiedziałem "przenośny", miałem na myśli "dobrze zachowany na wszystkich zgodnych ze standardem kompilatorach C", kiedy przekazano odpowiedni argument bez efektów ubocznych. –
Standard C stwierdza, że dowolna standardowa funkcja może być również zaimplementowana jako makro. Ale wymaga również, aby dowolna implementacja makr dowolnej standardowej funkcji C (z wyjątkiem 'getc()' i 'putc()') musiała tylko raz ocenić jej argumenty, ponieważ makro musi zachowywać się dokładnie tak, jakby było funkcją. Ponadto, każda funkcja musi być zarówno zadeklarowana, jak i zaimplementowana jako funkcja, tak aby jej adres mógł być przyjmowany i przekazywany jako wskaźnik do funkcji. To właśnie informacje, które Paul Griffiths zacytował u ciebie ze standardu. Zauważ, że 'setjmp()' jest jawnie makrem. –