Memcpy,字符串和终结符

时间:2011-05-10 15:28:47

标签: c++ c string terminator

我必须编写一个函数,用一个字符串的内容填充指定长度的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;
}

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

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

10 个答案:

答案 0 :(得分:7)

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

答案 1 :(得分:6)

C样式字符串必须以零字符'\0'终止。

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

P.S。为了完整性,这是一个很好的功能版本。感谢Naveen指出终止null中的off-by-one错误。我冒昧地使用你的返回值来表示返回字符串的长度,或者传入的长度是&lt; = 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;
}

答案 2 :(得分: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?
}

答案 3 :(得分:1)

char *缓冲区必须以null结尾,除非你在任何地方显式地传出长度并说明缓冲区不是以null结尾。

答案 4 :(得分:0)

它最应该是*,这可以防止缓冲区中的字符串太长而无法完全填充它并在访问时导致溢出。虽然imo,strncpy应该用memcpy而不是{{1}},但你仍然需要null来终止它。 (也是你的例子泄漏内存)。

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

答案 5 :(得分:0)

  

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

是。应该在那里。否则你怎么会知道字符串的结束位置? cout将如何知道?它会一直打印垃圾,直到它遇到一个价值恰好是\0的垃圾。你的程序甚至可能崩溃。

作为旁注,您的程序正在泄漏内存。它不会释放它分配的内存。但是既然你要从main()退出,那就无所谓了;毕竟一旦程序结束,所有内存都将返回操作系统,无论你是否解除分配。但是,如果你不忘记自己释放内存(或任何其他资源),那么这是一般的好习惯。

答案 6 :(得分:0)

是否应使用\0终止字符串取决于writebuff函数的规范。如果在调用函数后,buffer中的内容应该是有效的C风格字符串,则应使用\0终止它。

但请注意,c_str()将以\0终止,因此您可以使用text.size() + 1作为源字符串的大小。另请注意,如果length大于字符串的大小,则您将复制的内容比text提供的当前代码更多(您可以使用min(length - 2, text.size() + 1/*trailing \0*/)来阻止,并设置{ {1}}取消它。)

buffer[length - 1] = 0中分配的buffer被泄露,顺便说一下

答案 7 :(得分: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;
}

答案 8 :(得分: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;
}

答案 9 :(得分:0)

  1. main()中,您应delete使用new.分配的缓冲区,或静态分配(char buf[10])。是的,它只有10个字节,是的,它是一个内存“池”,而不是泄漏,因为它是一次性分配,是的,你需要在程序的整个运行时间内使用该内存。但这仍然是一个很好的习惯。

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