Cpp:C風格字符串

前面的內(nèi)容我們使用過字符串字面值,并了解字符串字面值的類型是字符常量的數(shù)組,現(xiàn)在可以更加明確的認識到:字符串字面值的類型就是 const char 類型的數(shù)組。C++從C繼承下來的一種通用的結(jié)構(gòu)是C風格字符串(C-Style character string),字符串字面值就是該類型的實例。實際上,C風格字符串既不能確切的歸結(jié)為C語言類型,也不能歸結(jié)為C++的類型,而是以空字符 null 結(jié)束的字符數(shù)組:

char c1[] = {'C', '+', '+'};        //c1 維數(shù)為3,not c-style
char c2[] = {'C', '+', '+', '\0'};    //c2 維數(shù)為4
char c3[] = "C++";                    //c3 維數(shù)為4,自動添加空字符
const char *cp = "C++";        //自動添加空字符
char *cp1 = c1;        //指向c1數(shù)組的第一個元素,not c-style
char *cp2 = c2;        //指向c1數(shù)組的第一個元素,以空字符 `null` 結(jié)束的字符數(shù)組

c1cp1 都不是C風格字符串:c1 是不帶結(jié)束符 null 的字符數(shù)組。其他都是C風格字符串。

1、C風格字符串的使用


C++通過 (const) char* 類型的指針來操作C風格的字符串。

const char *cp = "C++";
while (*cp) {
    cout << *cp << endl;
    ++cp;
}

輸出

C
+
+

如果cp指向的字符數(shù)組沒有 null 結(jié)束符,作為循環(huán)會失敗。這時循環(huán)繼續(xù)進行,知道遇到內(nèi)存中某處的 null 結(jié)束符。

2、C風格字符串的標準庫函數(shù)


下表類除了C語言標準庫函數(shù)提供的一系列處理C風格字符串的庫函數(shù),這些庫函數(shù)包含在 string.h 中。

庫函數(shù) 含義
strlen(s) 返回s的長度,不包括null結(jié)束符
strcmp(s1, s2) 比較s1和s2是否相同。s1等于s2,返回0;s1大于s2,返回正數(shù);s1小于s2,返回負數(shù)
strcat(s1, s2) 將字符串s2連接到s1后面,返回s1
strcpy(s1, s2) 將s2復(fù)制給s2,返回s1
strncat(s1, s2, n) 將s2的前n個字符連接到s1后面,并返回s1
strncpy(s1, s2, n) 將s2的前n個字符賦給s1,并返回s1

傳遞給這些函數(shù)的指針必須具有非零值,并且指向以null結(jié)束的字符數(shù)組中的第一個元素。并且要確保目標字符串必須有足夠大的空間存放結(jié)果串。

字符串的比較和比較結(jié)果都必須使用標準庫函數(shù) strcmp 進行

const char *cp1 = "C++";
const char *cp2 = "C";
int i = strcmp(cp1, cp2);
cout << i << endl; 
i = strcmp(cp2, cp1);
cout << i << endl;
i = strcmp(cp1, cp1);
cout << i << endl;

輸出

1
-1
0

不要忘記字符串結(jié)束符null

char c[] = { 'c' };
cout << strlen(c) << endl;

輸出結(jié)果是不可預(yù)料的,因為會一直尋找null結(jié)束符,然后再輸出。比如在我的電腦是輸出是 324,這明顯是錯誤的,在不同的電腦會輸出不同的值。

必須確保目標字符串有足夠的大小

庫函數(shù) strcat 和 strcpy 的第一個參數(shù)必須有足夠大的空間

const char *cp1 = "C++";
const char *cp2 = "C";
char str[3 + 1 + 2];
strcpy(str, cp1);
strcat(str, " ");
strcat(str, cp2);
cout << str << endl;

輸出

C++ C

