在編寫程序時需要時時提防編譯器背著我們干一些沒有通知我們做的事,下面列舉轉義字符對程序的影響。
首先列出二字符組和三字符組對應的意思。
二元字符 等價字符
<: [
:> ]
<% {
%> }
%:或者%% #
三元字符 等價字符
??= #
??/ \
??' ^
??( [
??) ]
??! |
??< {
??> }
??- ~
值得慶幸的是,二元字符或者三元字符轉義之後不能再與二元字符或者三元字符結合,要不然程序員可能會瘋了。閒話少說,那麼為什麼會c++標准允許出現這種情況呢?
根據 Intention to deprecate trigraphs in the next C++ Standard可以知道這有兩個原因:
1.由於類似“#\”等這些字符在EBCDIC的代碼頁中用來區分代碼點的。在所有的EBCDIC的代碼頁中使用“?”和"="不會分割代碼頁,而會共享同一個代碼點。
2.其次是因為一些國際化的鍵盤設置原因,有些鍵盤沒有這些字符對應的按鍵,而這些字符又必須在一些文本中使用,因此采用一些替代方法。
知道了原因,接下來看看讓程序員摸不著頭腦的錯誤代碼(其實不是語法錯誤,而是程序運行的結果會讓你大吃一驚)。
情形1:
#include <iostream>
int main()
{
int x = 1;
for( int i = 0; i < 100; ++i );
// What will the next line do? Increment???????????/
++x;
std::cout << x;
}
程序運行前,聰明的讀者可以猜猜結果是多少?改變上面的程序,僅僅是一點點的一點點。
情形2:
#include <iostream>
int main()
{
int x = 1;
for( int i = 0; i < 100; ++i )
// What will the next line do? Increment???????????/
++x;
std::cout << x;
}
最終的結果又是多少呢?
未運行前,你是否認為情形1的結果與情況2的結果一樣,輸出的結果都是101呢?細心的讀者,會認為情形1的最終結果是2,而情形2的結果是101呢?
當然你認為的結果在有些編譯器下確實是對的,這是因為有些編譯器在默認情況將3元轉義字符是禁止的。例如我在mac的g++編譯器下編譯上面的程序時它會提醒一個警告,如下:
warning: trigraph ??/ ignored, use -trigraphs to enable
即要使用trigraphs在編譯時需要添加上-trigraphs參數。因此未加-trigraphs參數時,得出的結果與細心的讀者預料的結果一致。而加上-trigraphs參數之後,那麼結果你可能就得好好分析了。這裡我們知道了注釋行存在一個三元轉義符"??/",其對應得符號為"\",而"\"將其下面得一行代碼也作為注釋,所以情形1實際的代碼為:
#include <iostream>
int main()
{
int x = 1;
for( int i = 0; i < 100; ++i );
std::cout << x;
}
情形2實際運行的代碼為:
#include <iostream>
int main()
{
int x = 1;
for( int i = 0; i < 100; ++i )
std::cout << x;
}
因此情形1的輸出為: 1
情形2的輸出為:11111111111。。。1(一共為100個1)。
或許你認為上面的錯誤出現的概率極其小,其實這是由於"?"與"/"只相差一個shift鍵而已。本來程序員是想打印出多個“???”,卻在最後一個“?”松開了shift鍵,而將“???”變成了“??/”。最終的結果你也看到了吧!
還有更多的2元、3元轉義字符造成的一些莫名其妙的結果參見:http://gcc.gnu.org/ml/gcc/2003-05/msg01691.html
最後,不得不提一下c++標准委員會已經在2009年對是否取消3元轉義符號問題進行了投票,雖然沒有完全取消三元轉義字符,但是增加一些使用的限制。具體可參考:http://stackoverflow.com/questions/6855149/are-trigraph-substitutions-reverted-when-a-raw-string-is-created-through-concate中的解答。
PS:前面提到了EBCDIC碼,而我們學習的基本上都是基於asc碼的,那麼他們之間的區別為什麼呢?
使用得最多的、最普遍的是ASCII字符編碼, 即American Standard Code for Information Interchange, 如表1所示。
從表中可以看到:
每個字符是用7位基2碼表示的, 其排列次序為b6b5b4b3b2b1b0, 在表中的b6b5b4為高位部分, b3b2b31b0為低位部分。而一個字符在計算機內實際上用8位表示。正常情況下, 最高一位b7為 "0"。在需要奇偶校驗時, 這一位可用於存放奇偶校驗的值, 此時稱這一位為校驗位。
表1 ASCII字符編碼表
b6b5b4
000 001 010 011 100 101 110 111
b3b2b1b0
-
0 0 0 0
0 0 0 1
0 0 1 0
0 0 1 1
0 1 0 0
0 1 0 1
0 1 1 0
0 1 1 1
1 0 0 0
1 0 0 1
1 0 1 0
1 0 1 1
1 1 0 0
1 1 0 1
1 1 1 0
1 1 1 1 NUL DLE SP 0 @ P 、 p
SOH DC1 ! 1 A Q a q
STX DC2 " 2 B R b r
ETX DC3 # 3 C S c s
EOT DC4 $ 4 D T d t
ENQ NAK % 5 E U e u
ACK SYN & 6 F V f v
BEL ETB ' 7 G W g w
BS CAN ( 8 H X h x
HT EM ) 9 I Y I y
LF SUB * : J Z j z
VT ESC + ; K [ k {
FF FS , < L \ l |
CR GS - = M ] m }
SO RS . > N ↑ m ~
SI US / ? O - o DEL
ASCII是128個字符組成的字符集。其中編碼值0-31不對應任何可印刷(或稱有字形)字符, 通常稱它們為控制字符, 用於通信中的通信控制或對計算機設備的功能控制。編碼值為32的是空格(或間隔)字符SP。編碼值為127的是刪除控制DEL碼。其余的94個字符稱為可印刷字符,有人把空格也計入可印刷字符時,則稱有95個可印刷字符。請注意, 這種字符編碼中有如下兩個規律:
(1)字符0-9這10個數字符的高3位編碼為011, 低4 位為000-1001。當去掉高3位的值時, 低4位正好是二進制形式的0-9。這既滿足正常的排序關系, 又有利於完成ASCII碼與二進制碼之間的類型轉換。
(2)英文字母的編碼值滿足正常的字母排序關系, 且大、小寫英文字母編碼的對應關系相當簡便, 差別僅表現在b5一位的值為0或1, 有利於大、小寫字母之間的編碼變換。
另有一種字符編碼,是主要用在IBM計算機中的EBCDIC代碼(Extended Binary Coded Decimal Interchange Code)。它采用8位碼, 有256個編碼狀態, 但只選用其中一部分。0-9十個數字符的高4位編碼為1111, 低4位仍為0000-1001。大、小寫英文字母的編碼同樣滿足正常的排序要求, 而且有簡單的對應關系, 即同一個字母的大小寫的編碼值僅最高的第二位的值不同, 易於識別與變換。