跳轉到內容

C 程式設計/字串操作

來自華夏公益教科書
前:檔案 I/O C 程式設計 後:進一步數學

C 中的**字串**只是一個字元陣列。字串的長度由終止的空字元決定:'\0'。所以,一個內容為"abc"的字串包含四個字元:'a''b''c'和終止的空字元 ('\0')。

終止的空字元的值為零。


在 C 中,字串常量 (字面值) 用雙引號括起來 ("),例如:"Hello world!"在編譯時,它們會被轉換為指定型別char值的陣列,並在最後附加一個空終止字元 (值為 0) 來標記字串的結束。字串常量的型別是char [].

反斜槓轉義

[編輯 | 編輯原始碼]

字串字面值在原始碼中不能直接包含嵌入的換行符或其他控制字元,也不能包含在字串中具有特殊意義的某些其他字元。

為了在字串中包含這些字元,可以使用反斜槓轉義,例如

轉義 含義
\\ \
\" 字面反斜槓
\' \"
雙引號 \'
單引號 \n
換行符 (換行) \r
回車符 \b
退格鍵 \t
水平製表符 \f
換頁符 \a
\? 警告 (鈴聲)
\\v 垂直製表符
\?問號 (用於轉義 三字母詞) \nnn

八進位制值為 nnn 的字元

\x

hh十六進位制值為 hh 的字元寬字元字串

[編輯 | 編輯原始碼] C 支援寬字元字串,定義為型別

wchar_tchar的陣列,至少為 16 位。它們在字串前面加上一個 L 來寫,例如十六進位制值為 hh 的字元wchar_t *p = L"Helloworld!";此特性允許使用超過 256 個不同字元的字串 (儘管也可以使用可變長度字串)。它們以一個值為零的.

結束。這些字串不受

<string.h>

函式的支援。相反,它們有自己的函式,在char<wchar.h>十六進位制值為 hh 的字元中宣告

字元編碼

  • [編輯 | 編輯原始碼]
  • C 標準沒有規定charchar表示什麼字元編碼,除了值 0x00 和 0x0000 指定字串的結束而不是一個字元。直接受字元編碼影響的是輸入和輸出程式碼。其他程式碼不應該受到太大影響。如果字串要能夠在原始碼中寫入,編輯器也應該能夠處理編碼。
  • 主要有三種類型的編碼十六進位制值為 hh 的字元每個字元一個位元組。通常基於 ASCII。有 255 個不同字元的限制加上一個零終止符。十六進位制值為 hh 的字元.

可變長度

字串,它允許使用超過 255 個不同字元。此類字串寫為普通的

基於陣列。這些編碼通常是基於 ASCII 的,例如 UTF-8Shift JIS.

寬字元字串。它們是

  • 值的陣列。 UTF-16 是最常見的這種編碼,它也是可變長度的,這意味著一個字元可以是兩個
  • <string.h> 標準標頭檔案
  • [編輯 | 編輯原始碼]

因為程式設計師發現原始字串很難處理,他們寫了 <string.h> 庫中的程式碼。它不代表一種精心設計的努力,而是多年來不同作者的貢獻積累的結果。

首先,字串庫中存在三種類型的函式

mem 函式操作任意字元序列,而不考慮空字元;

  • str 函式操作以空字元結尾的字元序列;
  • strn 函式操作非空字元序列。
  • 常用的字串函式
  • [編輯 | 編輯原始碼]
  • 字串庫中最常用的九個函式是
  • strcat - 連線兩個字串
  • strchr - 字串掃描操作
  • strcmp - 比較兩個字串
  • strcpy - 複製一個字串

strlen - 獲取字串長度

strncat - 連線一個字串和另一個字串的一部分

strncmp - 比較兩個字串的一部分
char *strcat(char * restrict s1, const char * restrict s2);

strncpy - 複製字串的一部分

strrchr - 字串掃描操作

其他函式,例如 strlwr (轉換為小寫)、strrev (返回反轉的字串) 和 strupr (轉換為大寫) 可能很流行;但是,它們既沒有在 C 標準中指定,也沒有在 Single Unix Standard 中指定。這些函式是否返回原始字串的副本或就地轉換字串也是未指定的。

strcat 函式
[編輯 | 編輯原始碼]

有些人建議使用 strncat() strlcat() 代替 strcat,以避免緩衝區溢位。

    #include <stdio.h>
    #include <string.h>
    ...
    static const char *colors[] = {"Red","Orange","Yellow","Green","Blue","Purple" };
    static const char *widths[] = {"Thin","Medium","Thick","Bold" };
    ...
    char penText[20];
    ...
    int penColor = 3, penThickness = 2;
    strcpy(penText, colors[penColor]);
    strcat(penText, widths[penThickness]);
    printf("My pen is %s\n", penText); /* prints 'My pen is GreenThick' */

strcat() 函式將把 s2 所指向的字串的副本 (包括終止的空位元組) 追加到 s1 所指向的字串的末尾。s2 的第一個位元組將覆蓋 s1 末尾的空位元組。如果在重疊的物件之間進行復制,行為是未定義的。該函式返回 s1

此函式用於將一個字串附加到另一個字串的末尾。必須確保第一個字串 (s1) 有足夠的空間來儲存這兩個字串。

 #include <string.h>
 /* strcat */
 char *(strcat)(char *restrict s1, const char *restrict s2)
 {
     char *s = s1;
     /* Move s so that it points to the end of s1.  */
     while (*s != '\0')
         s++;
     /* Copy the contents of s2 into the space at the end of s1.  */
     strcpy(s, s2);
     return s1;
 }
char *strchr(const char *s, int c);

strchr() 函式將在由 s 指向的字串中查詢 c(轉換為 char 型別)的第一個出現位置。終止的空位元組被視為字串的一部分。該函式返回找到的字元的位置,如果字元未找到則返回空指標。

此函式用於在字串中查詢特定字元。

在歷史上的某個時刻,此函式名為 indexstrchr 名稱雖然神秘,但符合一般的命名模式。

以下是 strchr 的公有領域實現

 #include <string.h>
 /* strchr */
 char *(strchr)(const char *s, int c)
 {
     char ch = c;
     /* Scan s for the character.  When this loop is finished,
        s will either point to the end of the string or the
        character we were looking for.  */
     while (*s != '\0' && *s != ch)
         s++;
     return (*s == ch) ? (char *) s : NULL;
 }

strcmp 函式

[編輯 | 編輯原始碼]
int strcmp(const char *s1, const char *s2);

字串比較的基本形式由 strcmp() 函式完成。它以兩個字串作為引數,如果第一個字串在字典序上小於第二個字串,則返回一個小於零的值;如果第一個字串在字典序上大於第二個字串,則返回一個大於零的值;如果兩個字串相等,則返回零。比較是透過逐個字元比較編碼(ASCII)值來完成的。

如今,這種簡單的字串比較型別在對字串列表進行排序時通常被認為不可接受。存在更高階的演算法能夠生成以字典順序排序的列表。它們還可以解決 strcmp() 將字串 "Alpha2" 視為大於 "Alpha12" 的問題。(在前面的示例中,"Alpha2" 與 "Alpha12" 的比較結果大於,因為字元集中 '2' 在 '1' 之後。)我們所要表達的是,不要在任何商業或專業程式碼中單獨使用 strcmp() 進行一般的字串排序。

strcmp() 函式將比較由 s1 指向的字串與由 s2 指向的字串。非零返回值的符號由被比較字串中第一對不同位元組(均解釋為 unsigned char 型別)的值之差的符號決定。完成時,strcmp() 將返回一個大於、等於或小於 0 的整數,分別表示由 s1 指向的字串大於、等於或小於由 s2 指向的字串。

由於直接比較指標本身在實際應用中沒有意義(除非比較同一陣列中的指標),因此該函式會按字典順序比較兩個指標指向的字串。

此函式在比較中很有用,例如:

if (strcmp(s, "whatever") == 0) /* do something */
    ;

strcmp() 使用的排序順序等效於機器的本地字元集。唯一保證的順序是數字從'0''9'按順序排列。

以下是 strcmp 的公有領域實現

 #include <string.h>
 /* strcmp */
 int (strcmp)(const char *s1, const char *s2)
 {
     unsigned char uc1, uc2;
     /* Move s1 and s2 to the first differing characters 
        in each string, or the ends of the strings if they
        are identical.  */
     while (*s1 != '\0' && *s1 == *s2) {
         s1++;
         s2++;
     }
     /* Compare the characters as unsigned char and
        return the difference.  */
     uc1 = (*(unsigned char *) s1);
     uc2 = (*(unsigned char *) s2);
     return ((uc1 < uc2) ? -1 : (uc1 > uc2));
 }

strcpy 函式

[編輯 | 編輯原始碼]
char *strcpy(char *restrict s1, const char *restrict s2);

有些人建議始終使用 strncpy() 而不是 strcpy,以避免緩衝區溢位。

strcpy() 函式將把由 s2 指向的 C 字串(包括終止的空位元組)複製到由 s1 指向的陣列中。如果在重疊的物件之間進行復制,則行為未定義。該函式返回 s1。沒有用於指示錯誤的值:如果 strcpy() 的引數正確,並且目標緩衝區足夠大,則該函式永遠不會失敗。

有些人建議使用 strncat() strlcat() 代替 strcat,以避免緩衝區溢位。

    #include <stdio.h>
    #include <string.h>
    /* ... */
    static const char *penType="round";
    /* ... */
    char penText[20];
    /* ... */
    strcpy(penText, penType);

重要提示:您必須確保目標緩衝區 (s1) 能夠容納源陣列中的所有字元,包括終止的空位元組。否則,strcpy() 將覆蓋緩衝區末尾之後的記憶體,導致緩衝區溢位,這會導致程式崩潰,或者被駭客利用以破壞計算機的安全性。

以下是 strcpy 的公有領域實現

 #include <string.h>
 /* strcpy */
 char *(strcpy)(char *restrict s1, const char *restrict s2)
 {
     char *dst = s1;
     const char *src = s2;
     /* Do the copying in a loop.  */
     while ((*dst++ = *src++) != '\0')
         ;               /* The body of this loop is left empty. */
     /* Return the destination string.  */
     return s1;
 }

strlen 函式

[編輯 | 編輯原始碼]
size_t strlen(const char *s);

strlen() 函式將計算由 s 指向的字串中的位元組數,不包括終止的空位元組。它返回字串中的位元組數。沒有用於指示錯誤的值。

以下是 strlen 的公有領域實現

 #include <string.h>
 /* strlen */
 size_t (strlen)(const char *s)
 {
     const char *p = s; /* pointer to character constant */
     /* Loop over the data in s.  */
     while (*p != '\0')
         p++;
     return (size_t)(p - s);
 }

注意以下行

  const char *p = s

宣告並初始化一個指向整數常量的指標 p,即 p 不能更改它指向的值。

strncat 函式

[編輯 | 編輯原始碼]
char *strncat(char *restrict s1, const char *restrict s2, size_t n);

strncat() 函式將從由 s2 指向的陣列末尾追加不超過 n 個位元組(空位元組及其後的位元組不追加)到由 s1 指向的字串末尾。s2 的第一個位元組覆蓋 s1 末尾的空位元組。始終將終止的空位元組追加到結果。如果在重疊的物件之間進行復制,則行為未定義。該函式返回 s1

以下是 strncat 的公有領域實現

 #include <string.h>
 /* strncat */
 char *(strncat)(char *restrict s1, const char *restrict s2, size_t n)
 {
     char *s = s1;
     /* Loop over the data in s1.  */
     while (*s != '\0')
         s++;
     /* s now points to s1's trailing null character, now copy
        up to n bytes from s2 into s stopping if a null character
        is encountered in s2.
        It is not safe to use strncpy here since it copies EXACTLY n
        characters, NULL padding if necessary.  */
     while (n != 0 && (*s = *s2++) != '\0') {
         n--;
         s++;
     }
     if (*s != '\0')
         *s = '\0';
     return s1;
 }

strncmp 函式

[編輯 | 編輯原始碼]
int strncmp(const char *s1, const char *s2, size_t n);

strncmp() 函式將比較不超過 n 個位元組(空位元組後的位元組不比較)從由 s1 指向的陣列到由 s2 指向的陣列。非零返回值的符號由被比較字串中第一對不同位元組(均解釋為 unsigned char 型別)的值之差的符號決定。有關返回值的說明,請參見 strcmp

此函式在比較中很有用,就像 strcmp 函式一樣。

以下是 strncmp 的公有領域實現

 #include <string.h>
 /* strncmp */
 int (strncmp)(const char *s1, const char *s2, size_t n)
 {
     unsigned char uc1, uc2;
     /* Nothing to compare?  Return zero.  */
     if (n == 0)
         return 0;
     /* Loop, comparing bytes.  */
     while (n-- > 0 && *s1 == *s2) {
         /* If we've run out of bytes or hit a null, return zero
            since we already know *s1 == *s2.  */
         if (n == 0 || *s1 == '\0')
             return 0;
         s1++;
         s2++;
     }
     uc1 = (*(unsigned char *) s1);
     uc2 = (*(unsigned char *) s2);
     return ((uc1 < uc2) ? -1 : (uc1 > uc2));
 }

strncpy 函式

[編輯 | 編輯原始碼]
char *strncpy(char *restrict s1, const char *restrict s2, size_t n);

strncpy() 函式將從由 s2 指向的陣列複製不超過 n 個位元組(空位元組後的位元組不復制)到由 s1 指向的陣列。如果在重疊的物件之間進行復制,則行為未定義。如果由 s2 指向的陣列是一個字串,其長度小於 n 個位元組,則將在由 s1 指向的副本中追加空位元組,直到寫入所有 n 個位元組。該函式將返回 s1;沒有返回值被保留用於指示錯誤。

該函式可能不會返回一個以空字元結尾的字串,這種情況發生在 s2 字串的長度大於 n 個位元組時。

以下是 strncpy 的公有領域版本

 #include <string.h>
 /* strncpy */
 char *(strncpy)(char *restrict s1, const char *restrict s2, size_t n)
 {
     char *dst = s1;
     const char *src = s2;
     /* Copy bytes, one at a time.  */
     while (n > 0) {
         n--;
         if ((*dst++ = *src++) == '\0') {
             /* If we get here, we found a null character at the end
                of s2, so use memset to put null bytes at the end of
                s1.  */
             memset(dst, '\0', n);
             break;
         }
     }
     return s1;
 }

strrchr 函式

[編輯 | 編輯原始碼]
char *strrchr(const char *s, int c);

strrchr 函式類似於 strchr 函式,區別在於 strrchr 返回一個指向 sc最後一個出現位置的指標,而不是第一個出現位置。

strrchr() 函式將在由 s 指向的字串中查詢 c(轉換為 char 型別)的最後一個出現位置。終止的空位元組被視為字串的一部分。它的返回值類似於 strchr 的返回值。

在歷史上的某個時刻,此函式名為 rindexstrrchr 名稱雖然神秘,但符合一般的命名模式。

以下是 strrchr 的公有領域實現

 #include <string.h>
 /* strrchr */
 char *(strrchr)(const char *s, int c)
 {
     const char *last = NULL;
     /* If the character we're looking for is the terminating null,
        we just need to look for that character as there's only one
        of them in the string.  */
     if (c == '\0')
         return strchr(s, c);
     /* Loop through, finding the last match before hitting NULL.  */
     while ((s = strchr(s, c)) != NULL) {
         last = s;
         s++;
     }
     return (char *) last;
 }

較少使用的字串函式

[編輯 | 編輯原始碼]

較少使用的函式是

  • memchr - 在記憶體中查詢位元組
  • memcmp - 比較記憶體中的位元組
  • memcpy - 複製記憶體中的位元組
  • memmove - 複製記憶體中重疊區域的位元組
  • memset - 設定記憶體中的位元組
  • strcoll - 根據特定於區域設定的排序順序比較位元組
  • strcspn - 獲取互補子字串的長度
  • strerror - 獲取錯誤訊息
  • strpbrk - 在字串中掃描位元組
  • strspn - 獲取子字串的長度
  • strstr - 查詢子字串
  • strtok - 將字串拆分為標記
  • strxfrm - 轉換字串

複製函式

[編輯 | 編輯原始碼]
memcpy 函式
[編輯 | 編輯原始碼]
void *memcpy(void * restrict s1, const void * restrict s2, size_t n);

memcpy() 函式將從由 s2 指向的物件複製 n 個位元組到由 s1 指向的物件。如果在重疊的物件之間進行復制,則行為未定義。該函式返回 s1

由於該函式不必擔心重疊問題,因此它可以執行最簡單的複製操作。

以下是 memcpy 的公有領域實現

 #include <string.h>
 /* memcpy */
 void *(memcpy)(void * restrict s1, const void * restrict s2, size_t n)
 {
     char *dst = s1;
     const char *src = s2;
     /* Loop and copy.  */
     while (n-- != 0)
         *dst++ = *src++;
     return s1;
 }
memmove 函式
[編輯 | 編輯原始碼]
void *memmove(void *s1, const void *s2, size_t n);

memmove() 函式將從 s2 指向的物件中複製 n 個位元組到 s1 指向的物件中。複製過程就像將 s2 指向的物件中的 n 個位元組先複製到一個大小為 n 位元組的臨時陣列中,該陣列與 s1s2 指向的物件沒有重疊,然後將臨時陣列中的 n 個位元組複製到 s1 指向的物件中。該函式返回 s1 的值。

在不使用臨時陣列的情況下實現此功能的簡單方法是檢查是否有條件阻止升序複製,如果有,則執行降序複製。

以下是 memmove 的公共領域實現,儘管並不完全可移植。

 #include <string.h>
 /* memmove */
 void *(memmove)(void *s1, const void *s2, size_t n) 
 {
    /* note: these don't have to point to unsigned chars */
    char *p1 = s1;
    const char *p2 = s2;
    /* test for overlap that prevents an ascending copy */
    if (p2 < p1 && p1 < p2 + n) {
        /* do a descending copy */
        p2 += n;
        p1 += n;
        while (n-- != 0) 
            *--p1 = *--p2;
    } else 
        while (n-- != 0) 
            *p1++ = *p2++;
    return s1; 
 }

比較函式

[編輯 | 編輯原始碼]
memcmp 函式
[編輯 | 編輯原始碼]
int memcmp(const void *s1, const void *s2, size_t n);

memcmp() 函式將比較 s1 指向的物件的前 n 個位元組(每個位元組被解釋為 unsigned char)和 s2 指向的物件的前 n 個位元組。非零返回值的符號將由被比較物件中第一個不同的位元組對的值的符號(均解釋為 unsigned char 型別)決定。

以下是 memcmp 的公共領域實現。

 #include <string.h>
 /* memcmp */
 int (memcmp)(const void *s1, const void *s2, size_t n)
 {
     const unsigned char *us1 = (const unsigned char *) s1;
     const unsigned char *us2 = (const unsigned char *) s2;
     while (n-- != 0) {
         if (*us1 != *us2)
             return (*us1 < *us2) ? -1 : +1;
         us1++;
         us2++;
     }
     return 0;
 }
strcollstrxfrm 函式
[編輯 | 編輯原始碼]
int strcoll(const char *s1, const char *s2);

size_t strxfrm(char *s1, const char *s2, size_t n);

ANSI C 標準指定了兩個與區域設定相關的比較函式。

strcoll 函式將 s1 指向的字串與 s2 指向的字串進行比較,兩者都按照當前區域設定的 LC_COLLATE 類別進行解釋。返回值類似於 strcmp

strxfrm 函式將 s2 指向的字串進行轉換,並將結果字串放置到 s1 指向的陣列中。轉換方式使得如果將 strcmp 函式應用於兩個轉換後的字串,它返回的值大於、等於或小於零,分別對應於將 strcoll 函式應用於相同的兩個原始字串的結果。放置到 s1 指向的結果陣列中的字元數不超過 n 個,包括終止空字元。如果 n 為零,則 s1 允許為一個空指標。如果在重疊的物件之間進行復制,則行為未定義。該函式返回轉換後的字串的長度。

這些函式很少使用,並且程式碼實現起來並不容易,因此本節沒有程式碼。

搜尋函式

[編輯 | 編輯原始碼]
memchr 函式
[編輯 | 編輯原始碼]
void *memchr(const void *s, int c, size_t n);

memchr() 函式將在 s 指向的物件的前 n 個位元組(每個位元組解釋為 unsigned char)中查詢 c(轉換為 unsigned char)的第一個出現位置。如果未找到 c,則 memchr 返回一個空指標。

以下是 memchr 的公共領域實現。

 #include <string.h>
 /* memchr */
 void *(memchr)(const void *s, int c, size_t n)
 {
     const unsigned char *src = s;
     unsigned char uc = c;
     while (n-- != 0) {
         if (*src == uc)
             return (void *) src;
         src++;
     }
     return NULL;
 }
strcspnstrpbrkstrspn 函式
[編輯 | 編輯原始碼]
size_t strcspn(const char *s1, const char *s2);
char *strpbrk(const char *s1, const char *s2);
size_t strspn(const char *s1, const char *s2);

strcspn 函式計算 s1 指向的字串的最大初始段的長度,該段完全由 來自 s2 指向的字串的字元組成。

strpbrk 函式在 s1 指向的字串中查詢 s2 指向的字串中任何字元的第一個出現位置,如果找到則返回指向該字元的指標,否則返回空指標。

strspn 函式計算 s1 指向的字串的最大初始段的長度,該段完全由來自 s2 指向的字串的字元組成。

所有這些函式除了測試和返回值之外,其他方面都類似。

以下是 strcspnstrpbrkstrspn 的公共領域實現。

 #include <string.h>
 /* strcspn */
 size_t (strcspn)(const char *s1, const char *s2)
 {
     const char *sc1;
     for (sc1 = s1; *sc1 != '\0'; sc1++)
         if (strchr(s2, *sc1) != NULL)
             return (sc1 - s1);
     return sc1 - s1;            /* terminating nulls match */
 }
 #include <string.h>
 /* strpbrk */
 char *(strpbrk)(const char *s1, const char *s2)
 {
     const char *sc1;
     for (sc1 = s1; *sc1 != '\0'; sc1++)
         if (strchr(s2, *sc1) != NULL)
             return (char *)sc1;
     return NULL;                /* terminating nulls match */
 }
 #include <string.h>
 /* strspn */
 size_t (strspn)(const char *s1, const char *s2)
 {
     const char *sc1;
     for (sc1 = s1; *sc1 != '\0'; sc1++)
         if (strchr(s2, *sc1) == NULL)
             return (sc1 - s1);
     return sc1 - s1;            /* terminating nulls don't match */
 }
strstr 函式
[編輯 | 編輯原始碼]
char *strstr(const char *haystack, const char *needle);

strstr() 函式將在 haystack 指向的字串中查詢 needle 指向的字串中位元組序列(不包括終止空位元組)的第一個出現位置。該函式返回指向 haystack 中匹配字串的指標,如果未找到匹配項,則返回空指標。如果 needle 是一個空字串,則該函式返回 haystack

以下是 strstr 的公共領域實現。

 #include <string.h>
 /* strstr */
 char *(strstr)(const char *haystack, const char *needle)
 {
     size_t needlelen;
     /* Check for the null needle case.  */
     if (*needle == '\0')
         return (char *) haystack;
     needlelen = strlen(needle);
     for (; (haystack = strchr(haystack, *needle)) != NULL; haystack++)
         if (memcmp(haystack, needle, needlelen) == 0)
             return (char *) haystack;
     return NULL;
 }
strtok 函式
[編輯 | 編輯原始碼]
char *strtok(char *restrict s1, const char *restrict delimiters);

strtok() 的一系列呼叫將 s1 指向的字串分解為一系列令牌,每個令牌都由 delimiters 指向的字串中的一個位元組分隔。序列中的第一個呼叫使用 s1 作為其第一個引數,隨後呼叫使用空指標作為其第一個引數。delimiters 指向的分隔符字串在每次呼叫之間可以不同。

序列中的第一個呼叫在 s1 指向的字串中搜索第一個不包含在當前分隔符字串(delimiters 指向)中的位元組。如果未找到這樣的位元組,則 s1 指向的字串中沒有令牌,strtok() 將返回一個空指標。如果找到這樣的位元組,則它是第一個令牌的開始。

然後,strtok() 函式從那裡開始搜尋包含在當前分隔符字串中的位元組(或多個連續位元組)。如果未找到這樣的位元組,則當前令牌擴充套件到 s1 指向的字串的末尾,並且後續搜尋令牌將返回一個空指標。如果找到這樣的位元組,則將其覆蓋為空位元組,這將終止當前令牌。strtok() 函式儲存指向下一個位元組的指標,後續搜尋令牌將從此指標開始。

每個後續呼叫(第一個引數的值為空指標)從儲存的指標開始搜尋,並按上述方式進行操作。

strtok() 函式不需要可重入。不需要可重入的函式也不需要執行緒安全。

由於 strtok() 函式必須在呼叫之間儲存狀態,並且不能同時進行兩個分詞器,因此單一 UNIX 標準定義了一個類似的函式 strtok_r(),該函式不需要儲存狀態。其原型如下所示。

char *strtok_r(char *s, const char *delimiters, char **lasts);

strtok_r() 函式將空終止字串 s 視為一系列零個或多個文字令牌,這些令牌由來自分隔符字串 delimiters 的一個或多個字元跨度隔開。引數 lasts 指向一個使用者提供的指標,該指標指向 strtok_r() 繼續掃描相同字串所需儲存的資訊。

在對 strtok_r() 的第一次呼叫中,s 指向一個空終止字串,delimiters 指向一個空終止的分隔符字串,並且 lasts 指向的值被忽略。strtok_r() 函式將返回指向第一個令牌的第一個字元的指標,將一個空字元寫入 s 中緊跟在返回的令牌之後的位元組,並更新 lasts 指向的指標。

在隨後的呼叫中,s 為空指標,並且 lasts 應與前一次呼叫保持不變,以便隨後的呼叫將遍歷字串 s,返回連續的令牌,直到沒有令牌剩餘。分隔符字串 delimiters 在每次呼叫之間可以不同。當 s 中沒有令牌剩餘時,將返回 NULL 指標。

以下 strtokstrtok_r 的公共領域程式碼將前者編碼為後者的一個特例。

 #include <string.h>
 /* strtok_r */
 char *(strtok_r)(char *s, const char *delimiters, char **lasts)
 {
     char *sbegin, *send;
     sbegin = s ? s : *lasts;
     sbegin += strspn(sbegin, delimiters);
     if (*sbegin == '\0') {
         *lasts = "";
         return NULL;
     }
     send = sbegin + strcspn(sbegin, delimiters);
     if (*send != '\0')
         *send++ = '\0';
     *lasts = send;
     return sbegin;
 }
 /* strtok */
 char *(strtok)(char *restrict s1, const char *restrict delimiters)
 {
     static char *ssave = "";
     return strtok_r(s1, delimiters, &ssave);
 }

其他函式

[編輯 | 編輯原始碼]

這些函式不屬於上述任何類別。

memset 函式
[編輯 | 編輯原始碼]
void *memset(void *s, int c, size_t n);

memset() 函式將 c 轉換為 unsigned char,然後將該字元儲存到 s 指向的記憶體的前 n 個位元組中。

以下是 memset 的公有領域實現

 #include <string.h>
 /* memset */
 void *(memset)(void *s, int c, size_t n)
 {
     unsigned char *us = s;
     unsigned char uc = c;
     while (n-- != 0)
         *us++ = uc;
     return s;
 }
strerror 函式
[編輯 | 編輯原始碼]
char *strerror(int errorcode);

此函式返回與引數相對應的特定於區域設定的錯誤訊息。根據具體情況,此函式可能很簡單,但作者不會這樣做,因為它會有所不同。

Single Unix System 版本 3 具有一個變體 strerror_r,其原型為

int strerror_r(int errcode, char *buf, size_t buflen);

此函式將訊息儲存在 buf 中,其長度為 buflen

要確定字串中的字元數,可以使用 strlen() 函式。

    #include <stdio.h>
    #include <string.h>
    ...
    int length, length2;
    char *turkey;
    static char *flower= "begonia";
    static char *gemstone="ruby ";
    
    length = strlen(flower);
    printf("Length = %d\n", length); // prints 'Length = 7'
    length2 = strlen(gemstone);
    
    turkey = malloc( length + length2 + 1);
    if (turkey) {
      strcpy( turkey, gemstone);
      strcat( turkey, flower);
      printf( "%s\n", turkey); // prints 'ruby begonia'
      free( turkey );
    }

請注意,為“turkey”分配的記憶體大小為要連線的字串長度之和加一。這是為了終止空字元,它不在字串的長度中計算。

  1. 字串函式使用大量的迴圈結構。有沒有辦法可移植地解開這些迴圈?
  2. 現在庫中可能缺少哪些函式?
前:檔案 I/O C 程式設計 後:進一步數學
華夏公益教科書