跳到內容

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 的字元
\xhh 具有十六進位制值 hh 的字元

寬字元字串

[編輯 | 編輯原始碼]

C 支援寬字元字串,定義為型別為wchar_t的陣列,至少為 16 位值。它們用字串前面的 L 表示,如下所示

wchar_t *p = L"Hello world!";

此功能允許字串,其中需要超過 256 個不同的可能字元 (儘管也可以使用可變長度char字串)。它們以一個零值的wchar_t結尾。這些字串不受<string.h>函式支援。相反,它們有自己的函式,在<wchar.h>.

中宣告

字元編碼

[編輯 | 編輯原始碼]charcharwchar_twchar_t 代表的字元編碼不受 C 標準的限制,只是 0x00 和 0x0000 值表示字串的結尾,而不是字元。直接受到字元編碼影響的是輸入和輸出程式碼。其他程式碼不應該受到太大影響。如果要在原始碼中編寫字串,編輯器也應該能夠處理編碼。

主要有三種類型的編碼

  • 每個字元一個位元組。通常基於 ASCII。最多有 255 個不同的字元,加上零終止字元。
  • 可變長度char字串,允許超過 255 個不同的字元。此類字串以正常char為基礎的陣列編寫。這些編碼通常是基於 ASCII 的,例如 UTF-8Shift JIS
  • 寬字元字串。它們是wchar_t值的陣列。UTF-16 是最常見的此類編碼,它也是可變長度的,這意味著一個字元可以是兩個wchar_t.

<string.h> 標準標頭檔案

[編輯 | 編輯原始碼]

因為程式設計師發現原始字串難以處理,他們編寫了 <string.h> 庫中的程式碼。它不代表一個協同的設計工作,而是多年來各種作者貢獻的累積。

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

  • mem 函式操作任意字元序列,不考慮空字元;
  • str 函式操作以空字元結尾的字元序列;
  • strn 函式操作非空字元序列。

常用的字串函式

[編輯 | 編輯原始碼]

字串庫中九個最常用的函式是

  • strcat - 連線兩個字串
  • strchr - 字串掃描操作
  • strcmp - 比較兩個字串
  • strcpy - 複製一個字串
  • strlen - 獲取字串長度
  • strncat - 連線一個字串和另一個字串的一部分
  • strncmp - 比較兩個字串的部分
  • strncpy - 複製字串的一部分
  • strrchr - 字串掃描操作

其他函式,例如 strlwr (轉換為小寫)、strrev (返回反轉的字串) 和 strupr (轉換為大寫) 可能很受歡迎;但是,它們既不受 C 標準的限制,也不受單一 Unix 標準的限制。這些函式是否返回原始字串的副本或就地轉換字串也是未指定的。

strcat 函式

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

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

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

此函式用於將一個字串附加到另一個字串的末尾。必須保證第一個字串 (s1) 具有儲存兩個字串所需的空間。

注意
對引數的 restrict 要求是在 C99 標準 中新增的。

示例

    #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() 之前,目標必須當前包含一個以空字元結尾的字串,或者第一個字元必須用空字元初始化 (例如 penText[0] = '\0';)。

以下是 strcat 的公共領域實現

 #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;
 }

strchr 函式

[編輯 | 編輯原始碼]
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() 的引數正確,並且目標緩衝區足夠大,則該函式永遠不會失敗。

示例

    #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 的陣列。非零返回值的符號由被比較字串中第一個差異位元組對的值的差值的符號決定。請參閱 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 個位元組首先複製到一個不與 s1s2 指向的物件重疊的 n 位元組臨時陣列中,然後將臨時陣列中的 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)的第一次出現。如果未找到 cmemchr 將返回空指標。

以下是一個公共領域的 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() 函式必須在呼叫之間儲存狀態,並且您不能同時執行兩個標記器,因此 Single Unix Standard 定義了一個類似的函式 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 程式設計 下一頁:更多數學
華夏公益教科書