编辑:我已经为示例添加了源代码。

我遇到了这个示例:

char source[MAX] = "123456789";
char source1[MAX] = "123456789";
char destination[MAX] = "abcdefg";
char destination1[MAX] = "abcdefg";
char *return_string;
int index = 5;

/* This is how strcpy works */
printf("destination is originally = '%s'\n", destination);
return_string = strcpy(destination, source);
printf("after strcpy, dest becomes '%s'\n\n", destination);

/* This is how strncpy works */
printf( "destination1 is originally = '%s'\n", destination1 );
return_string = strncpy( destination1, source1, index );
printf( "After strncpy, destination1 becomes '%s'\n", destination1 );


产生了以下输出结果:

destination is originally = 'abcdefg'
After strcpy, destination becomes '123456789'

destination1 is originally = 'abcdefg'
After strncpy, destination1 becomes '12345fg'


这让我想知道为什么有人会想要这种效果。看起来会令人困惑。该程序使我认为您基本上可以使用Tom Bro763复制某人的名字(例如Tom Brokaw)。

strncpy()相比,使用strcpy()有什么优势?

评论

我想您是想问“为什么在地球上有人会使用strcpy而不是strncpy?”

当我是用C开设的第一学期编程课程的助教时,我向学生保证,如果我根据精心设计的输入对它们进行评分,则诸如getline之类的方法的任何使用都会导致错误的结果。 :)

我认为您误解了代码的实际作用。仔细看看。

真可惜C从来没有一个像样的标准字符串库。

可惜的不是什么。我的意思是,这完全让我感到困惑,并使高级语言变得更加有趣:)

#1 楼

strncpy通过要求您增加长度来消除缓冲区溢出。 strcpy依赖于尾随的n,这种情况可能并不总是发生。

其次,为什么您选择只在7个字符串上复制5个字符,这超出了我的范围,但是却产生了预期的行为。它只是复制第一个n字符,其中n是第三个参数。

strcpy函数全部用作防御缓冲区溢出的防御性编码。请使用它们代替较早的功能,例如q4312079q。

评论


参见lysator.liu.se/c/rat/d11.html:strncpy最初被引入C库中,以处理诸如目录条目之类的结构中的定长名称字段。此类字段的使用方式与字符串不同:对于最大长度的字段,不需要尾随null;将较短名称的尾随字节设置为null可以确保按字段进行有效的比较。 strncpy并非源于“有约束力的strcpy”,委员会更愿意承认现有做法,而不是改变职能以使其更适合这种使用。

–SinanÜnür
09年8月11日在6:20

我不确定为什么这会引起很多投票-strncpy从来没有被用作strcpy的更安全替代品,并且实际上并没有任何安全性,因为它不会将字符串终止为零。它还具有不同的功能,因为它用NUL字符填充了所提供的长度。正如caf在他的答复中所说-用于覆盖固定大小的数组中的字符串。

–量油尺
09年8月11日在6:25

事实仍然是strncpy并不是strcpy的安全版本。

–SinanÜnür
09年8月11日在6:33

@Sinan:我从来没有说过它更安全。是防御性的。它迫使您付出一定的努力,使您思考自己在做什么。有更好的解决方案,但是事实仍然是人们会(并且确实)使用strncpy而不是strcpy,因为它是一种更具防御性的功能……这就是我所说的。

–埃里克
09年8月11日在6:36

n个函数均用作防御缓冲区溢出的防御性编码。请使用它们代替旧功能,例如strcpy。这对于s​​nprintf是正确的,但对于strncat则无关,而对于strncpy则完全不正确。这个答案怎么可能获得如此多的赞誉?它显示了关于此伪功能的情况有多糟糕。使用它不是防御性的:在大多数情况下,程序员不了解其语义,并创建了一个可能为非零终止的字符串。

– chqrlie
16年5月5日在19:16

#2 楼

strncpy()函数在设计时考虑了一个非常特殊的问题:处理以原始UNIX目录条目的方式存储的字符串。它们使用固定大小的数组,并且仅当文件名短于数组时才使用nul终止符。

这就是strncpy()的两个奇怪之处:<​​br />

如果目的地已满,它不会在目的地放置nul终止符;和
它总是完全填充目的地,必要时使用nul。

对于“更安全的strcpy()”,最好像下面这样使用strncat()

if (dest_size > 0)
{
    dest[0] = 'q4312078q';
    strncat(dest, source, dest_size - 1);
}


这将始终终止结果,并且不会复制多余的内容。

评论


但是,当然,strncpy也不总是您想要的:strncpy接受要添加的最大字符数,而不是目标缓冲区的大小……但这只是一件小事,因此除非您不这样做,否则可能不会成为问题。正在尝试将一个字符串连接到另一个字符串。

– David Wolever
09年8月11日在23:59

