儘管c++支援c風格字串,但在c++程式中最好還是不要使用它們。這是因為c風格字串不僅使用起來不太方便,而且極易引發程式漏洞,是諸多安全問題的根本原因。
字串字面值是一種通用結構的例項,這種結構即是c++由c繼承而來的c風格字串(c-style character string)。c風格字串不是一種型別,而是為了表達和使用字串而形成的一種約定俗成的寫法。按此習慣書寫的字串存放在字元陣列中並以空字元結束(null terminated)。以空字元結束的意思是在字串最後乙個字元後面跟著乙個空字元(』\0』)。一般利用指標來操作這些字串。
1.c標準庫string函式
表3.8列舉了c語言標準庫提供的一組函式,這些函式可用於操作c風格字串,它們定義在cstring標頭檔案中,cstring是c語言標頭檔案string.h的c++版本。
表3.8所列的函式不負責驗證其字串引數。
傳入此類函式的指標必須指向以空字元作為結束的陣列:
char ca = ; // 不以空字元結束
cout
<< strlen(ca) << endl; // 嚴重錯誤:ca沒有以空字元結束
此例中,ca雖然也是乙個字元陣列但它不是以空字元作為結束的,因此上述程式將產生未定義的結果。strlen函式將有可能沿著ca在記憶體中的位置不斷向前尋找,直到遇到空字元才停下來。
2. 比較字串
比較兩個c風格字串的方法和之前學習過的比較標準庫string物件的方法大相徑庭。比較標準庫string物件的時候,用的是普通的關係運算子和相等性運算子:
string s1 = "a string example";
string s2 = "a different string";
if (s1 < s2) // false:s2小於s1
如果把這些運算子用在兩個c風格字串上,實際比較的將是指標而非字串本身:
const
char ca1 = "a string example";
const
char ca2 = "a different string";
if (ca1 < ca2) // 未定義的:試圖比較兩個無關位址
當使用陣列的時候其實真正用的是指向陣列首元素的指標。因此,上面的if條件實際上比較的是兩個const char*的值。這兩個指標指向的並非同一物件,所以將得到未定義的結果。
要想比較兩個c風格字串需要呼叫strcmp函式,此時比較的就不再是指標了。如果兩個字串相等,strcmp返回0;如果前面的字串較大,返回正值;如果後面的字串較大,返回負值:
if (strcmp(ca1, ca2) < 0) // 和兩個string物件的比較 s1 < s2效果一樣
3. 目標字串的大小由呼叫者指定連線或拷貝c風格字串也與標準庫string物件的同類操作差別很大。例如,要想把剛剛定義的那兩個string物件s1和s2連線起來,可以直接寫成下面的形式:
// 將largestr初始化成s1、乙個空格和s2的連線
string largestr = s1 + " " + s2;
同樣的操作如果放到ca1和ca2這兩個陣列身上就會產生錯誤了。表示式ca1 + ca2試圖將兩個指標相加,顯然這樣的操作沒什麼意義,也肯定是非法的。
正確的方法是使用strcat函式和strcpy函式。不過要想使用這兩個函式,還必須提供乙個用於存放結果字串的陣列,該陣列必須足夠大以便容納下結果字串及末尾的空字元。下面的**雖然很常見,但是充滿了安全風險,極易引發嚴重錯誤:
// 如果我們計算錯了largestr的大小將引發嚴重錯誤
strcpy(largestr, ca1); // 把ca1拷貝給largestr
strcat(largestr, " "); // 在largestr的末尾加上乙個空格
strcat(largestr, ca2); // 把ca2連線到largestr後面
乙個潛在的問題是,我們在估算largestr所需的空間時不容易估準,而且largestr所存的內容一旦改變,就必須重新檢查其空間是否足夠。不幸的是,這樣的**到處都是,程式設計師根本沒法照顧周全。這類**充滿了風險而且經常導致嚴重的安全洩漏。
對大多數應用來說,使用標準庫string要比使用c風格字串更安全、更高效。
與舊**的介面
4. 混用string物件和c 風格字串
任何出現字串字面值的地方都可以用以空字元結束的字元陣列來替代:
上述性質反過來就不成立了:如果程式的某處需要乙個c風格字串,無法直接用string物件來代替它。例如,不能用string物件直接初始化指向字元的指標。為了完成該功能,string專門提供了乙個名為c_str的成員函式:
char *sstr = s; // 錯誤:不能用string物件初始化char*
const
char *sstr = s.c_str(); // 正確
c_str函式的返回值是乙個c風格的字串。也就是說,函式的返回結果是乙個指標,該指標指向乙個以空字元結束的字元陣列,而這個陣列所存的資料恰好與那個string物件的一樣。結果指標的型別是const char*,從而確保我們不會改變字元陣列的內容。
我們無法保證c_str函式返回的陣列一直有效,事實上,如果後續的操作改變了s的值就可能讓之前返回的陣列失去效用。
如果執行完c_str()函式後程式想一直都能使用其返回的陣列,最好將該陣列重新拷貝乙份。
5. 使用陣列初始化vector物件
不允許使用乙個陣列為另乙個內建型別的陣列賦初值,也不允許使用vector物件初始化陣列。相反的,允許使用陣列來初始化vector物件。要實現這一目的,只需指明要拷貝區域的首元素位址和尾後位址就可以了:
int int_arr = ;
// ivec有6個元素,分別是int_arr中對應元素的副本
vector
ivec(begin(int_arr), end(int_arr))
在上述**中,用於建立ivec的兩個指標實際上指明了用來初始化的值在陣列int_arr中的位置,其中第二個指標應指向待拷貝區域尾元素的下一位置。此例中,使用標準庫函式begin和end來分別計算int_arr的首指標和尾後指標。在最終的結果中,ivec將包含6個元素,它們的次序和值都與陣列int_arr完全一樣。
用於初始化vector物件的值也可能僅是陣列的一部分:
// 拷貝三個元素:int_arr[1]、int_arr[2]、int_arr[3]
vector
subvec(int_arr + 1, int_arr + 4);
這條初始化語句用3個元素建立了物件subvec,3個元素的值分別來自int_arr[1]、int_arr[2]和int_arr[3]。
建議:盡量使用標準庫型別而非陣列
使用指標和陣列很容易出錯。一部分原因是概念上的問題:指標常用於底層操作,因此容易引發一些與煩瑣細節有關的錯誤。其他問題則源於語法錯誤,特別是宣告指標時的語法錯誤。
現代的c++程式應當盡量使用vector和迭代器,避免使用內建陣列和指標;應該盡量使用string,避免使用c風格的基於陣列的字串。
C風格字串與C 風格字串
c風格字串 對字串進行操作的 c 函式定義在標頭檔案中 1.字串定義 char result 2.字串的最後乙個字元是null字元 0 可以通過這個字元確定字串的結尾。3.strlen 返回的是字串的大小 因此,分配空間的時候,需要比字串的實際空間大1.e.g.char copystring con...
C風格字串與C 風格字串
c風格字串 對字串進行操作的 c 函式定義在標頭檔案中 1.字串定義 char result 2.字串的最後乙個字元是null字元 0 可以通過這個字元確定字串的結尾。3.strlen 返回的是字串的大小 因此,分配空間的時候,需要比字串的實際空間大1.e.g.char copystring con...
c風格字串與c風格字串陣列
include includeusing namespace std int main 輸出結果 0034ff10 0034ff10 0034ff04 013bdc80 char str abcd 先在文字常量區為 abcd 常量分配5b,接著在棧裡為指標str分配4b,並接收 abcd 字串的首位...