Rol*_*lig 7 c c++ character undefined-behavior language-lawyer
C 编程语言表示,这些函数<ctype.h>遵循一个共同的要求:
ISO C99,7.4p1:
在所有情况下,参数都是 an
int,其值应表示为 anunsigned char或应等于宏的值EOF。如果参数有任何其他值,则行为未定义。
这意味着以下代码是不安全的:
int upper(const char *s, size_t index) {
return toupper(s[index]);
}
Run Code Online (Sandbox Code Playgroud)
如果此代码在char具有相同值空间的实现上执行,signed char并且字符串中有一个具有负值的字符,则此代码调用未定义行为。正确的版本是:
int upper(const char *s, size_t index) {
return toupper((unsigned char) s[index]);
}
Run Code Online (Sandbox Code Playgroud)
尽管如此,我还是在 C++ 中看到了许多不关心这种未定义行为可能性的例子。那么 C++ 标准中是否有任何内容可以保证上述代码不会导致未定义的行为,或者所有示例都是错误的?
【附加关键词:ctype cctype isalnum isalpha isblank iscntrl isdigit isgraph islowwer isprint ispunct isspace isupper isxdigit tolower]
就其价值而言,Solaris Studio 编译器(使用stlport4)就是这样一种编译器套件,它会在这里产生意外的结果。编译并运行:
#include <stdio.h>\n#include <cctype>\n\nint main() {\n char ch = \'\\xa1\'; // \'\xc2\xa1\' in latin-1 locales + UTF-8\n printf("is whitespace: %i\\n", std::isspace(ch));\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n给我:
\n\nkevin@solaris:~/scratch\n$ CC -library=stlport4 whitespace.cpp && ./a.out \nis whitespace: 8\nRun Code Online (Sandbox Code Playgroud)\n\n以供参考:
\n\n$ CC -V\nCC: Studio 12.5 Sun C++ 5.14 SunOS_i386 2016/05/31\nRun Code Online (Sandbox Code Playgroud)\n\n当然,这种行为已记录在 C++ 标准中,但这绝对令人惊讶。
\n\n编辑:由于有人指出上述版本在尝试分配时char ch = \'\\xa1\'由于整数溢出而包含未定义的行为,因此这里的版本可以避免这种情况并仍然保留相同的输出:
#include <stdio.h>\n#include <cctype>\n\nint main() {\n char ch = -95;\n printf("is whitespace: %i\\n", std::isspace(ch));\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n在我的 Solaris VM 上仍然打印 8:
\n\nkevin@solaris:~/scratch\n$ CC -library=stlport4 whitespace.cpp && ./a.out \nis whitespace: 8\nRun Code Online (Sandbox Code Playgroud)\n\n编辑2:这里的程序可能看起来很正常,但由于 UB 在使用中给出了意想不到的结果std::isspace():
#include <cstdio>\n#include <cstring>\n#include <cctype>\n\nstatic int count_whitespace(const char* str, int n) {\n int count = 0;\n for (int i = 0; i < n; i++)\n if (std::isspace(str[i])) // oops!\n count += 1;\n return count;\n}\n\nint main() {\n const char* batman = "I am batman\\xa1";\n int n = std::strlen(batman);\n std::printf("%i\\n", count_whitespace(batman, n));\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n并且,在我的 Solaris 计算机上:
\n\nkevin@solaris:~/scratch\n$ CC whitespace.cpp && ./a.out\n3\nRun Code Online (Sandbox Code Playgroud)\n\n请注意,根据您如何排列此程序,您可能会得到两个空白字符的预期结果;也就是说,几乎可以肯定存在一些编译器优化,利用此 UB 更快地给出错误结果。
\n\n例如,如果您尝试通过搜索字符串中的(非多字节)空白字符来标记 UTF-8 字符串,您可以想象这会令您感到痛苦。这样的程序在转换str[i]为unsigned char.