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 an unsigned char or shall equal the value of
the macro EOF . 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 – 在调用toupper(),tolower()等之前,是否需要转换为unsigned char? - 代码日志