上述程序就目前來看是完全沒問題的,但如果 cp1 和 cp2 指向的字符串大小發(fā)生了改變,str 所需的大小就不滿足要求了。會導(dǎo)致嚴重的安全漏洞。

strn函數(shù)處理C風格的字符串

如果必須處理C風格字符串,strncat,strncpy會比strcat,strcpy更安全

char str[3 + 1 + 2];
strncpy(str, cp1, 4);    //1. 包含null
strncat(str, " ", 2);    //2. 包含null,看起來冗余,但是個好習(xí)慣
strncat(str, cp2, 2);    //3. 要包含null
cout << str << endl;

分步驟解釋:

    1. strncpy cp1 時需要復(fù)制 4 個字符:cp1 中所有字符,加上 null 結(jié)束符;此時 strlen(str) = 3.
    1. strncat " " 時需要復(fù)制 2 個字符:一個空格字符,加上 null 結(jié)束符;此時空格符會把步驟1復(fù)制的 null 結(jié)束符覆蓋。此時 strlen(str) = 4.
    1. strncat cp2 時需要復(fù)制 2 個字符:cp2 中所有字符,加上 null 結(jié)束符;此時 strlen(str) = 6.

最后 str 的內(nèi)容:cp1 和 cp2 中所有字符,一個空格,和一個null結(jié)束符。

盡可能使用string類型

string str = cp1;
str += " ";
str += cp2;

可以達到上面一樣的效果。

3、創(chuàng)建動態(tài)數(shù)組


數(shù)組類型的限制:長度不變;編譯時須知道長度;數(shù)組只在定義它的語句內(nèi)存在。可以使用動態(tài)分配解決這一問題,跟C中的malloc和free類似,C++中使用 newdelete 實現(xiàn)。

3.1、定義

數(shù)組變量需要指定類型、數(shù)組名和維數(shù)定義,而動態(tài)分配的數(shù)組只需指定類型和長度,不必為數(shù)組對象命名。

int *ip = new int[10];

3.2、初始化

動態(tài)分配的數(shù)組,如果有類型,則使用類型的默認構(gòu)造函數(shù)初始化;如果是內(nèi)置類型,則無初始化:

string *sp = new string[10];    //初始化為10個空字符串
int *ip = new int[10];        //不初始化
int *ip2 = new int [10]();    //初始化為10個0

圓括號要求編譯器對數(shù)組進行初始化;
動態(tài)分配的數(shù)組,其元素只能初始化為默認值,而不能像變量一樣提供初始化列表進行初始化

3.3、const對象的動態(tài)數(shù)組

必須為該數(shù)組提供初始化值,因為數(shù)組內(nèi)都是const對象,無法賦值

const int *caip_bad = new const int[10];    //error
const int *caip_ok = new const int[10]();    //ok

也可以定義類類型的const數(shù)組,但該類型必須提供默認構(gòu)造函數(shù)

 const string *csp = new const string[10];    //ok

3.4、動態(tài)分配空數(shù)組

有時候,編譯時并不知道數(shù)組的長度,這時可以動態(tài)分配空數(shù)組??慈缦鲁绦?/p>

size_t n = get_size();
int *p = new int[n];
for (int *q = p; q != p + n; ++q)
    //process the array

即使 get_size() 返回 0 也是可以的。例如

char arr[0];    //error
char *cp = new char[0];    //ok,但不能被解引用

上述 cp 指針 允許 的操作有:比較;本身加(減)0。

3.5、動態(tài)空間釋放

動態(tài)分配的空間必須釋放,不然內(nèi)存會被逐漸耗盡

delete [] cp;

該語句回收了 cp 所指向的數(shù)組。把相應(yīng)的內(nèi)存返還給自由存儲區(qū)。

理論上,如果少了 [] 應(yīng)該會導(dǎo)致少釋放空間,從而產(chǎn)生內(nèi)存泄露,因此,釋放動態(tài)數(shù)組時,不能忘記 []

3.6、動態(tài)數(shù)組的使用

