跳转到内容

C 编程/string.h/函数参考

来自维基教科书,开放的书籍,开放的世界


#include <string.h>
void *memset(void *s, int c, size_t n);

memset()c 复制到 s 指向的对象的前 n 个字节中。

返回值

[编辑 | 编辑源代码]

该函数返回指针 s。当发生错误时,没有定义的返回值。

在计算中,C 编程语言提供了一个名为 strcat 的库函数,允许将一个内存块附加到另一个内存块。这两个内存块都需要以 null 结尾。因为在 C 中,字符串不是一等公民数据类型,而是作为内存中的 ASCII 字节块实现的,strcat将有效地将一个字符串附加到另一个字符串,给定指向两个已分配内存块的指针。名称strcat是“字符串连接”的缩写。strcat可以在string.h头文件中找到。

例如

char str1[14] = "Hello,";    /* The array has enough space for 'Hello,' plus " world!" plus a null terminator */
strcat(str1, " world!");
puts(str1);                 /* prints "Hello, world!" to stdout followed by a newline */

以下是可能实现的strcat:

char *
strcat(char *dest, const char *src)
{
    size_t i,j;
    for (i = 0; dest[i] != '\0'; i++)
        ;
    for (j = 0; src[j] != '\0'; j++)
        dest[i+j] = src[j];
    dest[i+j] = '\0';
    return dest;
}

它也可以用其他字符串库函数来定义

char *
strcat(char *dest, const char *src)
{
    strcpy(dest + strlen(dest), src);
    return dest;
}

边界错误

[编辑 | 编辑源代码]

strcat可能是危险的,因为如果要附加的字符串太长,无法放入目标缓冲区,它将覆盖相邻的内存,从而引发未定义的行为。通常程序在这种情况下只会导致段错误,但熟练的攻击者可以使用这种缓冲区溢出攻击来侵入系统(参见计算机安全)。

边界检查变体

[编辑 | 编辑源代码]

为了防止缓冲区溢出,已经使用了多种替代strcat的方法。它们都接受一个额外的参数,该参数对目标缓冲区的长度进行编码,并且不会写入该缓冲区末尾。如果提供了错误的长度,它们都可能导致缓冲区溢出。

char* strncat(char* dst, const char* src, size_t n);

最常见的边界变体,strncat,只附加指定数量的字节,加上一个 NULL 字节。这允许每个连接的字符串使用不超过其在缓冲区中的“份额”,也许是用来制作表格的。它不适合更常见的需求,即获得适合缓冲区的连接字符串的前缀。为此,要传递的计数的正确值是bufferSize-strlen(buffer)-1。常见的错误是传递bufferSize, bufferSize-1bufferSize-strlen(buffer),这些都会导致缓冲区溢出。

size_t strlcat(char* dst, const char* src, size_t size);

strlcat函数由 OpenBSD 开发人员 Todd C. Miller 和 Theo de Raadt 创建,通常被认为是strncat的更安全、更有用的版本。它接受缓冲区的实际长度作为参数,并返回所需的字节数,允许调用者根据需要重新分配缓冲区。它已被移植到许多操作系统,但被 glibc 维护者明确拒绝,他们认为 C 程序员需要跟踪字符串长度,并且“使用此函数只会导致其他错误”。[1]

errno_t strcat_s(char* dst, rsize_t size, const char* src);

strcat_s 函数在 ISO/IEC TR 24731 中提出标准化。[2][3] 受 Microsoft C 运行时库支持。[4] 以及其他一些 C 库。如果源字符串不适合,它会返回非零值,并将缓冲区设置为空字符串(如果原始字符串未存储在其他位置或调用者忽略返回值,则这是一个灾难性的结果)。它还被一些库明确不支持,包括 GLibc 库。[5] Microsoft 编译器发出的警告消息,建议程序员将strcatstrncat更改为此函数,据一些人推测是微软试图将开发者锁定在其平台上。[6][7]

[编辑 | 编辑源代码]

C 和 C++ 的函数名称 strchr()

语法
include <string.h>
char *strchr(const char *s, int c);

描述
strchr() 函数在 s 指向的字符串中查找 c 的第一个出现,转换为 char。终止 null 字符被认为是字符串的一部分。

参数
s 指向要搜索的字符串。

c 是要在字符串 s 中搜索的字符。

