官术网_书友最值得收藏!

1.8 字符指針和字符串處理

在C++中,字符串既可以通過字符數組來存取,也可以用字符指針來操作。但使用字符數組時,有時其數組大小與字符串的長度不一定相匹配,因此在許多場合中使用字符指針更為恰當。另外,字符串本身有拼接、截取及比較等操作,本節就來討論這些內容。

1.8.1 字符指針

若定義一個指針時指定的類型是char*,則這樣定義的指針為字符指針。與普通指針變量一樣,在C++中定義一個字符指針變量(指針)的格式如下:

char*<指針名1>[,*<指針名2>, …];

例如:

char*str1,*str2;                 // 字符指針

則定義的str1和str2都是字符指針變量。對于字符指針變量的初始化,可以用字符串常量或一維字符數組進行。

由于一個字符串常量有一個地址,因而它可以賦給一個字符指針變量。例如:

char *p1="Hello";

char *p1;
p1 = "Hello";

都使得字符指針變量p1指向“Hello”字符串常量的內存空間。

由于一維字符數組可以存放字符串常量,此時的字符數組名就是一個指向字符串的指針常量,因此它也可用于字符指針變量的初始化或賦值操作。例如:

char *p1, str[] = "Hello";

p1 = str;

則使得字符指針變量p1指向字符數組str的內存空間,而str存放的內容是“Hello”字符串常量,因此這種賦值實際上是使p1間接指向“Hello”字符串常量。

要注意,用字符串常量和字符數組來初始化字符指針在本質上是不同的。主要體現在:

(1)在有的編譯系統中,相同字符串常量的地址可能是相同的,但相同字符串內容的兩個字符數組的地址一定不同。例如:

char *p1,*p2;
p1="Hello";   p2="Hello";

由于字符指針p1和p2指向的是同一個字符串變量,因此它們的地址值在有的編譯系統(如Visual C++)中是相同的。正因為如此,許多操作系統允許用一個字符串常量來標識一個內存塊。但如果:

char *p1,*p2;
char str1[]="Hello",  str2[]="Hello";
p1=str1; p2=str2;

則字符指針p1和p2間接指向的雖是同一個字符串變量,但它們的地址值一定是不同的。因為它們指向的字符數組空間的地址不一樣。

(2)在大多數編譯系統(如Visual C++)中,字符串常量所在的是常量區,其內存空間的內容在程序運行時是不可修改的,而字符數組的內存空間的內容是可修改的。例如,若:

char str[80],*p1=str;
cin>>str;                          /* 合法 */
cout<<p1;                          /* 合法 */

char *p1="Hello";
cin>>p1;                          /*A:合法,但Visual C++會使程序異常終止*/
cout<<p1;                         /* 合法 */

這里p1指向“Hello”所在的常量內存區。由于該內存空間的內容不可在程序運行中修改,因此A語句雖在編譯時是合法的,但運行時它會修改常量區的內容,這是不允許的,因而會造成程序異常終止。

字符指針一旦初始化或賦初值后,就可在程序中使用它,并且以前討論過的指針操作都可以用于字符指針。例如,下面的示例是將一個字符串逆序輸出。

例Ex_StrInv】 字符串逆序輸出

#include <iostream.h>
int  main()
{
    char *p1 = "ABCDEFG", *p2 = p1;
    while(*p1!='\0')p1++;           /* 將指針指向字符常量最后的結束符 */
    while (p2<=p1--)
        cout<<*p1;
    cout<<endl;
    return 0;
}

程序運行結果如下:

GFEDCBA

1.8.2 帶參數的main函數

到目前為止,我們所接觸到的main函數都是不帶參數的。但在實際應用中,程序有時需要從命令行輸入參數。例如:

c:\>copy file1 file2

這是一個常用的DOS命令。當它運行時,操作系統將命令行參數以字符串的形式傳遞給main()。為了能使程序處理這些參數,需要main帶有參數,其最常用的格式是:

數據類型 main(int argc,char*argv[])

