Почему `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, вы бы указали куда-то намного раньше в строке.

Другие вопросы по тегам