返回值
strchr() 函数返回指向 s 内找到的字符 c 的第一个出现的指针。如果字符串中没有出现字符 c,strchr() 返回一个空指针。

在 POSIX 和 C 编程语言中,strcmp 是 C 标准库中的一个函数(在 string.h 中声明),用于比较两个 C 字符串。

根据 ISO/IEC 9899:1999,7.21.4.2 的原型

int strcmp(const char *s1, const char *s2);

当字符串相等时,strcmp 返回 0;当 s1 小于 s2 时,返回负整数;当 s1 大于 s2 时,返回正整数,根据字典序。

strcmp 的一个变体名为 strncmp,它只比较字符串到一定偏移量。

另一个变体 strcasecmp 符合 POSIX.1-2001,工作方式类似于 strcmp,但区分大小写。一些系统反而用名为 stricmpstrcmpi 的函数提供此功能。为了区分大小写地比较两个字符串的子集,各种系统可能会提供 strncasecmpstrnicmpstrncmpi

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main (int argc, char **argv)
{
    int v;

    if (argc < 3)
    {
        fprintf (stderr, "Compares two strings\nUsage: %s string1 string2\n",argv[0]);
        return EXIT_FAILURE;
    }

    v = strcmp (argv[1], argv[2]);

    if (v < 0)
        printf ("'%s' is less than '%s'.\n", argv[1], argv[2]);
    else if (v == 0)
        printf ("'%s' equals '%s'.\n", argv[1], argv[2]);
    else if (v > 0)
        printf ("'%s' is greater than '%s'.\n", argv[1], argv[2]);

    return 0;
}

上面的代码是一个工作示例,它打印第一个参数是否小于、等于或大于第二个参数。

可能的实现是(P.J. Plauger,The Standard C Library,1992)

int strcmp (const char * s1, const char * s2)
{
    for(; *s1 == *s2; ++s1, ++s2)
        if(*s1 == 0)
            return 0;
    return *(unsigned char *)s1 < *(unsigned char *)s2 ? -1 : 1;
}

但是,大多数现实世界的实现将具有各种优化技巧来减少函数的执行时间。你会注意到,strcmp 不仅返回 -1、0 和 +1,还会返回其他负值或正值,这是由于优化掉了由 ?: 运算符引入的分支。

return *(const unsigned char *)s1 - *(const unsigned char *)s2;
[编辑 | 编辑源代码]

C 编程语言提供了一个名为 strcpy 的库函数,在 string.h 头文件中定义,它允许将以 null 结尾的内存块从一个位置复制到另一个位置。由于 C 中的字符串不是一等数据类型,而是作为内存中的连续字节块来实现,strcpy将有效地复制给定指向两个已分配内存块的指针的字符串。

函数的原型是:[8]

char *strcpy(char *destination, const char *source);

参数顺序模仿赋值:目标“=”源。返回值是 目标

用法和实现

[编辑 | 编辑源代码]

例如

char *str1 = "abcdefghijklmnop";
char *str2 = malloc(100); /* must be large enough to hold the string! */
strcpy(str2, str1); /* str2 is now "abcdefghijklmnop" */
str2[0] = 'A'; /* str2 is now "Abcdefghijklmnop" */
/* str1 is still "abcdefghijklmnop" */

在第二行,内存被分配来保存字符串的副本,然后字符串从一个内存块复制到另一个内存块,然后该副本的第一个字母被修改。

虽然简单的赋值str2 = str1似乎可以做同样的事情,它只复制了str1的内存地址str2str1中,但不是实际的字符串。两者都将引用相同的内存块,以前由

strcpy指向的分配块将丢失。对 str2[0] 的赋值也会修改 str1,或者会导致访问冲突(因为现代编译器通常将字符串常量放在只读内存中)。strcpy函数通过遍历字符串的各个字符并将它们逐个复制来执行复制。的显式实现是

char *strcpy(char *dest, const char *src)
{
  unsigned i;
  for (i=0; src[i] != '\0'; ++i)
    dest[i] = src[i];

  //Ensure trailing null byte is copied
  dest[i]= '\0';

  return dest;
}

一个常见的紧凑实现是

char *strcpy(char *dest, const char *src)
{
   char *save = dest;
   while(*dest++ = *src++);
   return save;
}

