Back to basics - idiomatic way to copy string to static array
Ok, strncpy
is not designed to work with NULL terminated strings - it's not designed for NULL terminated strings (if dest is too short it won't be terminated by NULL and if dest is longer it will be padded by zero's).
So, here is a trivial code:
const char *src = ....; // NULL terminated string of unknown length
char dest[30];
How to src to dest? strcpy
is not safe, strncpy
is bad choice too. So I left with strlen followed by memcpy
?
I suppose solution will differ a bit whenever I care care dest won't be truncated (dest is smaller than length of开发者_如何学Go src) or not.
Some limitations:
- Legacy code, so I don't want and can not to change it to std::string
- I don't have
strlcpy
- gcc doesn't supply it. - Code may be used in parts of application where performance is critical (e.g. I don't want to wast CPU time padding with zeros dest as
strncpy
does). However, I'm not talking about premature optimization, but rather the idiotic way to perform string copying in C-way.
Edit
Oopps, I meant strncpy
and not snprintf
. My mistake
With strncpy:
strncpy(dest, src, sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';
This pads with zeros, but does much less formatting work than snprintf
. If you really must have the computer do as little as possible, describe it yourself:
char* last = dest + sizeof(dest) - 1;
char* curr = dest; /* assuming we must not alter 'dest' */
while (curr != last && *src) { *curr++ = *src++; }
*last = '\0'; /* avoids a branch, but always writes.
If branch prediction is working well and the text normally fits:
if (curr == last) { *curr = '\0'; } */
I think you're talking about strncpy()
, which might not terminate the string and will fill the remainder of the buffer with zeros.
snprintf()
always terminates the destination string (as long as thebuffer has a size of at least 1) and doesn't pad the remainder of the buffer with zeros.
In summary, snprintf()
is what you want, except you're very concerned about performance. Since snprintf()
needs to interpret the format string (even if all it ends up doing is copying a string), you might be better off with something like strlcpy()
for bounded string copy operations.
(and if you want strlcpy()
but don't have it, you can get the rather simple source here. For completeness, strlcat()
is here)
If you don't care about truncation, you can use strncat()
:
dest[0] = 0;
strncat(dest, src, sizeof dest - 1);
I'd just roll my own:
for (int i = 0; i < (sizeof(dest) - 1) && src[i] != NULL; i++)
{
dest[i] = src[i];
}
dest[i] = NULL;
This ensures that dest
is null-terminated, but never adds more nulls than necessary. If you're really performance-sensitive, you can declare this as a macro or an inline function in a common header.
Use snprintf
. It always null-terminates and does not do any null padding. Don't know where you got the misconceptions about it...
std::copy(src, src+strlen(src)+1, dest)
I'm not sure I understand your question entirely, but if you're concerned about zero-padding it can often be done pretty efficiently if you initialize your array like this.
char dest[30] = { 0 };
If you initialize it like that you don't have to care about extra logic to add '\0' to the end of the string and it might even turn out faster.
But if you're going to optimize remember to measure the performance before and after the optimization. Otherwise always code with readability and maintainability in mind.
精彩评论