長度不一樣的兩個字符串,賦給同一個新的字符數(shù)組

const char *txt1 = "HAHAHA";
const char *txt2 = "HEHEHEHEHEHE";

const char *txt;
if(condition)    //condition表示某個條件
    txt = txt1;
else
    txt = txt2;

size_t demension = strlen(txt) + 1;
char *msg = new char[demension];
strncpy(msg, txt, demension);
cout << msg << endl;

4、新舊代碼的兼容

4.1、混合使用string庫和C風格字符串

可用字符串字面值初始化string對象

string str("Hello World");

通常,C風格字符串與字符串字面值有相同的數(shù)據(jù)類型,而且都是以空字符null結(jié)束,因此可以把C風格字符串用在任何可以使用字符串字面值的地方:

  • C風格字符串對string對象進行賦值或初始化;
  • C風格字符串可以作為string類型的加法操作兩個參數(shù)中的一個;

但反之不成立,但可以通過名為 c_str() 的函數(shù)轉(zhuǎn)化為C風格字符串

char *str1 = str;    //error
const char *str2 = str.c_str();    //ok

4.2、使用數(shù)組初始化vector對象

const size_t arr_size = 6;
int int_arr[arr_size] = {0, 1, 2, 3, 4, 5};
vector<int> ivec(int_arr, int_arr + arr_size);

//相當于用 int_arr[1], int_arr[2], int_arr[3] 初始化 ivec1
vector<int> ivec1(int_arr + 1, int_arr + 4);

舉例1

編寫程序: 從標準輸入設(shè)備讀入字符串,并把字符串存放在字符數(shù)組中(輸入的字符串長度不定)。

int main()
{
    size_t arry_size = 10;
    char *p_str = new char[arry_size];
    int count = 0;//統(tǒng)計輸入的字符數(shù)
    char c;//存放字符

    cout << "Enter some chars:" << endl;
    while (cin.get(c))//每次讀取一個字符賦給c
    {
        if (count + 1 >= arry_size)//超出預(yù)設(shè)長度
        {
            arry_size += 10;//長度增加10
            char *p_temp = new char[arry_size];//申請新空間
            strncpy(p_temp, p_str, count);//將就空間的內(nèi)容拷貝到新空間
            delete[] p_str;//銷毀以前的p_str(p_temp)
            p_str = p_temp;//將新空間p_temp賦給p_str
        }

        p_str[count] = c;
        count++;
    }
    p_str[count] = '\0';//添加null結(jié)束符

    cout << "outputs:" <<endl;
    for (size_t i = 0;i != strlen(p_str); ++i)
    {
        cout << p_str[i];
    }
    cout << endl;
    cout << p_str << endl;
    delete[] p_str;//最后銷毀p_str
    cout << "end" <<endl;

    return 0;
}

運行測試

Enter some chars:
asdfghjklqwertyuiop↙
^Z↙
outputs:
asdfghjklqwertyuiop(輸出換行符)

asdfghjklqwertyuiop(輸出換行符)

end

舉例2

編寫程序:讀入一組string類型數(shù)據(jù),將它們存儲在vector中,然后將vector中的對象復(fù)制給一個字符指針數(shù)組。即為vector中的每個元素創(chuàng)建一個新的字符數(shù)組,然后把vector元素的數(shù)據(jù)復(fù)制到相應(yīng)的字符數(shù)組中,最后將指針插入到指針數(shù)組中。

int main()
{
    vector<string> svec;
    string str;
    while (cin >> str) {
        svec.push_back(str);
    }

    char **cp_arr = new char*[svec.size()];//指針數(shù)組
    int cnt = 0;
    for (vector<string>::iterator iter = svec.begin(); iter != svec.end(); ++iter) {
        string s = *iter;
        char *cp = new char[s.size()+1];//+1表示為null結(jié)束符預(yù)留空間
        strncpy(cp, s.c_str(), s.size()+1);
        cp_arr[cnt] = cp;
        ++cnt;
    }

    for (int i = 0; i != cnt; ++i) {
        cout << cp_arr[i] << endl;
        delete[] cp_arr[i];
    }
    delete[] cp_arr;

    return 0;
}

