c – 在调用toupper(),tolower()等之前,是否需要转换为unsigned char?
作者:互联网
不久之前,StackOverflow上有名望的人在评论中写道,在调用std :: toupper(和类似函数)之前,有必要将char-argument转换为unsigned char.
另一方面,Bjarne Stroustrup没有提到在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版.)
The reference表示输入需要表示为unsigned char.
对我来说,这听起来像每个char都有,因为char和unsigned char具有相同的大小.
那么这个演员是不必要的还是Stroustrup不小心?
编辑:libstdc++ manual提到输入字符必须来自basic source character set,但不会强制转换.我想这是@Keith Thompson的回复所涵盖的,他们都有正面的代表作为签名的char和unsigned char?
解决方法:
是的,toupper的参数需要转换为unsigned char,以避免未定义行为的风险.
char,signed char和unsigned char类型是三种不同的类型. char具有与signed char或unsigned char相同的范围和表示. (普通字符非常常见,能够表示-128 .. 127范围内的值.)
toupper函数接受一个int参数并返回一个int结果.引用C标准,第7.4节第1段:
In all cases the argument is an
int
, the value of which shall be
representable as anunsigned char
or shall equal the value of
the macroEOF
. If the argument has any other value, the
behavior is undefined.
(C包含大部分C标准库,并将其定义推迟到C标准.)
std :: string上的[]索引运算符返回一个char值.如果plain char是有符号类型,并且name [0]返回的值恰好是负数,那么表达式
toupper(name[0])
有未定义的行为.
该语言保证,即使普通字符被签名,基本字符集的所有成员都具有非负值,因此初始化
string name = "Niels Stroustrup";
该程序不会冒未定义行为的风险.但是,是的,通常传递给toupper的char值(或者在< cctype> /< ctype.h>中声明的任何函数)需要转换为unsigned char,因此隐式转换为int将不会产生负值并导致未定义的行为.
< ctype.h>函数通常使用查找表来实现.就像是:
// assume plain char is signed
char c = -2;
c = toupper(c); // undefined behavior
可以在该表的范围之外索引.
请注意,转换为无符号:
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>中声明的函数没有这样的调整.
标签:c-3,toupper,c,undefined-behavior,casting 来源: https://codeday.me/bug/20190911/1804468.html