跳到内容

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 编程 下一页:更多数学
华夏公益教科书