As a C++ programmer I sometimes need deal with memory buffers using techniques from C. For example:
char buffer[512];
sprintf(buffer, \"Hello %s!\", userName.c_s
I assume your interest comes about primarily from a performance perspective, since solutions like vector, string, wstring, etc. will generally work even for interacting with C APIs. I recommend learning how to use those and how to use them efficiently. If you really need it, you can even write your own memory allocator to make them super fast. If you are sure they're not what you need, there's still no excuse for you to not write a simple wrapper to handle these string buffers with RAII for the dynamic cases.
With that out of the way:
Is passing the buffer as &buffer[0] better programming style than passing buffer? (I prefer &buffer[0].)
No. I would consider this style to be slightly less useful (admittedly being subjective here) as you cannot use it to pass a null buffer and therefore would have to make exceptions to your style to pass pointers to arrays that can be null. It is required if you pass in data from std::vector to a C API expecting a pointer, however.
Is there a maximum size that is considered safe for stack allocated buffers?
This depends on your platform and compiler settings. Simple rule of thumb: if you're in doubt about whether your code will overflow the stack, write it in a way which can't.
Is a static buffer (static char buffer[N];) faster? Are there any other arguments for or against it?
Yes, there is a big argument against it, and that is that it makes your function no longer re-entrant. If your application becomes multithreaded, these functions will not be thread safe. Even in a single-threaded application, sharing the same buffer when these functions are recursively called can lead to problems.
What about using static char * buffer = new char[N]; and never deleting the buffer? (Reusing the same buffer each call.)
We still have the same problems with re-entrancy.
I understand that heap allocation should be used when (1) dealing with large buffers or (2) maximum buffer size is unknown at compile time. Are there any other factors that play in the stack/heap allocation decision?
Stack unwinding destroys objects on the stack. This is especially important for exception-safety. Thus even if you allocate memory on the heap within a function, it should generally be managed by an object on the stack (ex: smart pointer). ///@see RAII.
Should you prefer the sprintf_s, memcpy_s, ... variants? (Visual Studio has been trying to convince me of this for a long time, but I want a second opinion :p )
MS was right about these functions being safer alternatives since they don't have buffer overflow problems, but if you write such code just as is (without writing variants for other platforms), your code will be married to Microsoft since it will be non-portable.
When using static buffers you can use return type const char *. Is this (generally) a good or a bad idea? (I do realize that the caller will need to make his own copy to avoid that the next call would change the previous return value.)
I'd say in almost every case, you want to use const char* for return types for a function returning a pointer to a character buffer. For a function to return a mutable char* is generally confusing and problematic. Either it's returning an address to global/static data which it shouldn't be using in the first place (see re-entrancy above), local data of a class (if it's a method) in which case returning it ruins the class's ability to maintain invariants by allowing clients to tamper with it however they like (ex: stored string must always be valid), or returning memory that was specified by a pointer passed in to the function (the only case where one might reasonably argue that mutable char* should be returned).
1) buffer
and &buffer[0]
should be equivalent.
2) Stack size limits will depend on your platform. For most simple functions, my personal rule of thumb is anything over ~256KB is declared dynamically; there's no real rhyme or reason to that number, though, it's just my own convention and it's currently within the default stack sizes for all of the platforms I develop for.
3) Static buffers aren't faster or slower (for all intents and purposes). The only difference is the access control mechanism. The compiler generally places static data in a separate section of the binary file than non-static data, but there is no noticeable/significant performance benefit or penalty involved. The only real way to tell for sure is to write the program both ways and time them (since many of the speed aspects involved here are dependent on your platform/compiler).
4) Don't return a const
pointer if the caller will need to modify it (that defeats the point of const
). Use const
for function parameters and return types if and only if they are not designed to be modified. If the caller will need to modify the value, your best bet is for the caller to pass the function a pointer to a pre-allocated buffer (along with the buffer size) and for the function to write the data into that buffer.
5) Reusing a buffer may lead to a performance improvement for larger buffers due to bypassing the overhead that is involved in calling malloc
/free
or new
/delete
each time. However, you run the risk of accidentally using old data if you forget to clear the buffer each time or you try to run two copies of the function in parallel. Again, the only real way to know for sure is to try both ways and measure how long the code takes to run.
6) Another factor in stack/heap allocation is scoping. A stack variable goes out of scope when the function it lives in returns, but a variable that was dynamically allocated on the heap can be returned to the caller safely or accessed the next time the function is called (a la strtok).
7) I would recommend against the use of sprintf_s
, memcpy_s
, and friends. They are not part of the standard library and are not portable. The more you use these functions, the more extra work you will have when you want to run your code on a different platform or use a different compiler.
You have a lot of questions! I'll do my best to answer a couple and give you a place to look for the others.
Is there a maximum size that is considered safe for stack allocated buffers?
Yes, but the stack size itself varies based on the platform you are working on. See When do you worry about stack size? for a very similar question.
Is static char buffer[N]; faster? Are there any other arguments for or against it?
The meaning of static is dependent on where the buffer is declared, but I assume you are talking about a static
declared inside a function, so it is initialized only once. In functions called many times, using static buffers may be a good idea to prevent stack overflow, but otherwise, keep in mind that allocating buffers is a cheap operation. Also, static buffers are much harder to work with when dealing with multiple threads.
For answers to most of your other questions, see Large buffers vs Large static buffers, is there an advantage?.