Bau*_*gen 30 c++ undefined-behavior language-lawyer toupper tolower
不久之前,StackOverflow上有一个声誉很高的人在评论中写道,在调用(和类似的函数)之前必须抛出一个char
-argument .unsigned char
std::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 char
和signed 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 char
int
这些<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
值转换-2
为unsigned
yield 4294967294
.然后将其隐式转换为int
(参数类型),这可能会产生-2
.
toupper
可以实现,因此它对负值表现出明智的行为(接受来自CHAR_MIN
to的所有值UCHAR_MAX
),但不需要这样做.此外,函数in <ctype.h>
需要接受带有值的参数EOF
,这通常是-1
.
C++标准对某些C标准库函数进行了调整.例如,strchr
其他几个函数被强制const
正确性的重载版本所取代.对于声明的函数没有这样的调整<cctype>
.
该引用指的是可表示为 an的值unsigned char
,而不是指它是an unsigned char
。也就是说,如果实际值不在 0 和UCHAR_MAX
(通常为 255)之间,则行为未定义。(或者EOF
,这基本上就是它使用 anint
而不是 a的原因char
。)