我不知道它的原因,这与我在atm上进行的工作非常相关。

–马特细木工
2010-09-21 16:16

strncpy()函数旨在以固定长度的null填充格式存储字符串。这种格式曾用于原始Unix目录条目,但也用于其他许多地方,因为它允许将0-N字节的字符串存储在N字节的存储空间中。即使在今天,许多数据库在其固定长度字符串字段中仍使用填充了空值的字符串。与strncpy()的混淆源于将字符串转换为FLNP格式的事实。如果需要的是FLNP字符串,那就太好了。如果需要一个以零结尾的字符串,则必须自己提供终止符。

–超级猫
2011-11-20 23:26

为什么我们需要写dest [0] ='\ 0';在strncat通话之前?您介意先生吗?

–snr
19年3月6日在19:31

@snr:strncat()将源字符串连接到目标字符串的末尾。我们只想将源字符串复制到目标位置,因此我们首先将目标位置设置为空字符串-这就是dest [0] ='\ 0';做。

–caf
19 Mar 6 '19 at 22:36

#3 楼

虽然我知道strncpy的意图,但这并不是一个很好的功能。避免两者。 Raymond Chen解释。

就个人而言,如果您要处理以null结尾的字符串,我的结论就是避免strncpy及其所有朋友。尽管名称中带有“ str”,但这些函数不会产生以null终止的字符串。他们将以空字符结尾的字符串转换为原始字符缓冲区。在需要以空值终止的字符串作为第二个缓冲区的地方使用它们是完全错误的。如果源太长,不仅无法获得正确的null终止,而且如果源太短,则会获得不必要的null填充。

另请参阅为什么strncpy不安全?

#4 楼

strncpy并不比strcpy安全,它只是将一种错误与另一种错误进行了交换。在C语言中,当处理C字符串时,您需要知道缓冲区的大小,无法解决它。 strncpy对于其他人提到的目录内容是合理的,但是否则,您永远不要使用它:


如果您知道字符串和缓冲区的长度,为什么要使用strncpy?充其量是在浪费计算能力(添加无用的0)。
如果您不知道长度,那么您可能会面临无声地截断字符串的风险,这并不比缓冲区溢出好多少


评论


我认为这是对strncpy的很好描述,所以我投票赞成。 strncpy有它自己的麻烦。我想这就是例如glib具有自己的扩展名的原因。是的,不幸的是,作为程序员,您必须了解所有数组的大小。以0结尾的char数组作为字符串的决定,使我们所有人付出了沉重的代价。

–弗里德里希
09-10-21在16:04

将数据存储在固定格式的文件中时,零填充字符串很常见。可以肯定的是,诸如数据库引擎和XML之类的东西的普及以及不断发展的用户期望,已经导致固定格式的文件比20年前少见。但是,此类文件通常是最节省时间的数据存储方式。除非记录中数据的预期长度和最大长度之间存在巨大差异,否则将记录作为包含一些未使用数据的单个块读取要比读取分为多个块的记录要快得多。

–超级猫
2011-11-20 23:39

只是接管了使用g_strlcpy()的旧代码的维护,因此不会遇到填充效率低下的问题,但是可以肯定的是,传输的字节数没有得到维护,因此代码默默地截断了结果。

–user2548100
2014年1月28日19:37

#5 楼

您要查找的是函数strlcpy(),该函数始终始终以0终止字符串并初始化缓冲区。它还能够检测溢出。唯一的问题是,它不是(真正地)可移植的,并且仅在某些系统(BSD,Solaris)上存在。该功能的问题在于,它打开了另一种蠕虫病毒,如在
http://en.wikipedia.org/wiki/Strlcpy


