7

我必须编写一个函数,用字符串的内容填充指定长度的 char* 缓冲区。如果绳子太长,我只需要剪掉它。缓冲区不是由我分配的,而是由我的函数的用户分配的。我试过这样的事情:

int writebuff(char* buffer, int length){
    string text="123456789012345";
    memcpy(buffer, text.c_str(),length);
    //buffer[length]='\0';
    return 1;
}


int main(){
    char* buffer = new char[10];
    writebuff(buffer,10);
    cout << "After: "<<buffer<<endl;
}

我的问题是关于终结者:它应该存在还是不存在?这个函数用在更广泛的代码中,有时当字符串需要被剪切时,我会遇到奇怪字符的问题。

关于正确程序的任何提示?

4

10 回答 10

10

C 风格的字符串必须以零字符结尾'\0'

此外,您的代码还有另一个问题 - 它可能会尝试从源字符串的末尾进行复制。这是典型的未定义行为。它可能看起来很有效,直到有一次在堆内存块的末尾分配字符串并且副本进入内存的受保护区域并严重失败。您应该只复制到缓冲区长度或字符串长度的最小值。

PS为了完整起见,这是您功能的一个很好的版本。感谢Naveen指出终止 null 中的一个错误。我冒昧地使用您的返回值来指示返回字符串的长度,或者如果传入的长度<= 0,则需要的字符数。

int writebuff(char* buffer, int length)
{
    string text="123456789012345";
    if (length <= 0)
        return text.size();
    if (text.size() < length)
    {
        memcpy(buffer, text.c_str(), text.size()+1);
        return text.size();
    }
    memcpy(buffer, text.c_str(), length-1);
    buffer[length-1]='\0';
    return length-1;
}
于 2011-05-10T15:33:56.923 回答
8

如果要将缓冲区视为字符串,则应 NULL 终止它。为此,您需要使用复制length-1字符并将字符memcpy设置length-1\0.

于 2011-05-10T15:34:43.967 回答
2

看来您正在使用 C++ - 鉴于此,最简单的方法是(假设接口规范需要 NUL 终止)

int writebuff(char* buffer, int length)
{
  string text = "123456789012345";
  std::fill_n(buffer, length, 0); // reset the entire buffer
  // use the built-in copy method from std::string, it will decide what's best.
  text.copy(buffer, length);
  // only over-write the last character if source is greater than length
  if (length < text.size())
    buffer[length-1] = 0;
  return 1; // eh?
}
于 2011-05-10T15:50:52.897 回答
1

char * 缓冲区必须以空值终止,除非您明确地将长度与它一起传递到各处并说明缓冲区不是以空值终止的。

于 2011-05-10T15:34:47.063 回答
0

首先,我不知道是否writerbuff应该终止字符串。writebuff这是一个设计问题,由决定它应该存在的人来回答。

其次,综合你的具体例子,有两个问题。一种是您将未终止的字符串传递给operator<<(ostream, char*). 其次是注释掉的行写入超出指定缓冲区的末尾。这两个都调用未定义的行为。

(第三个是设计缺陷——你能知道它length总是小于 的长度text吗?)

尝试这个:

int writebuff(char* buffer, int length){
  string text="123456789012345";
  memcpy(buffer, text.c_str(),length);
  buffer[length-1]='\0';
  return 1;
}


int main(){
  char* buffer = new char[10];
  writebuff(buffer,10);
  cout << "After: "<<buffer<<endl;
}
于 2011-05-10T15:44:51.237 回答
0
  1. main()中,您应该delete使用 分配的缓冲区new.,或者静态分配它 ( char buf[10])。是的,它只有 10 个字节,是的,它是一个内存“池”,而不是泄漏,因为它是一次性分配,是的,您需要在程序的整个运行时间内使用该内存。但这仍然是一个好习惯。

  2. 在 C/C++ 中,字符缓冲区的一般约定是它们以空值结尾,所以我会包含它,除非我被明确告知不要这样做。如果我这样做了,我会对其进行评论,甚至可能在char *参数上使用 typedef 或名称,表明结果是一个非空终止的字符串。

于 2011-05-10T15:52:34.933 回答
0

它应该绝对存在*,这可以防止字符串太长而无法完全填充缓冲区,并在以后访问时导致溢出。虽然 imo,strncpy应该被用来代替memcpy,但你仍然必须 null 终止它。(您的示例也泄漏了内存)。

*如果您有任何疑问,请走最安全的路线!

于 2011-05-10T15:33:03.200 回答
0

我的问题是关于终结者:它应该存在还是不存在?

是的。它应该在那里。否则你以后怎么知道字符串在哪里结束?又怎么cout会知道?它会一直打印垃圾,直到遇到值恰好是\0. 你的程序甚至可能崩溃。

作为旁注,您的程序正在泄漏内存。它不会释放它分配的内存。但是,既然您要退出main(),那就没关系了;毕竟一旦程序结束,所有内存都会回到操作系统,无论你是否释放它。但是,如果您没有忘记自己释放内存(或任何其他资源),那么它通常是一种很好的做法。

于 2011-05-10T15:33:03.620 回答
0

是否应该用 a 终止字符串\0取决于writebuff函数的规范。如果你buffer在调用你的函数后应该是一个有效的 C 风格的字符串,你应该用\0.

但是请注意,这c_str()将以 a 结束\0,因此您可以text.size() + 1其用作源字符串的大小。另请注意,如果length大于字符串的大小,您将复制比text当前代码提供的内容更多的内容(您可以使用它min(length - 2, text.size() + 1/*trailing \0*/)来防止这种情况,并将buffer[length - 1] = 0其设置为关闭)。

buffer分配的 in被main泄露了,顺便说一句

于 2011-05-10T15:34:16.237 回答
0

我同意 Necrolis 的观点,即 strncpy 是要走的路,但如果字符串太长,它不会得到空终止符。您在放置一个显式终止符方面有正确的想法,但是正如您所写的那样,您的代码将它放在了末尾。(这是在 C 中,因为您似乎做的 C 比 C++ 多?)

int writebuff(char* buffer, int length){
    char* text="123456789012345";
    strncpy(buffer, text, length);
    buffer[length-1]='\0';
   return 1;
}
于 2011-05-10T15:39:17.090 回答