C奇怪的数组行为

6

在得知strncmp并非表面看起来的样子,以及系统(Linux)上没有strlcpy函数之后,我决定尝试着自己写一个替代品。

我找到了libc维护者Ulrich Drepper发布的一段引用,其中提供了使用mempcpystrlcpy替代方案。虽然我也没有mempcpy,但它的行为很容易复制。首先,这是我使用的测试用例:

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

#define BSIZE 10

void insp(const char* s, int n)
{
   int i;

   for (i = 0; i < n; i++)
      printf("%c  ", s[i]);

   printf("\n");

   for (i = 0; i < n; i++)
      printf("%02X ", s[i]);

   printf("\n");

   return;
}

int copy_string(char *dest, const char *src, int n)
{
   int r = strlen(memcpy(dest, src, n-1));
   dest[r] = 0;

   return r;
}

int main()
{
   char b[BSIZE];
   memset(b, 0, BSIZE);

   printf("Buffer size is %d", BSIZE);

   insp(b, BSIZE);

   printf("\nFirst copy:\n");
   copy_string(b, "First", BSIZE);
   insp(b, BSIZE);
   printf("b = '%s'\n", b);

   printf("\nSecond copy:\n");
   copy_string(b, "Second", BSIZE);
   insp(b, BSIZE);

   printf("b = '%s'\n", b);

   return 0;
}

以下是它的结果:

Buffer size is 10                    
00 00 00 00 00 00 00 00 00 00 

First copy:
F  i  r  s  t     b     =    
46 69 72 73 74 00 62 20 3D 00 
b = 'First'

Second copy:
S  e  c  o  n  d          
53 65 63 6F 6E 64 00 00 01 00 
b = 'Second'

您可以在内部表示中看到(由insp()创建的行),存在一些混杂的噪音,比如第一次复制后检查中的printf()格式字符串和第二次复制中的外来0x01。

这些字符串被完整地复制,并且它能正确处理过长的源字符串(现在先忽略将0作为长度传递给copy_string可能出现的问题,稍后我会解决这个问题)。

但是为什么我的目标中有外来数组内容(来自格式字符串)呢?就好像目标实际上被调整大小以匹配新长度一样。


在这种情况下,我指的是来自printf的字符串文字,即“b ='%s'”,它与我的数组“b”混合在一起,成为了目标。 - LukeN
请注意,strlcpy()strlcat()的源代码在自由许可下非常容易获取:http://www.openbsd.org/cgi-bin/cvsweb/~checkout~/src/lib/libc/string/strlcpy.c?content-type=text%2Fplain - Michael Burr
4个回答

4

字符串的结尾由\0标记,之后的内存可以是任何东西,除非您的操作系统故意将其清空,否则它只是留在那里的随机垃圾。

请注意,在这种情况下,“问题”不在copy_string中,您确切地复制了10个字符 - 但是主代码中“first”之后的内存是随机的。


哦天啊,我没有考虑到memcpy()不会在'\0'处停止,我真是太蠢了。 - LukeN

2

你停止的不是源尺寸,而是目标尺寸,它恰巧比源更大,所以你复制了源字符串加上一些垃圾信息。

你可以很容易地看到自己正在复制源字符串,包括它的空终止符。但由于你正在复制10个字节,而“First”和“Second”两个字符串都比10个字节短,你也在复制它们后面的额外字节。


1

如果destsrc的长度不都至少为n-1,那么使用memcpy(dest, src, n-1)会导致未定义的行为。

例如,First\0长度为六个字符,但你从中读取了n-1(9)个字符;字符串字面值结尾后的内存内容是未定义的,当你读取该内存时,程序的行为也是未定义的。


0

额外的 "stuff" 存在是因为你已经将缓冲区大小传递给了 memcpy。即使源字符串较短,它也会复制那么多字符。

我会有一些不同的做法:

void copy_string(char *dest, char const *src, size_t n) { 
    *dest = '\0';
    strncat(dest, src, n);
}

strncatstrncpy 不同,它的定义符合大多数人的合理预期。


人们经常期望 strncat 的工作方式与 strlcat 相同,即他们期望它使用目标缓冲区的全部长度,而实际上它只使用可用于连接的剩余长度。 - AnT stands with Russia
实际上,您需要 if (n > 0) strncat(dest, src, n - 1)(假设 n 是目标缓冲区的大小)。 - caf

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