上的讨论中可以看到的那样。它比strncpy()strcpy()有用得多。它具有更好的性能,并且是snprintf()的良好伴侣。对于没有它的平台,它相对容易实现。
(对于应用程序的开发阶段,我用陷阱版本替换了这两个函数(snprintf()strlcpy()),该版本会在缓冲区溢出或终止时残酷地终止程序。截断,这样可以迅速捕获最严重的违规者。尤其是当您在其他人的代码库中工作时。

编辑:strlcpy()可以轻松实现:

size_t strlcpy(char *dst, const char *src, size_t dstsize)
{
  size_t len = strlen(src);
  if(dstsize) {
    size_t bl = (len < dstsize-1 ? len : dstsize-1);
    ((char*)memcpy(dst, src, bl))[bl] = 0;
  }
  return len;
}


评论


您可以写道,strlcpy在Linux和Windows之外的几乎所有其他功能上都可用!但是,它已获得BSD许可,因此您可以将其放入一个库中并从那里使用它。

–迈克尔·范·德·韦斯特惠岑
09年8月11日在8:07

您可能想要为dstsize> 0添加一个测试,如果没有,则不执行任何操作。

– chqrlie
16-2-5在19:19

你是对的。我将添加检查,因为没有它,dstsize将触发目标缓冲区长度为len的memcpy并将其溢出。

–帕特里克·施吕特(PatrickSchlüter)
16年2月8日在8:05

再加上一个促进好的解决方案。更多的人需要了解strlcpy,因为每个人都在不断地对其进行改造。

–rsp
16年6月26日在6:16

@MichaelvanderWesthuizen它在Linux上可用,只是在glibc中不可用。查看我的答案以获取更多信息(1)(2)(3)

–rsp
16-6-26在6:19



#6 楼

strncpy()函数是更安全的函数:您必须传递目标缓冲区可以接受的最大长度。否则,可能会发生源字符串未正确终止为0的情况,在这种情况下,strcpy()函数可能会向目标写入更多字符,从而破坏目标缓冲区之后的内存中的任何内容。这是许多漏洞利用中使用的缓冲区溢出问题。

对于POSIX API函数(例如read()),该函数也不会将终止符0放入缓冲区,但会返回读取的字节数,您可以手动放置0,或使用strncpy()复制它。

在您的示例代码中,index实际上不是索引,而是count-它指示从源到目标最多复制多少个字符。如果源的前n个字节中没有空字节,则放置在目标中的字符串将不会以空终止。

#7 楼

即使目标文件的大小较小,strncpy也会用'\ 0'填充目标文件的大小,即使目标文件的大小较小....

联机帮助页:


如果src的长度小于n,则strncpy()将空字节的
dest余数填充为空字节。

不仅余数...在此之后直到n个字符
到达。这样就会溢出...(请参见手册页
实现)


评论


即使目标的大小较小,strncpy也会用目标源的大小'\ 0'填充目标。...恐怕此语句是错误和令人困惑的:strncpy的目标用'\ 0'填充目标size参数,如果源的长度较小。 size参数不是源的大小,不是要从源复制的最大字符数,因为它在strncat中,它是目标的大小。

– chqrlie
16-2-5在19:21



@chqrlie:是的。与其他复制操作相比,strncpy的优点在于它可以保证将写入整个目标。由于编译器在复制包含一些不确定值的结构时可能会尝试获得“创意”,因此确保结构中的任何字符数组都被完整写入可能是防止“意外”的最简单方法。

–超级猫
17年4月15日在16:34

@supercat:对于这种特定情况来说,这是一个很小的优势...但是在调用strncpy之后必须修补目标以确保终止为空:strncpy(dest,src,dest_size)[dest_size-1] ='\ 0';

– chqrlie
17年4月16日在20:17



@chqrlie:是否需要尾随空字节取决于数据应表示的内容。在结构中使用填充零而不是终止于零的数据并不像以前那样普遍,但是例如目标文件格式使用8字节的节名,能够在结构中使用char [8]处理最多8个字符的事物可能比使用char [8]更好,但只能处理7个字符,或者必须将字符串复制到char [9]缓冲区中,然后将其memcpy传输到目标位置。

–超级猫
17年4月16日在20:32

@chqrlie:大多数使用字符串执行代码的代码应该知道它们可能有多长时间,并且不应盲目地使用char指针运行,直到它们达到零为止。零终止的字符串真正唯一适合的是字符串文字,即使使用可变长度编码的前缀也可能会更好。对于几乎所有其他内容,最好使用带长度前缀的字符串或具有特殊前缀的字符串,这将表明char *确实类似于struct stringInfo {char header [4]; char * realData; size_t长度; size_t size;}。

–超级猫
17年4月16日在20:37



#8 楼

这可能会在许多其他情况下使用,您只需要将原始字符串的一部分复制到目标即可。使用strncpy()可以复制原始字符串的有限部分,而不是strcpy()。我看到您输入的代码来自publib.boulder.ibm.com。

#9 楼

这取决于我们的要求。
对于Windows用户

,只要我们不想复制整个字符串或只想复制n个字符,就使用strncpy。但是strcpy复制整个字符串,包括终止的空字符。

这些链接将帮助您更多地了解strcpy和strncpy
以及在哪里使用。

关于strcpy

关于strncpy

#10 楼

实际上,strncpy是strcpy的安全版本,绝对不要使用strcpy,因为它可能存在缓冲区溢出漏洞,使您的系统容易受到各种攻击。

评论


参见lysator.liu.se/c/rat/d11.html:strncpy函数strncpy最初被引入C库中,以处理诸如目录条目之类的结构中的定长名称字段。此类字段的使用方式与字符串不同:对于最大长度的字段,不需要尾随null;将较短名称的尾随字节设置为null可以确保按字段进行有效的比较。 strncpy并非源于``有约束力的strcpy'',委员会更愿意承认现有做法,而不是改变职能使其更适合这种使用。

–SinanÜnür
09年8月11日在6:19