重学C++ (一)
2015-08-02 10:16
211 查看
序
一直以来都把C++当作C+template+STL,然而C++其实远不止此。有天,大师兄神神秘秘地叫住我:“给你看段C++,猜猜它是在做什么?”显然我并没有看懂。据他说这四行代码牵涉到C++的五个较为罕见的语法点orz
于是就有了写本系列博客的欲望。以C++11标准的Working Draft为根据,以期总结一些以前不太留意的、比较有意思的C/C++特性。
记得有位哲人曾经说过这么一句话,我认为非常切题:
大一就已经学过C++,到了大四却看不懂几行代码。
没错这是我说的。
Trigraph Sequences
Trigraph Sequences是一个古老且讨厌的东西。它出现的原因是某些键盘只支持ISO 646——C/C++语言标准中包含了该字符集以外的一些字符(#{}[]等)。虽然我感觉这辈子也遇不到一个这样的键盘,但C/C++的预处理程序还是会乖乖地把程序中所有的Trigraph Sequences给替换掉。
C99或是C++0x下的一个有趣的例子:
int i = 0; //test??/ i++;
打印出
i后可以发现
i++根本没有被执行。因为
??/被替换成了
\,而这会把下一行与本行拼接起来。
所以在我们写程序时要时刻注意两个问号连用的情况,无论是在注释还是在字符串中(可能会报warning,g++中可以使用-Wno-trigraphs关闭提示)。下面是C++11(也有人管它叫C++0x)标准中规定的所有Trigraph Sequences:
Trigraph | Replacement | Trigraph | Replacement | Trigraph | Replacement |
---|---|---|---|---|---|
??= | # | ??( | [ | ??< | { |
??/ | \ | ??) | ] | ??> | } |
??’ | ^ | ??! | | | ??- | ~ |
Alternative Tokens
Trigraph有效地解决了ISO 646的问题,然而这么写程序真的是太丑了://#define arraycheck(a,b) a[b] || b[a] ??=define arraycheck(a,b) a??(b??) ??!??! b??(a??)
于是人们发明了第二套方案,使用Alternative Token来替换无法显示的token:
//#define arraycheck(a,b) a[b] || b[a] %:define arraycheck(a,b) a<:b:> or b<:a:>
好看多了是吧。
在C中同样的东西是以宏的形式定义在
<iso646.h>中,而在C++中是内建在语法中的。
为什么要使用token这个词,而不像Trigraph一样用Sequence呢?前面提到过,在预编译阶段Trigraph的替换是首先发生的,是在识别token之前的。这意味着不论这个Trigraph的位置在哪里它都会被替换。
而在C++11中提到,Alternative Tokens会以token为单位替换它的原始token。作为比较,可以在g++中使用-std=c++11编译选项,运行一下这段程序:
cout << "??(" << endl; //print [ cout << "<:" << endl; //print <:
下面是C++11规定的所有Alternative Tokens:
Alternative | Primitive | Alternative | Primitive | Alternative | Primitive |
---|---|---|---|---|---|
<% | { | and | && | and_eq | &= |
%> | } | bitor | | | or_eq | |= |
<: | [ | or | || | xor_eq | ^= |
:> | ] | xor | ^ | not | ! |
%: | # | compl | ~ | not_eq | != |
%:%: | # | bitand | & |
Escape Sequence
转义字符其实没什么多说的。以前忽略的一点是八进制或十六进制数形式的直接转义。前者直接就是
\ooo,后者
\xhhh要在前面加一个x。最常用的就是
\0啦。
这种形式的转义,八进制字符最多有三个,而对十六进制的合法字符来说并没有什么数目的限制,碰到第一个非法字符即认为character literal结束。溢出怎么办,就由编译器决定咯。
Raw String
为了简化包含大量需要转义字符的字符串的书写,python采用了原始字符串的方法,C++11也开始使用这一概念:prefixoptR"delimiteropt(raw_characteropt∗)delimiteropt"
prefix是可选项,包括u8、u、U和L。前三者是从C++11开始才有的新特性。这四种前缀对应的字符串类型分别为
const char[]、
const char_16[]、
const char_32[]和
const wchar_t[]。
字母R是Raw String的标志。
delimiter是可选项,的作用是标记Raw String的开头和结尾,至多16个字符。这16个字符中不能出现转义符\、左右圆括号()和空格等空白字符。注意开头结尾的两个delimiter是一样的。
raw_character也是可选项。它们被包含在
d(和
)d之间,不能包含结束符号组合
)d。其中
d代表一个特定的delimiter,可以为空。
Raw String能Raw到什么程度呢?它连源代码中的换行符也不会放过的:
const char *p = R"(a\ b c)"; assert(std::strcmp(p, "a\\\nb\n\tc") == 0);
更神奇的是这个:
const char *p = R"(??)"; assert(std::strcmp(p, "\?\?") == 0);
如果看过前面关于Trigraph Sequences的介绍,这两句的重要性就不言而喻了。显然,Trigraph替换是在识别任何token之前发生的,但为何这里咋又变回来了呢?
且听下回分解。
转载请注明出处,欢迎关注我的新浪微博@窃听者Oscar
相关文章推荐
- CTime 和CString的互相转换
- C++面向对象程序设计 笔记1(Class without pointer members)
- 黑马程序员---c语言 通讯录的实现
- C语言深度解剖——读书笔记-5、预处理
- C++抽象类小览
- C++ new(3)
- C++ new(2)
- C语言深度解剖——读书笔记-4、符号
- C++ new(1)
- 一起talk C栗子吧(第二十八回:C语言实例--希尔排序)
- C语言变参问题
- PROBLEM_J: Gym 100345J - Zen Garden
- C语言函数参数传递之痛
- 我自己的noip复习(实时更新)
- NYOJ 485 A*B Problem
- C语言之函数调用19—总结
- 利用静态数组实现栈 c语言
- C语言之函数调用18—多字符串排序
- c++_benchMark_vector_list_deque
- Three Palindromes