其中,第一個int型參數用來存放命令行參數的個數,實際上argc所存放的數值比命令行參數的個數多1,即將命令字(或稱為可執行文件名,如copy)也計算在內。第二個參數argv是一個一維的指針數組,用來存放命令行中各個參數和命令字的字符串,且規定:

argv[0]存放命令字
argv[1]存放命令行中第一個參數
argv[2]存放命令行中第二個參數
argv[3]存放命令行中第三個參數
…

這里,argc的值和argv[]各個元素的值都是系統自動賦給的。

例Ex_Main】 處理命令行參數

#include <iostream.h>
int  main(int argc,char*argv[])
{
    cout<<"這個程序的程序名是:"<<argv[0]<<"\n";
    if (argc<=1)
        cout<<"沒有參數!";
    else
    {
        int nCount = 1;
        while(nCount < argc)
        {
            cout<<"第"<<nCount<<"個參數是:"<<argv[nCount]<<"\n";
            nCount++;
        }
    }
    return 0;
}

程序編譯連接后,將Ex_Main.exe復制到C盤,然后切換到DOS命令提示符進行測試。

程序運行結果如下:

這個程序的程序名是:Ex_Main

第1個參數是:ab

第2個參數是:cd

第3個參數是:E

第4個參數是:F

1.8.3 字符串處理函數

由于字符串使用廣泛,幾乎所有版本的C++都提供了若干個字符串處理函數,放在string.h頭文件中,這里介紹幾個常用的函數。

1.strcat和strncat

函數strcat是“string(字符串)catenate(連接)”的簡寫,其作用是將兩個字符串連接起來,形成一個新的字符串。它的函數原型如下:

char *strcat(char*dest,const char*src);

其功能是將第2個參數src指定的字符串連接到由第1個參數dest指定的字符串的末尾,連接成新的字符串后由參數dest返回。函數成功調用后,返回指向dest內存空間的指針,否則返回空指針NULL。例如:

結果輸出goodmorning。需要說明的是:

(1)dest指向的內存空間必須足夠大,且是可寫的,以便能存下連接的新字符串。這就是說, dest位置處的實參不能是字符串常量,也不能是const字符指針。

(2)盡管dest和scr指定的字符串都有'\0',但連接的時候,dest字符串后面的'\0'被清除,這樣連接后的新字符串只有末尾仍保留'\0'結束符。

(3)在string.h頭文件中,還有一個strncat函數,其作用也是用于兩個字符串的連接,其函數原型如下:

char *strncat(char*dest,const char*src,size_t maxlen);

只不過,它還限定了連接到dest的字符串src的最大字符個數maxlen。若字符串src字符個數小于或等于maxlen,則等同于strcat。若字符串src字符個數大于maxlen,則只有字符串src的前maxlen個字符被連接到dest字符串末尾。例如:

則輸出結果為goodmor。

2.strcpy和strncpy

函數strcpy是“string copy”(字符串復制)的簡寫,用于字符串的“賦值”。其函數原型如下:

char  *strcpy(char*dest,const char*src);

其功能是將第2個參數src指定的字符串復制到由第1個參數dest指定的內存空間中,包括結尾的字符串結束符'\0'。復制后的字符串由參數dest返回。函數成功調用后,返回指向dest內存空間的指針,否則返回空指針NULL。例如:

char s1[50];
char s2[]="word";
strcpy(s1,s2);
cout<<s1;

結果輸出word,說明strcpy已經將s2的字符串復制到了s1中。需要說明的是:

(1)復制是內存空間的寫入操作,因而需要dest所指向的內存空間足夠大,且內存空間是可寫入的,以便能容納被復制的字符串src。要注意,dest所指向的內存空間的大小至少是scr字符個數+1,因為末尾還有一個結束符'\0'。例如,下面的錯誤代碼比較隱蔽:

char s2[]="ABC";
char s1[3];
strcpy(s1,s2);
cout<<s1;

表面上看s2只有3個字符,s1定義長度3就夠了。但strcpy執行過程會將字符串結束符也一起復制過去,因此s1的長度應該至少定義為4。

