'that is what the libc runtime, which is the foundation that every language ultimately reaches down to in the end, is built around.'
Except, interestingly enough, Go code compiled with the standard compilers (5g, 6g, 8g, see http://golang.org/doc/go_faq.html#How_is_the_run_time_suppor...). The Plan9 expats are hell-bent on doing things their own way, I am 70% in agreement with them :).
Well, Go code will eventually call into OS-level functions. And when your Linux kernel is built in C, the eventual calls to open(), etc will need null-terminated strings. So you will have to make a copy, eg std::string::c_str().
I was under the impression that std::string::c_str() did not make an allocation, but rather had the scope of the parent std::string, it does in fact return a const char* which can't be freed like normal. Either that, or nearly all code I've seen which uses it amuses memory leaks.
I suppose it could allocate an extra byte anyway, and set that byte to null. It seems like that could cause issues with memory alignment if you go thinking every string is 256-bytes, but it's 257-bytes because of that. Not that big of a deal, of course.
The length value in that case would also be invalid for C usage, because std::string does allow storing nulls inside the string, but it would eliminate the need for allocation.
(you had me questioning my sanity for a moment there)
short answer: I did. It got slower!
They're not doing what you think they are. Each string only gets measured twice.
To factor them out I will need to start allocating extra arrays just to keep their values: the extra malloc and free is slower than measuring the strings twice. I also tried a Reallocing version that didn't need them but it was twice as slow.
I use this code for composing text in places where speed isn't my first concern; especially error messages. But its plenty fast enough for 90% of what anyone would need. You'd rarely be concating more than 6 things together.
note: it's really hard to talk about pointers on HN as you can't type an asterisk without a code block, but it's:
Yes, this. Alloca should only be a couple of instructions on most modern systems, and there is no needed call to free, which is likely the slower of the two calls..
It's the second strlen plus the memcpy right after it that I would consider replacing (with a loop that fuses both). (Though since you say it doesn't matter, never mind...)
If len overflows and wraps (i.e. the sum of string lengths is > SIZE_MAX-1), you'll allocate less memory than is required and memcpy will write outside of buffer bounds.
While that may be possible in some segmented systems, it's worth noting that on just about any modern system having a set of strings bigger than all available memory is unlikely.
Yeah, don't use cstrings. If you're using C, use bstrings. If you're using C++, use std::string. Ignore the extra three bytes of memory overhead and enjoy the extra safety.
Yeah, there might be a few cases were null terminated strings work okay but mostly they are just a recipe for buffer overflows. Seriously, how many years is this going to take to figure out?
Well move semantics were only introduced in C++11. So until C++11 is supported natively everywhere you can't build something like:
std::map<std::string, int>
without paying for a extra copy of the string on insertion. How many years since C++ has existed before they finally allowed for a single copy? Everything is relative. You might never have had a need to worry about that double copy, but there are other people who have.
As long as you're worrying about the double copy, you may as well worry about chasing the pointers in your map through the cache hierarchy. Usually I'd use unordered_map here unless there was a good reason not to.
Deficiencies in strl aside, most C programmers do not realize the target-padding that is going on with the strn family. Discussion is always good, but it will be much more of an uphill battle to introduce new routines than it would be to get strn users to use strl. I've seen too many people bitten by declaring something like:
char buf[65536]; /* 64k is enough for everything! */
The best system involves keeping end pointers, not lengths. Remaining length changes. End pointer never changes. At previous job we converted our whole code base to this system, and it worked great.
I figured the actual length value may be useful without needing pointer arithmetic, but this is a nice solution to the argument taking the size, and the return being the length (size-1, eg p[return]=NUL location.)
I'll consider this idea and write some comparisons to see how it works, thanks.
The information in the strncpy() / strncat() section is incorrect.
Firstly, strncpy() was not designed to be a "safer strcpy()" at all. It was designed for storing strings in the manner of original UNIX directory entries: in a fixed size buffer, null-padded only if the stored string is shorter than the buffer. This is why it fills the buffer with nulls, and why it doesn't necessarily null-terminate the destination.
Secondly, contrary to what the article implies, strncat() does not work this way: strncat() always null-terminates the destination, and it doesn't write any extra nulls.
Thanks, I will make the necessary corrections shortly.
The strncat() behavior is quite confusing. I can understand expecting the buffer to already be null-padded and skipping that step, but always null-terminating the result seems to go against strncpy()'s expected behavior. It also looks mildly dangerous, as the NUL-write can be one after the size given.
It's not confusing when you realise that strncat() is not a concatenating analogue of strncpy(). It's just a version of strcat() that accepts an upper bound on the number of characters copied.
Having the size so fixed, storing the 15th character that would always be null on the disks (and also in memory) was a pure waste. Note that earliest versions of Unix run in less than 64 KB.
When copying the name to a fixed buffer of 14 characters which is to be stored on the disk, padding the buffer with nulls was also not a performance issue and had a benefit of not leaving "junk" in the buffer.
The problems only happen when people expect from such routines with very specific usage scenarios something for which they were not designed.
So the strncpy was never meant to be "a safer veriant for strcpy." Moreover, strcpy_s is also not a replacement for strncpy, but for a strcpy -- it was introduced to be replaced instead of strcpy in the old code bases as a part of reducing the number of security issues in the old code.
Of course there is (in Microsoft's CRT) strncpy_s, the rationale behind all _s functions is here:
"Sized Buffers. The secure functions require that the buffer size be passed to any function that writes to a buffer. The secure versions validate that the buffer is large enough before writing to it, helping to avoid dangerous buffer overrun errors that could allow malicious code to execute. These functions usually return an errno type of error code and invoke the invalid parameter handler if the size of the buffer is too small. Functions that read from input buffers, such as gets, have secure versions that require you to specify a maximum size.
(...)
Null termination. Some functions that left potentially non-terminated strings have secure versions which ensure that strings are properly null terminated."
The article is based on the wrong premises.
Advice: if you work low level with substrings and concatenations, where the performance matters, don't try to fix the str routines, use the pointers. Maintain the pointer to the last character of the area to which you concatenate. Traverse only the characters you need from the source. If you don't care about the performance or exact storage aspects, use much higher level approaches, some string libraries.
I should note that I usually write articles to kick off discussions on my own forum, and then revise them after all input is gathered. They almost never appear on other sites, especially pre-revision.
I'm not saying the strmcpy/cat functions there now are the best solution, and I'm open to hearing what's wrong with them so that we can improve upon them.
Granted, it's a tiny example, but you're going to be writing fewer bugs in the long run if you avoid being clever. So here are two rules to start with:
1. Never put more than one statement on one line.
2. Never use the comma operator outside the "for" syntax.
29 comments
[ 5.1 ms ] story [ 80.7 ms ] threadExcept, interestingly enough, Go code compiled with the standard compilers (5g, 6g, 8g, see http://golang.org/doc/go_faq.html#How_is_the_run_time_suppor...). The Plan9 expats are hell-bent on doing things their own way, I am 70% in agreement with them :).
The length value in that case would also be invalid for C usage, because std::string does allow storing nulls inside the string, but it would eliminate the need for allocation.
And much of libc is not well designed for modern code. Mostky I ptogram to the system call API in C.
short answer: I did. It got slower!
They're not doing what you think they are. Each string only gets measured twice.
To factor them out I will need to start allocating extra arrays just to keep their values: the extra malloc and free is slower than measuring the strings twice. I also tried a Reallocing version that didn't need them but it was twice as slow.
I use this code for composing text in places where speed isn't my first concern; especially error messages. But its plenty fast enough for 90% of what anyone would need. You'd rarely be concating more than 6 things together.
note: it's really hard to talk about pointers on HN as you can't type an asterisk without a code block, but it's:
notI'll consider this idea and write some comparisons to see how it works, thanks.
Firstly, strncpy() was not designed to be a "safer strcpy()" at all. It was designed for storing strings in the manner of original UNIX directory entries: in a fixed size buffer, null-padded only if the stored string is shorter than the buffer. This is why it fills the buffer with nulls, and why it doesn't necessarily null-terminate the destination.
Secondly, contrary to what the article implies, strncat() does not work this way: strncat() always null-terminates the destination, and it doesn't write any extra nulls.
The strncat() behavior is quite confusing. I can understand expecting the buffer to already be null-padded and skipping that step, but always null-terminating the result seems to go against strncpy()'s expected behavior. It also looks mildly dangerous, as the NUL-write can be one after the size given.
http://computing.fnal.gov/unixatfermilab/html/filesys.html
Having the size so fixed, storing the 15th character that would always be null on the disks (and also in memory) was a pure waste. Note that earliest versions of Unix run in less than 64 KB.
When copying the name to a fixed buffer of 14 characters which is to be stored on the disk, padding the buffer with nulls was also not a performance issue and had a benefit of not leaving "junk" in the buffer.
The problems only happen when people expect from such routines with very specific usage scenarios something for which they were not designed.
So the strncpy was never meant to be "a safer veriant for strcpy." Moreover, strcpy_s is also not a replacement for strncpy, but for a strcpy -- it was introduced to be replaced instead of strcpy in the old code bases as a part of reducing the number of security issues in the old code.
Of course there is (in Microsoft's CRT) strncpy_s, the rationale behind all _s functions is here:
http://msdn.microsoft.com/en-us/library/8ef0s5kh(v=vs.80).as...
"Sized Buffers. The secure functions require that the buffer size be passed to any function that writes to a buffer. The secure versions validate that the buffer is large enough before writing to it, helping to avoid dangerous buffer overrun errors that could allow malicious code to execute. These functions usually return an errno type of error code and invoke the invalid parameter handler if the size of the buffer is too small. Functions that read from input buffers, such as gets, have secure versions that require you to specify a maximum size.
(...)
Null termination. Some functions that left potentially non-terminated strings have secure versions which ensure that strings are properly null terminated."
The article is based on the wrong premises.
Advice: if you work low level with substrings and concatenations, where the performance matters, don't try to fix the str routines, use the pointers. Maintain the pointer to the last character of the area to which you concatenate. Traverse only the characters you need from the source. If you don't care about the performance or exact storage aspects, use much higher level approaches, some string libraries.
I should note that I usually write articles to kick off discussions on my own forum, and then revise them after all input is gathered. They almost never appear on other sites, especially pre-revision.
I'm not saying the strmcpy/cat functions there now are the best solution, and I'm open to hearing what's wrong with them so that we can improve upon them.
1. Never put more than one statement on one line.
2. Never use the comma operator outside the "for" syntax.