Я хочу знать, как макрос isupper определен в C / C ++. Не могли бы вы предоставить мне то же самое или указать мне доступные ресурсы. Я попытался посмотреть ctype.h, но не понял.

5
josh 4 Авг 2010 в 10:11

4 ответа

Лучший ответ

Его реализация определена - каждый поставщик может и обычно делает это по-своему.

Чаще всего используется таблица «признаков» - массив с одним элементом для каждого символа, значение которого представляет собой набор флагов, указывающих подробности о персонаже. Примером может быть:

 traits[(int) 'C'] = ALPHA | UPPER | PRINTABLE;

В этом случае isupper () будет выглядеть примерно так:

 #define isupper(c) ((traits[(int)(c)] & UPPER) == UPPER)
13
James Curran 4 Авг 2010 в 10:17

Это зависит от реализации. Один из очевидных способов реализовать это:

extern char *__isupper;
#define isupper(x) ((int)__isupper[(x)])

Где __isupper указывает на массив из 0 и 1, определяемый локалью. Однако этот вид техники потерял популярность, поскольку доступ к глобальным переменным в разделяемых библиотеках довольно неэффективен и создает постоянные требования к ABI, а также поскольку он несовместим с локальными локальными стандартами потока POSIX.

Другой очевидный способ реализовать его в реализациях только для ASCII или UTF-8:

#define isupper(x) ((unsigned)(x)-'A'<='Z'-'A')
5
R.. GitHub STOP HELPING ICE 4 Авг 2010 в 10:19
Очень хорошо, я никогда не думал об этом (опять же, я никогда не пробовал :))
 – 
Matt Joiner
4 Авг 2010 в 10:23
Между прочим, все реализации должны #define isdigit(x) ((unsigned)(x)-'0'<10), потому что ISO C требует, чтобы поведение было идентично этому выражению и было оптимальным.
 – 
R.. GitHub STOP HELPING ICE
4 Авг 2010 в 10:26
Я не так уверен в UTF8, как вы, кажется. Наверняка все эти другие языки за пределами диапазона ASCII также имеют верхний регистр? И, если вы оставите его там, вам, вероятно, следует сказать «Unicode». UTF8 - это кодировка, а не набор символов.
 – 
paxdiablo
4 Авг 2010 в 10:26
@paxdiablo, это isupper не iswupper. В UTF-8 все байты вне диапазона ASCII не имеют значения сами по себе, только как часть многобайтовых последовательностей, поэтому неширокие функции is* всегда возвращают 0 для байтов, отличных от ASCII.
 – 
R.. GitHub STOP HELPING ICE
4 Авг 2010 в 10:29
1
@R, я думаю, вы путаете здесь термин байт. Байт - это символ. В ISO C нет никаких многобайтовых символов. Если базовый набор символов - Unicode (независимо от кодировки), isupper и его собратья должны обрабатывать и другие языки - это зависит от локали.
 – 
paxdiablo
4 Авг 2010 в 10:33

Это функция, а не макрос. Определение функции isupper() различается в зависимости от таких вещей, как локаль и текущий набор символов - поэтому существует функция специально для этой цели.

Для ASCII, из-за способа присвоения букв, на самом деле довольно легко проверить это. Если код ASCII символа находится между 0x41 и 0x5A включительно, то это заглавная буква.

4
In silico 4 Авг 2010 в 10:22

На самом деле это довольно сложно, например, в GCC. Но простая реализация isupper может быть (хотя и имеет ошибку двойной оценки) проще всего определить как:

# определить isupper (c) (c> = 'A') & (c <= 'Z')

http://ideone.com/GlN05

GCC специально проверяет, что бит 0 равен 1 в символе для текущей локали:

(* __ ctype_b_loc ()) [(int) (c)] & (unsigned short int) (1 << (0))

Где __ctype_b_loc () - это функция, которая возвращает указатель на массив символов в текущей локали, который содержит характеристики для каждого символа в текущем наборе символов.

1
Scott S. McCoy 7 Авг 2010 в 06:26
Этот макрос не работает, потому что в нем отсутствуют круглые скобки вокруг аргумента, и он вычисляет свой аргумент дважды (подумайте о isupper(*s++) ...). Вам нужно привести к unsigned и использовать семантику беззнакового переполнения, чтобы проверить диапазон, не оценивая аргумент более одного раза.
 – 
R.. GitHub STOP HELPING ICE
4 Авг 2010 в 10:47
Честно говоря, я назвал ошибку двойной оценки. :-)
 – 
Scott S. McCoy
7 Авг 2010 в 06:26