Memcpy,字符串和终结符

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

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

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

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

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

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

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

看来你正在使用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? } 

char *缓冲区必须为空终止,除非您在任何地方明确地传递长度并说明缓冲区不是空终止。

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

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

main分配的buffer是泄露的,顺便说一句

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

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

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

我同意Necrolis的观点,strncpy是要走的路,但如果字符串太长,它就不会得到null终止符。 你有一个正确的想法,就是设置一个明确的终结符,但是如你所写,你的代码会把它放在一个结尾处。 (这是在C中,因为你似乎比C ++做的更多C?)

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

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

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

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

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