Почему `strchr`, кажется, работает с многобайтовыми символами, несмотря на отказ от руководства на странице?
От:
man strchr
char * strchr (const char * s, int c);
Функция strchr() возвращает указатель на первое вхождение символа c в строке s.
Здесь "символ" означает "байт"; эти функции не работают с широкими или многобайтовыми символами.
Тем не менее, если я попытаюсь найти многобайтовый символ, как é
(0xC3A9
в UTF-8):
const char str[] = "This string contains é which is a multi-byte character";
char * pos = strchr(str, (int)'é');
printf("%s\n", pos);
printf("0x%X 0x%X\n", pos[-1], pos[0]);
Я получаю следующий вывод:
который является многобайтовым символом
0xFFFFFFC3 0xFFFFFFA9
Несмотря на предупреждение:
предупреждение: многосимвольная символьная константа [-Wmultichar]
Итак, вот мои вопросы:
- Что это значит
strchr
не работает с многобайтовыми символами? (похоже на работу, при условииint
тип достаточно большой, чтобы содержать ваш многобайтовый файл, который может быть максимум 4 байта) - Как избавиться от предупреждения, т.е. как безопасно восстановить многобайтовое значение и сохранить его в int?
- Почему префиксы
0xFFFFFF
?
2 ответа
strchr()
только кажется, что работает для вашего многобайтового символа.
Фактическая строка в памяти
... c, o, n, t, a, i, n, s, ' ', 0xC3, 0xA9, ' ', w ...
Когда вы звоните strchr()
вы действительно только ищете 0xA9
, которые являются младшими 8 битами. Вот почему pos[-1]
имеет первый байт вашего многобайтового символа: он был проигнорирован во время поиска.
char
подписан в вашей системе, поэтому ваши персонажи имеют расширенный знак (0xFFFFFF
) когда вы их распечатываете.
Что касается предупреждения, кажется, что компилятор пытается сказать вам, что вы делаете что-то странное, что вы есть. Не игнорируйте это.
Это проблема. Вроде работает. Во-первых, все зависит от того, что компилятор поместит в строку, если вы поместите в нее многобайтовые символы, если он вообще ее скомпилирует. Очевидно, что вам повезло (для некоторой подходящей интерпретации повезло) в том, что он заполнил вашу строку
.... c3, a9, ' ', 'w', etc
и что вы ищете c3a9
Как это можно найти довольно легко. Страница руководства на strchr гласит:
Функция strchr() возвращает указатель на первое вхождение c (преобразованное в символ) в строке s
Таким образом, вы передаете C3A9 к этому, который преобразуется в char
со значением "а9". Находит a9
символ, и вы получите указатель на него.
ffffff
Префикс заключается в том, что вы выводите подписанный символ в виде 32-битного шестнадцатеричного числа, поэтому его расширение расширяет его для вас. Это как и ожидалось.
Проблема в том, что "неопределенное поведение" - это как раз то, что нужно. Это может работать почти правильно. А может и нет, в зависимости от обстоятельств.
И снова это почти. Вы не получаете указатель на многобайтовый символ, вы получаете указатель на его середину (и я удивлен, что вы воспринимаете это как работу). Если бы многобайтовый символ был равен 0xff20, вы бы указали куда-то намного раньше в строке.