2014-01-14 1 views
2

При переопределении некоторого Libc функции (в «личном» библиотеке проекте моей школы под названием libft), я получил представление о реализации некоторых как макросы, например, так:Как можно (макросы) libc макросов (GNU/BSD) быть безопасными?

#define ft_isalnum(c) (ft_isalpha(c) || ft_isdigit(c)) 
#define ft_isalpha(c) (ft_isupper(c) || ft_islower(c)) 
#define ft_isascii(c) (((c) >= 0) && ((c) <= 0177)) 
#define ft_isdigit(c) (((c) >= '0') && ((c) <= '9')) 
#define ft_islower(c) (((c) >= 'a') && ((c) <= 'z')) 
#define ft_isprint(c) (((c) >= 0040) && ((c) <= 0176)) 
#define ft_isspace(c) ((((c) >= 0x09) && ((c) <= 0x0d)) || ((c) == 0x20)) 
#define ft_isupper(c) (((c) >= 'A') && ((c) <= 'Z')) 

Однако вскоре я узнал, что инструкции например, ft_isspace(s[--len]) нарушается, потому что переменная len уменьшается в три раза. Поэтому я должен был сделать фактические функции вместо макросов.

Я знаю, что макросы небезопасны. Но я вижу, что реализация libc GNU/BSD в тестах символов is*(3) - это макросы. Как сделать их макросы безопасными?

Я не допускается (в школе), чтобы использовать функции я не реализованы себя (кроме malloc(3), free(3), и несколько системных вызовов, таких как write(2). И я полагаю, что вызов функции только для проверки ASCII-символ а неэффективный.

Спасибо.

+1

«Мне не разрешено (в школе) использовать функции, которые я сам не реализовал». - Похоже, было бы очень сложно написать любой рабочий код, следуя этому правилу. –

+0

Макросы BSD также не являются «безопасными». Макросов препроцессора нет. –

+1

«Мне не разрешено (в школе) использовать функции, которые я сам не реализовал». - Странно, если школа действительно научит синдрома Inv Invented Here Syndrome. –

ответ

1

в данном случае для функций в ctype.h как Linux и BSDs искать значение в массиве, который содержит битовую маску для всех действительных значений функции могут быть вызваны с - [0,255]. Каждая из функций просто индексирует массив своим аргументом и проверяет, является ли конкретный бит, который вы t esting for установлен. Вот как они только оценивают аргумент один раз.

Это может выглядеть примерно так:

#define C_ALPHA 0x01 
#define C_NUM 0x02 
... 
int ctype_array[256] = { ..., /* offset for 'A' */C_ALPHA|C_PRINT|C_UPPER, ...}; 
#define isalnum(c) (ctype_array[(c)] & (C_ALPHA|C_NUM)) 

В более общем случае для всех макросов трюк должен сделать:

#define foo(x) do { int _x = (x); do_something_with(_x, twice(_x)); } while (0) 

Конечно эта конструкция не может ничего возвращать (Конструкция do-while такова, что это один оператор, который не разбивается, когда он помещается как один оператор в оператор if/for/while без скобок). Существуют расширения компилятора, которые позволяют реализовать макросы, подобные этому, и вернуть значение.

В общем, я бы рекомендовал использовать встроенные функции или просто использовать обычные функции. Накладные расходы функций не так высоки для современных процессоров, и в больших программах вы даже можете получить существенные ускорения на макроуровне, потратив меньше кеша на код. Сначала получите свой код правильно, а затем подумайте о том, чтобы ускорить его, измеряя, если ваши потоки фактически вообще что-то делают.

+0

Уход! Что означает функция 'дважды (_x)', которая должна выполняться? – Diti

+0

@ Diti Это был просто умный способ написать «сделать что-то с x дважды». – Art