Нужно ли приводить к unsigned char перед вызовом toupper (), tolower () и др.?

Некоторое время назад кто-то с высокой репутацией на StackOverflow написал в комментарии, что необходимо разыграть charАргумент к unsigned char перед звонком std::toupper (и аналогичные функции).

С другой стороны, Бьярн Страуструп не упоминает о необходимости делать это на языке программирования C ++.
Он просто использует toupper лайк

string name = "Niels Stroustrup";

void m3() {
string s = name.substr(6,10);  // s = "Stroustr up"name.replace(0,5,"nicholas");  // name becomes "nicholas Stroustrup"name[0] = toupper(name[0]);   // name becomes "Nicholas Stroustrup"}

(Цитируется из упомянутой книги, 4-е издание.)

Ссылка говорит, что вход должен быть представлен как unsigned char,
Для меня это звучит так, как будто char поскольку char а также unsigned char имеют одинаковый размер.

Так что этот актёрский состав ненужен или Страуструп был небрежным?

Изменить: руководство по libstdc ++ упоминает, что входной символ должен быть из основной исходный набор символов, но не бросает. Я предполагаю, что это покрыто ответом @Keith Thompson, все они имеют положительное представление как signed char а также unsigned char?

26

Решение

Да, аргумент toupper должен быть преобразован в unsigned char чтобы избежать риска неопределенного поведения.

Типы char, signed char, а также unsigned char три разных типа. char имеет тот же диапазон и представление, что и или signed char или же unsigned char, (Plain char очень часто подписывается и может представлять значения в диапазоне -128 .. + 127.)

toupper функция занимает int аргумент и возвращает int результат. Цитируя стандарт С, раздел 7.4, пункт 1:

Во всех случаях аргумент является int, значение которого должно быть
представима как unsigned char или должен быть равен значению
макрос EOF . Если аргумент имеет любое другое значение,
поведение не определено.

(C ++ включает в себя большую часть стандартной библиотеки C и переносит ее определение в стандарт C.)

[] оператор индексации на std::string возвращает char значение. Если обычный char является типом со знаком, и если значение возвращается name[0] бывает отрицательным, то выражение

toupper(name[0])

имеет неопределенное поведение.

Язык гарантирует, что, даже если простой char подписан, все члены базового набора символов имеют неотрицательные значения, поэтому с учетом инициализации

string name = "Niels Stroustrup";

программа не рискует неопределенным поведением. Но да, в общем char значение передано toupper (или к любой из функций, объявленных в <cctype> / <ctype.h> должен быть преобразован в unsigned char, так что неявное преобразование в int не приведет к отрицательному значению и приведет к неопределенному поведению.

<ctype.h> Функции обычно реализуются с использованием справочной таблицы. Что-то вроде:

// assume plain char is signed
char c = -2;
c = toupper(c); // undefined behavior

может индексировать за пределами этой таблицы.

Обратите внимание, что преобразование в unsigned:

char c = -2;
c = toupper((unsigned)c); // undefined behavior

не избегает проблемы. Если int 32 бита, преобразуя char значение -2 в unsigned доходность 4294967294, Затем это неявно преобразуется в int (тип параметра), который наверное доходность -2,

toupper Можно быть реализован таким образом, что он ведет себя разумно для отрицательных значений (принимая все значения из CHAR_MIN в UCHAR_MAX), но это не обязательно. Кроме того, функции в <ctype.h> должны принять аргумент со значением EOF, который обычно -1,

Стандарт C ++ вносит коррективы в некоторые функции стандартной библиотеки C. Например, strchr и некоторые другие функции заменены перегруженными версиями, которые обеспечивают const правильность. Для функций, объявленных в <cctype>,

26

Другие решения

В С, toupper (и многие другие функции) взять intдаже если вы ожидаете, что они примут chars. Дополнительно, char подписан на некоторых платформах и не подписан на других.

Совет бросить на unsigned char перед звонком toupper правильно для C. Я не думаю, что это необходимо в C ++, если вы передадите его int это в диапазоне Я не могу найти ничего конкретного о том, нужно ли это в C ++.

Если вы хотите обойти проблему, используйте toupper определяется в <locale>. Это шаблон, который принимает любой приемлемый тип символов. Вы также должны передать это std::locale, Если вы не знаете, какой язык выбрать, используйте std::locale(""), который должен быть предпочтительным языком пользователя:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <locale>
#include <string>

int main()
{
std::string name("Bjarne Stroustrup");
std::string uppercase;

std::locale loc("");

std::transform(name.begin(), name.end(), std::back_inserter(uppercase),
[&loc](char c) { return std::toupper(c, loc); });

std::cout << name << '\n' << uppercase << '\n';
return 0;
}
2

Ссылка относится к значению, являющемуся представима как unsigned charне к этому являющийся unsigned char, То есть поведение не определено, если фактическое значение не находится между 0 и UCHAR_MAX (обычно 255). (Или же EOFчто является в основном причиной, по которой int вместо char.)

2

К сожалению, Страуструп был неосторожен 🙁
И да, латинские буквенные коды должны быть неотрицательными (и приведение не требуется) …
Некоторые реализации корректно работают без приведения к unsigned char …
По определенному опыту, это может стоить нескольких часов, чтобы найти причину ошибки такого таппера (когда известно, что есть ошибка) …
И есть также isupper, islower и т. Д.

1

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

#include <string>
#include <algorithm>
#include <functional>
#include <locale>
#include <iostream>

int main()
{
typedef unsigned char BYTE; // just in case

std::string name("Daniel Brühl"); // used this name for its non-ascii character!

std::transform(name.begin(), name.end(), name.begin(),
(std::function<int(BYTE)>)::toupper);

std::cout << "uppercase name: " << name << '\n';
return 0;
}

Выход:

uppercase name: DANIEL BRüHL

Как и ожидалось, toupper не влияет на символы не-ascii. Но этот кастинг полезен для избежания неожиданного поведения.

0
По вопросам рекламы [email protected]