C 库提供的现代版本通常一次复制多个字节,依靠位数学来检测较大的字在写入之前是否包含空字节。通常,调用会编译成专门用于执行strcpy.

strcpy将适用于 Unicode 字符串的所有常见字节编码,包括 UTF-8。只要空字节不被其使用,就无需真正知道编码。

如果 Unicode 以大于一个字节的单位编码,例如 UTF-16,则需要不同的函数,因为空字节将出现在更大的代码单元的一部分中。C99 定义了函数wcscpy(),它将复制wchar_t大小的对象,并在遇到第一个值为零的对象时停止。这不像看起来那么有用,因为不同的计算机平台在如何设置wchar_t的大小(有些使用 16 位,有些使用 32 位)方面存在分歧。

缓冲区溢出

[编辑 | 编辑源代码]

strcpy可能很危险,因为如果要复制的字符串太长而无法放入目标缓冲区,它将覆盖相邻的内存,导致未定义的行为。通常,程序在这种情况下只会导致分段错误,但熟练的攻击者可以使用缓冲区溢出来入侵系统。为了防止缓冲区溢出,已经使用了几种替代方案strcpy。它们都接受一个额外的参数,该参数是目标缓冲区的长度,并且不会写入该缓冲区末尾。如果提供了不正确的长度,它们仍然会导致缓冲区溢出。

char* strncpy(char* dst, const char* src, size_t size);

strncpy写入了给定的字节数,如果字符串太长,则只复制字符串的开头,或者在副本的末尾添加零来填充缓冲区。它被引入 C 库是为了处理目录条目等结构中的固定长度名称字段。尽管它的名字是strcpy,但它不是的边界版本;它不保证结果是以 null 结尾的字符串。函数的名称具有误导性,因为strncatsnprintf 的边界版本,分别是strcatsprintf.

假设结果是以 null 结尾的字符串会导致两个问题。如果源字符串太长,则结果不是以 null 结尾的,这使得缓冲区末尾之后的数据看起来像是字符串的一部分。如果源字符串比缓冲区短得多,则将浪费大量时间将剩余的缓冲区填充为 null 字节。

标准 C 库中一个始终追加一个 null 字节的替代方法是使用 strncat,并将一个最初为空的字符串作为目标。

size_t strlcpy(char* dst, const char* src, size_t size);

strlcpy函数由 OpenBSD 开发人员 Todd C. Miller 和 Theo de Raadt 创建,通常被认为是strncpy的更安全版本。它始终添加一个 null 字节,并返回所需的字节数,允许调用者重新分配缓冲区(如果可能)。它已被移植到许多操作系统,但最值得注意的是被 glibc 维护者 Ulrich Drepper 拒绝,他建议 C 程序员需要跟踪字符串长度,并且“使用此函数只会导致其他错误”。[1]

errno_t strcpy_s(char* dst, rsize_t size, const char* src);

strcpy_s 函数在 ISO/IEC TR 24731 中被提议用于标准化。[9][10] 它受 Microsoft C 运行时库[11] 和其他一些 C 库的支持。如果源字符串不适合,它将返回非零值,并将缓冲区设置为空字符串(而不是前缀!)。它也明确不受一些库的支持,包括 GNU C 库。[12] 一些人推测,Microsoft 编译器发出的建议程序员将strcpystrncpy更改为该函数的消息是 Microsoft 试图将开发人员锁定在其平台上。[13][14]

[编辑 | 编辑源代码]

strcspn 是来自 C 标准库(头文件 string.h)的函数。

它在字符串中搜索特定的一组字符。

strcspn() 函数计算字符串 1 的初始段的长度,该段不包含字符串 2 中的任何字符。

返回值

[edit | edit source]

该函数返回字符串 1 中第一个与字符串 2 中的任何字符匹配的字符的索引。

语法

[edit | edit source]
#include <string.h>

size_t strcspn( const char *str1, const char *str2 );

示例

[edit | edit source]
#include <stdio.h>
#include <string.h>

int main(){
  char s[20] = "wikireader007", t[11] = "0123456789";
  printf("The first decimal digit of s is at position: %d.\n", '''strcspn'''(s, t));

  return 0;
}

输出:

s 的第一个十进制数字位于位置:10

[edit | edit source]

strerror

[edit | edit source]