(2)不能試圖通過指針的指向改變來復制字符串。例如,下面的代碼都不是復制:

char s2[]="ABC";
char s1[10], *pstr;
s1=s2;                        // 錯誤:s1是指針常量,不能作為左值
pstr=s1;                      // pstr指向s1內存空間
pstr=s2;                      // pstr指向s2內存空間
cout<<s1;

雖然輸出的結果也是ABC,看似復制成功,但事實上只是pstr指向s2內存空間,并非s1內存空間的內容是字符串“ABC”。

(3)可以使用strncpy函數來限制被復制的字符串src的字符個數。strncpy函數原型如下:

char *strncpy(char*dest,const char*src,size_t maxlen);

其中,maxlen用來指定被復制字符串src的最大字符個數(不含結束符'\0')。若字符串src字符個數小于或等于maxlen,則等同于strcpy。若字符串src字符個數大于maxlen,則只有字符串src的前maxlen個字符連同結束符'\0'被復制到dest指定的內存空間中。例如:

char s1[50];
char s2[]="word";
strncpy(s1,s2, 2);
cout<<s1;

結果輸出wo。

3.strcmp和strncmp

string.h頭文件中定義的函數strcmp是“string compare”(字符串比較)的簡寫,用于兩個字符串的“比較”。其函數原型如下:

int  strcmp(const char*s1,const char*s2);

其功能是:如果字符串s1和字符串s2完全相等,則函數返回0;如果字符串s1大于字符串s2,則函數返回一個正整數;如果字符串s1小于字符串s2,則函數返回一個負整數。

在strcmp函數中,字符串比較的規則是:將兩個字符串從左至右逐個字符按照ASCII碼值的大小進行比較,直到出現ASCII碼值不相等的字符或遇到'\0'為止。如果所有字符的ASCII碼值都相等,則這兩個字符串相等。如果出現了不相等的字符,以第一個不相等字符的ASCII碼值比較結果為準。需要說明的是:

(1)在字符串比較操作中,不能直接通過使用“關系運算符”來比較兩個字符數組名或字符串常量或字符指針來決定字符串本身是否相等、大于或小于等。例如:

char s1[100], s2[100];
cin>>s1>>s2;
if( s1 == s2 ) cout<<"same!"<<endl;

這種比較只是比較s1和s2所在的內存空間的首地址,并非是字符串內容的比較。

(2)可以使用strncmp函數來限制兩個字符串比較的字符個數。strncmp函數原型如下:

int strncmp(const char *s1, const char *s2, size_t maxlen);

其中,maxlen用來指定兩個字符串比較的最大字符個數。若字符串s1或s2中任一字符串的字符個數小于或等于maxlen,則等同于strcmp。若字符串s1和s2字符個數都大于maxlen,則參與比較的是前maxlen個字符。例如:

char s1[] = "these";
char s2[] = "that";
int i = strncmp(s1,s2, 2);
cout<<i<<endl;

結果輸出為0,因為s1和s2字符串的前兩個字符是相等的。

事實上,字符串操作還不止上述論及的庫函數,string.h頭文件中定義的還有許多,如strlen (求字符串長度、字符個數,不是字節數)、strlwr(轉換成小寫)、strupr(轉換成大寫)及strstr (查找子串)等。這些庫函數的功能和原型可參見附錄A。

主站蜘蛛池模板: 田阳县| 边坝县| 萝北县| 河东区| 建平县| 大城县| 高密市| 云和县| 麻栗坡县| 海丰县| 拉萨市| 苏州市| 凭祥市| 察哈| 龙川县| 平南县| 沧州市| 鄂托克旗| 铜陵市| 慈溪市| 即墨市| 偃师市| 武夷山市| 沈丘县| 星子县| 齐齐哈尔市| 平利县| 太保市| 唐山市| 元江| 林芝县| 晋宁县| 镇坪县| 涡阳县| 嘉荫县| 甘德县| 如东县| 宜兰市| 合川市| 奉贤区| 奉节县|