跳转到内容

C 编程/字符串操作

来自维基教科书,开放世界中的开放书籍
(从 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 编程 后:进一步数学
华夏公益教科书