字符串错误函数 strerror 是一个 C/C++ 函数,它将错误代码(通常存储在全局变量 errno 中)转换为人类可读的错误消息。

历史

[edit | edit source]

strerror 函数在 IEEE Std 1003.1(也称为 POSIX 1)中定义。

可重入性

[edit | edit source]

strerror 函数不可重入。有关该函数的可重入版本,请参见 strerror r。

用法

[edit | edit source]

包含

[edit | edit source]
#include <string.h>

声明

[edit | edit source]
char* strerror(int errnum);

语义

[edit | edit source]

该函数生成并报告一个 C 样式字符串,其中包含从与 errnum 传递的错误代码派生的错误消息。

参考

[edit | edit source]
  1. strerror by OpenGroup
[edit | edit source]

strlen

[edit | edit source]

在 C 标准库中,strlen 是一个字符串函数,它确定 C 字符串的长度。

示例用法

[edit | edit source]
#include <stdio.h>
#include <string.h>
 
int main()
{
    char *string = "Hello World";
    printf("%lu\n", (unsigned long)strlen(string));
    return 0;
}

此程序将打印值 11,它是字符串 "Hello World" 的长度。字符字符串存储在称为char的数据类型的数组中。字符串的结尾通过搜索数组中的第一个空字符来找到。

重要的是要注意,此长度 *不* 包含 C 字符串的结束字符所需的尾随空字节的数组条目。因此,如果您需要复制 C 字符串,则需要分配 strlen() + 1 的空间。

实施

[edit | edit source]

FreeBSD 6.2 实现strlen如下所示:[15]

size_t strlen(const char * str)
{
    const char *s;
    for (s = str; *s; ++s) {}
    return(s - str);
}

可以在 C 中编写更快的版本,这些版本检查完整的机器字而不是逐字节地检查。Hacker's Delight 给出了一个算法,该算法利用按位运算来检测这些字节中的任何一个是否为空 ('\0')。当前的 FreeBSD 实现就是这样做的。 [16]

现代 C 编译器通常提供strlen的快速内联版本,这些版本使用汇编编写,使用按位运算技术或某些 CISC 处理器提供的特殊指令。此外,strlen引用的字符串常量的长度通常优化为常数整数。

[edit | edit source]

strrchr

[edit | edit source]

strrchrstring.h 中的函数。[17] 它主要用于从末尾开始搜索字符串中字符的最后一次出现。它返回指向字符串中字符最后一次出现的指针。结束空字符被认为是 C 字符串的一部分。因此,它也可以被定位以检索指向字符串末尾的指针。

语法

[edit | edit source]

在 C 中,此函数被声明为

char *strrchr ( const char *, int );

str 是一个 C 字符串。character 是要定位的字符。它作为其 int 提升传递,但它在内部被转换回 char。

返回值

[edit | edit source]

指向 str 中 character 最后一次出现的指针。如果未找到该值,该函数将返回一个 空指针

示例

[edit | edit source]
#include <stdio.h>
#include <string.h>

int main(void)
{
    const char *str = "This is a sample string";
    char *pch = strrchr(str, 's');
    printf("Last occurrence of 's' found at %d\n", pch - str + 1);
    return 0;
}

输出:'s' 的最后一次出现位于 18。

另请参阅

[edit | edit source]

strchr

[edit | edit source]

strspn

[edit | edit source]

strspn() 函数用于找出子字符串的长度或由一个字符串指向的字符串(例如 s1)的最大初始段的长度,该初始段包含来自另一个由另一个字符串(例如 s2)指向的字符串的所有字符,而 strscpn() 用于发现包含不在拒绝列表中的所有元素的初始段的长度。

size_t strspn(const char *s, const char *accept);

size_t strcspn(const char *s, const char *reject);

s1 - 指向要搜索的以空字符结尾的字符串。

s2 - 指向以空字符结尾的字符集。

strstr是 C 标准库字符串函数,定义在string.h. strstr()具有以下函数签名:char * strstr(const char *haystack, const char *needle);它返回指向第一个索引处的字符的指针,该索引处needlehaystack中,如果不存在,则返回 NULL。[18]

strcasestr()是一个与strstr()非常相似的函数,它忽略了needlehaystack. strcasestr()是一个非标准函数,而strstr()符合 C89 和 C99。[18]