運行結(jié)果

hello↙
world↙
^Z↙
hello
world

5、多維數(shù)組

嚴格的講,C++中沒有多維數(shù)組,只有數(shù)組的數(shù)組

初始化

int ia[3][2] = {
    {0, 1},
    {2, 3},
    {4, 5}
};

且等價于

int ia[3][2] = {0, 1, 2, 3, 4, 5};

而下面情況只初始化第一個元素,則其余初始化為0

int ia[3][2] = {{1}, {2}, {3}};

等價于

int ia[3][2] = {{1, 0}, {2, 0}, {3, 0}};

但不會等價于

int ia[3][2] = {1, 2, 3};//等價于int ia[3][2] = {1, 2, 3, 0, 0, 0}

下標引用

const size_t rsz = 3;
const size_t csz = 2;
int ia[rsz][csz];
for (int i = 0; i != rsz; ++i) {
    for (int j = 0;j != csz; ++j) {
        ia[i][j] = i + j;
    }
}

訪問特定元素時,必須提供行下標與列下標

指針與多維數(shù)組

與普通數(shù)組一樣,多為數(shù)組名也是指向第一個元素的指針。

int main()
{
    int ia[3][2] = {
        { 0, 1 },
        { 2, 3 },
        { 4, 5 }
    };
    int (*ip)[2];    //ok: 一個指向包含2個int值的數(shù)組的指針
    ip = &ia[1];    //ok: ia[1] 包含2個int值
    cout << (*ip)[0] << "," << (*ip)[1] << endl;
    return 0;
}

運行結(jié)果

2,3

int (*ip)[2];//指向數(shù)組的指針;即ip是指向包含兩個int值的數(shù)組的指針
int *ip[2];//指針的數(shù)組;即ip是包含兩個指針的數(shù)組

typedef簡化多維數(shù)組指針

指向 ia 的指針

typedef int int_arr[2];
int_arr *ip = ia;

int_arr 輸出 ia 的元素

int ia[3][2] = {
    { 0, 1 },
    { 2, 3 },
    { 4, 5 }
};
typedef int int_arr[2];
for (int_arr *p = ia; p != ia + 3; ++p) {
    for (int *q = *p; q != *p + 2; ++q){
        cout << *q << endl;
    }
}

結(jié)果

0
1
2
3
4
5

END.


Github Pages同步更新: Humooo's Blog

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 指針是C語言中廣泛使用的一種數(shù)據(jù)類型。 運用指針編程是C語言最主要的風格之一。利用指針變量可以表示各種數(shù)據(jù)結(jié)構(gòu); ...
    朱森閱讀 3,619評論 3 44
  • 第5章 引用類型(返回首頁) 本章內(nèi)容 使用對象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類型 使用基本類型...
    大學(xué)一百閱讀 3,691評論 0 4
  • 一、字符串操作 strcpy(p, p1) 復(fù)制字符串 strncpy(p, p1, n) 復(fù)制指定長度字符串 s...
    JaiUnChat閱讀 1,753評論 0 7
  • 李笑來常說,七年就是一輩子。 我生于電影的奇跡之年,現(xiàn)在馬上快要23歲。 “這輩子”是我開始職業(yè)成長的第一輩子,也...
    翟幾枝閱讀 443評論 11 2
  • 在職場,有些人經(jīng)常會說些讓別人很難堪的話,同事之間還好,如果你經(jīng)常在領(lǐng)導(dǎo)面前口不擇言,經(jīng)常性的說這幾句話,很可能會...
    無顧有長風閱讀 1,063評論 0 1

友情鏈接更多精彩內(nèi)容