将strcpy替换为strcpy_mine,后者将使用strncpy并在末尾添加空字符以实现字符串复制。

6

标题中已经有提示,基本上我继承了一些代码,其中有800多个strcpy实例。我想编写一个新函数,然后用strcpy_mine替换strcpy。

所以我正在尝试确定strcpy_mine的参数列表。

我尝试过:

void strcpy_mine( char* pTarget, const char* const pCopyMe )
{
  const unsigned int lenAlwaysFour = sizeof(pCopyMe ); //:(
  strncpy( pTarget, pCopyMe, lenAlwaysFour );

  //add extra terminator in case of overrun
  pTarget[lenAlwaysFour] = 0;
}

但是sizeof始终为4,pCopyMe是一个指针。

我不想做的是替换。

strcpy (buf, pCopyMe);

使用

strncpy (buf, pCopyMe, sizeof(pCopyMe)); buf[sizeof(pCopyMe)] = 0;

有什么想法吗?(没有可用的strcpy_l)

7个回答

12

sizeof() 返回类型的大小 - 在这种情况下是 const char* const,在32位机器上为4。

我认为你想要的是 strlen()。但这并不是正确使用 strncpy 函数的方法。 你需要知道 strncpy 的输出缓冲区大小。

要解决这个问题,你需要检查每个调用站点的代码,并计算出输出缓冲区的大小,并将其作为参数传递给 strcpy_mine。如果 strcpy(或 strcpy_mine)的调用站点不知道输出缓冲区的大小,则需要向后搜索代码,找到分配缓冲区的位置,并将大小一直传递到 strcpy 站点。

基本上,你不能编写一个与 strcpy 接受相同参数的替代函数,希望避免产生 strncpy 的问题(以及更好的替代方案)。你可以创建一个与 strncpy 接受相同参数的函数,但确保结果以空字符结尾 - 查看OpenBSD的 strlcpy() 函数实现。但第一步必须是更改调用站点,以传递有关输出缓冲区大小的信息。


+1 for strlcpy。在编写类似于此的自己的函数时,我还会将枚举 {AllOrNothing, TruncateOkay} 传递给函数,以使其处理溢出情况。 - Dolphin

4

根据调用网站的外观不同,通常大部分情况可以通过一个简单模板来处理:

#include <string.h>

template <int bufferSize>
void strcpy_mine( char (&pTarget)[bufferSize], const char* const pCopyMe )
{
  strncpy( pTarget, pCopyMe, bufferSize-1 );

  //add extra terminator in case of overrun
  pTarget[bufferSize-1] = 0;
}

int main()
{
  char buf[128];
  strcpy_mine(buf,"Testing");
  return 0;
}

如果您使用的是Microsoft Visual Studio 2005或更新版本,请参见Microsoft实现的安全模板重载

太好了!这几乎是我想要的,但它仍然强制我在尖括号中放置缓冲区的大小。char buf[200]; strcpy_mine<200>(buf, pString); - timB33
strCpyMine<sizeof(buf)>(buf, pString); - timB33
过时的编译器,我认为应该是使用VC6;请参阅http://support.microsoft.com/kb/165803。 - timB33
1
我做错了什么。模板需要稍微不同。现在正在修复。 - Suma

2
你可以使用与strncpy相同的参数列表来编写strcpy_mine,但要确保它始终将结果以空字符结尾。这应该不难实现。
然而,一个挑战是,你现有的一些调用strcpy()的代码可能也不知道缓冲区的大小。

我赞同这个观点。你需要添加另一个参数来指定输出缓冲区的大小。strcpy方法是缓冲区溢出错误的经典来源。甚至微软已经弃用了这个函数,转而使用类似strncpy的函数。 - Mark

2

或许有些边缘,但由于没有人提到它并且在标题中炫耀:您不能(合法地)编写名为strcpy_mine()的全局函数。

str开头的函数的“名称空间”保留给标准库。例如,请参见此问题的被接受答案


1
Douglas Leeder说得对。除非你愿意在每个实例中传递一个好的、合理的缓冲区长度,否则替换strcpy的有用性是有限的。那是很多工作!
好消息是,这是值得的!几年前,我参与了几个C++项目,它们都晚期、有bug且不可靠。通过声明strcpy和strlen为禁止使用,并花费2-3天时间将它们替换为自定义的strncpy/strnlen,在所有这些项目中,我们突然可以运行数天而不是数小时。我们还看到了很多截断的字符串出现在屏幕显示和日志文件中。这给了我们追踪截断问题(以前是崩溃问题)所需的线索。
如果你不想这样做,你可以通过简单地检查两个指针参数是否为NULL,并限制字符串复制的最大大小,记录达到边界的所有时间,来获得更小的收益。不要对任何一个参数进行strlen,因为如果字符串没有正确的空终止符,strlen会轻易崩溃。
现在,新项目使用良好的字符串对象,但还有很多遗留代码存在。

0
你一定需要将目标缓冲区的大小作为参数传递,正如其他人所说。
这有点离题,但是我想指出,在使用 `strncpy()` 后,您需要将缓冲区的最后一个字符设置为 null,它的索引比长度小 1(不是缓冲区的长度):
strncpy (buf, pCopyMe, buflen); buf[buflen - 1] = '\0';

或者,你可以在一个空字符串上使用 strncat(),传递比实际长度少 1 的长度,这会保证你的字符串以 null 结尾:
buf[0] = '\0'; strncat (buf, pCopyMe, buflen - 1);

如果你已经在做这件事,为什么不顺手这样写呢?strncpy(buf, pCopyMe, buflen)[buflen-1]='\0'; :-) - AndersK

0

此外,您可以使用宏来避免多次编辑。或者通过一些脚本自动化编辑。


如果你在意的话,宏(macro)是单数形式,宏(macros)是复数形式。Macroses 不是一个词。我猜英语不是你的母语。我只是想帮助你。 - jmucchiello

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接