#include <stdio.h>
#include <string.h>

int main(void) 
{
	/* Define a pointer of type char, a string and the substring to be found*/
	char *cptr;
	char str[] = "Wikipedia, be bold";
	char substr[] = "edia, b";

	/* Find memory address where substr "edia, b" is found in str */
	cptr = strstr(str, substr);

	/* Print out the character at this memory address, i.e. 'e' */
	printf("%c\n", *cptr);

	/* Print out "edia, be bold" */
	printf("%s\n", cptr);
	
	return 0;
}

cptr现在是指向第六个字母(e)的指针,位于 "wikipedia" 中。

[编辑 | 编辑源代码]

strtok 是 C 库函数之一。strtok() 函数的语法如下:语法

#include <string.h>
char *strtok( char *str1, const char *str2 );

描述:strtok() 函数返回 str1 中下一个“标记”的指针,其中 str2 包含确定标记的分隔符。如果找不到标记,则 strtok() 返回 NULL。为了将字符串转换为标记,第一次调用 strtok() 应使 str1 指向要标记化的字符串。所有此后的调用都应使 str1 为 NULL。

strxfrm 是 C 标准库字符串函数,在 string.h 中声明。它根据当前区域设置转换字符串。

此函数的原型为

  size_t strxfrm(char *str1 , const char *str2 , size_t num);

是接收 num 个字符的转换字符串的字符串。如果 num 等于零,则 str1 仅包含空字符。

是要转换的字符串。

是要复制到 str1 中的最大字符数。

strxfrm() 函数根据当前区域设置转换 str2。为此,使用定义在 locale.h 中的 LC_COLLATE 类别。转换后,转换后的字符串的前 num 个字符被复制到 str1 中。strxfrm() 函数执行转换,以使对两个字符串的 strcmp 结果与对两个原始字符串的 strcoll 结果相同。

返回值

[编辑 | 编辑源代码]

strxfrm() 函数返回转换后的字符串的长度,不包括终止空字符。

示例用法

[编辑 | 编辑源代码]
#include <stdio.h>
#include <string.h>
 
int main(void)
{
    char str2[] = "Hello World";
    char str1[];
    printf("The length of str2 = %d\n",strxfrm(str1, str2, 4));
    printf("The content of str1 = %s\n", str1[]);
    printf("The content of str2 = %s\n", str2[]);
    return 0;
}

str2 的长度 = 11
str1 的内容 = Hell
str2 的内容 = Hello World

[编辑 | 编辑源代码]
  1. a b libc-alpha 邮件列表,选自 2000 年 8 月 8 日的主题:536061
  2. ISO/IEC. ISO/IEC WDTR 24731 安全 C 库函数规范. 国际标准化组织. Retrieved 2008-04-23.
  3. Plakosh, Daniel. "strcpy_s() 和 strcat_s()". Pearson Education, Inc. 检索于 2006-08-12.
  4. 微软. "CRT 中的安全增强功能". MSDN. 检索于 2008-09-16.
  5. "Re: 实施“C 库扩展”(ISO/IEC WG14 N1172)".
  6. Danny Kalev. "他们又在搞什么鬼". InformIT.
  7. "安全增强型 CRT,比标准库更安全?".
  8. ISO/IEC 9899:1999 规范, 第 326 页,第 7.21.2.3 节
  9. ISO/IEC. ISO/IEC WDTR 24731 安全 C 库函数规范. 国际标准化组织. 检索于 2008-04-23.
  10. Plakosh, Daniel. "strcpy_s() 和 strcat_s()". Pearson Education, Inc. 检索于 2006-08-12.
  11. 微软. "CRT 中的安全增强功能". MSDN. 检索于 2008-09-16.
  12. "Re: 实施“C 库扩展”(ISO/IEC WG14 N1172)".
  13. Danny Kalev. "他们又在搞什么鬼". InformIT.
  14. "安全增强型 CRT,比标准库更安全?".
  15. "strlen.c 修订版 1.4". FreeBSD. 2002-03-21. 检索于 2009-03-04.
  16. "/stable/10/lib/libc/string/strlen.c 的内容". FreeBSD. 2013-10-10.
  17. ISO/IEC 9899:1999 规范 (PDF). 第 343 页,第 7.12.4.3 节.
  18. a b strcasestr(3)
华夏公益教科书