跳转到内容

C 编程/string.h/strcpy

来自 Wikibooks,开放世界中的开放书籍

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

该函数的原型为:[1]

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

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

用法和实现

[编辑 | 编辑源代码]

例如

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的内存地址到str2中,而不是实际的字符串。两者str1str2将引用相同的内存块,并且以前由str2指向的已分配块将丢失。对 str2[0] 的赋值要么也会修改 str1,要么会导致访问冲突(因为现代编译器通常将字符串常量放在只读内存中)。

strcpy函数通过迭代字符串的各个字符并将它们逐个复制来执行复制。的显式实现为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 程序员需要跟踪字符串长度,并且“使用此函数只会导致其他错误”。[2]

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

strcpy_s 函数,在 ISO/IEC TR 24731 中提议进行标准化,[3][4] 受 Microsoft C 运行时库[5] 和一些其他 C 库的支持。如果源字符串不适合,则返回非零值,并将缓冲区设置为空字符串(而不是前缀!)。它也明确地不受某些库(包括 GNU C 库)的支持。[6] Microsoft 编译器生成的警告消息建议程序员更改strcpystrncpy为该函数,据一些人推测,这是微软试图将开发人员锁定在其平台上的一种尝试。[7][8]

参考文献

[编辑 | 编辑源代码]
  1. ISO/IEC 9899:1999 规范,第 326 页,第 7.21.2.3 节
  2. libc-alpha 邮件列表,选自 2000 年 8 月 8 日的主题:536061
  3. ISO/IEC. ISO/IEC WDTR 24731 安全 C 库函数规范. 国际标准化组织. 检索于 2008-04-23.
  4. Plakosh,Daniel。 “strcpy_s() 和 strcat_s()”。Pearson Education, Inc。检索于 2006-08-12.
  5. Microsoft。 “CRT 中的安全增强功能”。MSDN。检索于 2008-09-16.
  6. “关于实现“C 库扩展”(ISO/IEC WG14 N1172)”.
  7. Danny Kalev。 “他们又来了”。InformIT。
  8. “安全增强 CRT,比标准库更安全?”.
[编辑 | 编辑源代码]
华夏公益教科书