作为一名C++程序员,我有时需要使用C语言处理内存缓冲区.例如:
char buffer[512];
sprintf(buffer, "Hello %s!", userName.c_str());
Run Code Online (Sandbox Code Playgroud)
或者在Windows中:
TCHAR buffer[MAX_PATH+1]; // edit: +1 added
::GetCurrentDirectory(sizeof(buffer)/sizeof(TCHAR), &buffer[0]);
Run Code Online (Sandbox Code Playgroud)
上面的示例是我通常如何创建本地缓冲区(本地堆栈分配的char数组).但是,有许多可能的变化,因此我对您对以下问题的答案非常感兴趣:
&buffer[0]比传递更好的编程风格buffer?(我更喜欢&buffer[0].)static char buffer[N];)更快吗?是否还有其他论据支持或反对?const char *.这(通常)是好还是坏?(我确实意识到调用者需要自己创建副本以避免下一次调用会改变之前的返回值.)static char * buffer = new char[N];,永远不要删除缓冲区并在每次调用时重复使用它.sprintf_s,memcpy_s......变种?(Visual Studio一直试图让我相信这一点很长时间,但我想要第二个意见:p)如果您想要重新使用代码,请远离静态缓冲区.
使用snprintf()而不是sprintf(),这样你就可以控制缓冲区溢出.
你永远不知道你的通话环境中剩余多少堆栈空间 - 所以没有尺寸在技术上是"安全的".大部分时间你都有很多空间.但那一次会让你好.我使用经验法则永远不会将数组放在堆栈上.
让客户拥有缓冲区并将其大小传递给您的函数.这使得它可以重新进入,并且对于谁需要管理缓冲区的生命没有任何含糊之处.
如果你正在处理字符串数据,请仔细检查你的字符串函数,以确保它们终止,特别是当它们到达缓冲区的末尾时.在处理各种函数的字符串终止时,C库非常不一致.
我认为您的兴趣主要来自性能方面,因为矢量,字符串,wstring等解决方案通常即使与C API交互也可以使用。我建议学习如何使用它们以及如何有效地使用它们。如果确实需要它,甚至可以编写自己的内存分配器以使其超快。如果您确定它们不是您所需要的,那么您就没有理由不编写简单的包装程序来使用RAII处理动态情况下的这些字符串缓冲区。
顺便说一句:
将缓冲区作为&buffer [0]传递是比传递缓冲区更好的编程风格吗?(我更喜欢&buffer [0]。)
不。我认为这种样式的用处不大(这里肯定是主观的),因为您不能使用它来传递空缓冲区,因此必须对样式进行例外处理才能将指针传递给可以为空的数组。但是,如果将数据从std :: vector传递到需要指针的C API,则是必需的。
是否有最大大小对于堆栈分配的缓冲区来说是安全的?
这取决于您的平台和编译器设置。简单的经验法则:如果您不确定代码是否会溢出堆栈,请以无法做到的方式编写。
静态缓冲区(static char buffer [N];)更快吗?还有其他支持或反对的论点吗?
是的,对此有一个很大的争论,那就是它使您的函数不再可重入。如果您的应用程序成为多线程,则这些函数将不是线程安全的。即使在单线程应用程序中,递归调用这些函数时共享相同的缓冲区也会导致问题。
怎样使用静态char * buffer = new char [N]; 从不删除缓冲区?(每个调用重用相同的缓冲区。)
重新进入我们仍然有同样的问题。
我了解在以下情况下应使用堆分配:(1)处理大缓冲区或(2)在编译时不知道最大缓冲区大小。堆栈/堆分配决策中还有其他因素吗?
堆栈展开会破坏堆栈上的对象。这对于异常安全特别重要。因此,即使您在函数内的堆上分配内存,通常也应该由堆栈上的对象来管理它(例如:智能指针)。/// @请参阅RAII。
您是否应该喜欢sprintf_s,memcpy_s,...变体?(Visual Studio长期以来一直试图说服我,但我想再说一遍:p)
MS认为这些函数是更安全的替代方法是正确的,因为它们没有缓冲区溢出问题,但是如果您照原样编写此类代码(不编写其他平台的变体),则您的代码将嫁给Microsoft,因为它们不会便携式的。
使用静态缓冲区时,可以使用返回类型const char *。(通常)这是一个好主意还是一个坏主意?(我确实意识到,调用方将需要制作自己的副本,以避免下一次调用更改前一个返回值。)
我想说的是,几乎在每种情况下,您都想将const char *用作函数的返回类型,以将指针返回到字符缓冲区。对于要返回可变char *的函数,通常会造成混乱和问题。它要么返回一个地址,该地址首先是它不应该使用的全局/静态数据(请参见上面的重入),则返回一个类的本地数据(如果是方法),在这种情况下,返回它会破坏该类的能力通过允许客户端随意修改变量来保持不变(例如,存储的字符串必须始终有效),或者返回由传入函数的指针指定的内存(唯一可能合理地认为可变char *的情况)应该返回)。