在调用toupper(),tolower()等之前,是否需要转换为unsigned char?

Bau*_*gen 30 c++ undefined-behavior language-lawyer toupper tolower

不久之前,StackOverflow上有一个声誉很高的人在评论中写道,在调用(和类似的函数)之前必须抛出一个char-argument .unsigned charstd::toupper

另一方面,Bjarne Stroustrup没有提到在C++ - Programming Language中这样做的必要性.他只是std::tolower喜欢用

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"
} 
Run Code Online (Sandbox Code Playgroud)

(引用自该书,第4版.)

参考文献说输入需要表示为toupper.对我来说,这听起来像每个unsigned char以后都有,char并且char具有相同的大小.

那么这个演员是不必要的还是Stroustrup不小心?

编辑:libstdc ++手册提到输入字符必须来自基本源字符集,但不会强制转换.我想这是由@Keith Thompson的回复所涵盖的,他们都有积极的表现unsigned charsigned char

Kei*_*son 30

是的,toupper需要转换的参数是unsigned char为了避免未定义行为的风险.

类型char,signed char和,unsigned char是三种不同的类型.char具有相同的范围,并表示为任一 signed char unsigned char.(普通char是非常常见的签名,能够表示-128 .. + 127范围内的值.)

toupper函数接受一个int参数并返回一个int结果.引用C标准,第7.4节第1段:

在所有情况下,参数都是a int,其值应表示为unsigned char或等于宏的值EOF.如果参数具有任何其他值,则行为未定义.

(C++包含了大部分C标准库,并将其定义推迟到C标准.)

[]上索引操作符std::string返回一个char值.如果plain char是带符号的类型,并且返回的值name[0]恰好是负数,那么表达式

toupper(name[0])
Run Code Online (Sandbox Code Playgroud)

有未定义的行为.

该语言保证,即使plain char是有符号的,基本字符集的所有成员都具有非负值,因此初始化

string name = "Niels Stroustrup";
Run Code Online (Sandbox Code Playgroud)

该程序不会冒未定义行为的风险.但是,通常是char传递给toupper(或者需要转换为<cctype>/中声明的任何函数的值),因此隐式转换不会产生负值并导致未定义的行为.<ctype.h>unsigned charint

这些<ctype.h>功能通常使用查找表来实现.就像是:

// assume plain char is signed
char c = -2;
c = toupper(c); // undefined behavior
Run Code Online (Sandbox Code Playgroud)

可以在该表的范围之外索引.

请注意,转换为unsigned:

char c = -2;
c = toupper((unsigned)c); // undefined behavior
Run Code Online (Sandbox Code Playgroud)

不能避免这个问题.如果int是32位,则将char值转换-2unsignedyield 4294967294.然后将其隐式转换为int(参数类型),这可能会产生-2.

toupper 可以实现,因此它对负值表现出明智的行为(接受来自CHAR_MINto的所有值UCHAR_MAX),但不需要这样做.此外,函数in <ctype.h>需要接受带有值的参数EOF,这通常是-1.

C++标准对某些C标准库函数进行了调整.例如,strchr其他几个函数被强制const正确性的重载版本所取代.对于声明的函数没有这样的调整<cctype>.

  • @JonathanMee:好问题.这是因为C++继承了C的大部分标准库,并将其定义推迟到C标准. (4认同)

Sne*_*tel 5

该引用指的是可表示为 an的值unsigned char,而不是指它an unsigned char。也就是说,如果实际值不在 0 和UCHAR_MAX (通常为 255)之间,则行为未定义。(或者EOF,这基本上就是它使用 anint而不是 a的原因char。)

  • 由于 `toupper` 的参数是一个 `int`,我认为负的 `char` 值可能会导致 UB。从 `int` 到 `unsigned char` 的任何转换都发生在函数内部。 (2认同)
  • @dyp“从`int` 到`unsigned char` 的任何转换都发生在函数内部。” --&gt; 不太可能将 `EOF` 转换为 255。_After_ 处理 `EOF`,转换为 `unsigned char` 是合理的,但未指定该行为。 (2认同)