- Never use strncpy! - 20 Updates
- "Microsoft Azure CTO Mark Russinovich: C/C++ should be deprecated" - 2 Updates
- Functional programming at its best - 3 Updates
| Juha Nieminen <nospam@thanks.invalid>: Sep 21 10:04AM Well, *almost* never, at least. I always thought that std::strncpy() works exactly like std::strcpy(), except that it stops early if the specified count is reached. Turns out that I was gravely mistaken: "If, after copying the terminating null character from src, count is not reached, additional null characters are written to dest until the total of count characters have been written." This means that if you have, let's say, a 1 MB buffer into which you copy with std::strncpy() lots and lots of strings, the vast majority of them very short, expecting it to be efficient... turns out you'll be writing 1 MB worth of data every single time. Which will make the thing quite slow if you weren't aware of this. It's just better to do your own custom version of strncpy() that does what strncpy() should be doing, ie. just stop once the source string ends. I can't find any standard library (C or C++) function that does that, so you'll just have to write your own. (Luckily it's trivial to do.) And while you are at it, you might also want to fix this little problem: "If count is reached before the entire string src was copied, the resulting character array is not null-terminated." Perhaps return to the caller some value telling if the string was truncated. |
| "Alf P. Steinbach" <alf.p.steinbach@gmail.com>: Sep 21 03:06PM +0200 On 21 Sept 2022 12:04, Juha Nieminen wrote: > resulting character array is not null-terminated." > Perhaps return to the caller some value telling if the string was > truncated. Thank you. I was not aware. - Alf |
| David Brown <david.brown@hesbynett.no>: Sep 21 03:24PM +0200 On 21/09/2022 12:04, Juha Nieminen wrote: > resulting character array is not null-terminated." > Perhaps return to the caller some value telling if the string was > truncated. Several of the str* functions in the C standard library are downright silly. There are surprising inconsistencies (strncat guarantees a null-terminated result, strncpy does not), mostly useless return values, and missing functions (no strnlen). There are no volatile versions which could be used to ensure that something like an "memset" to wipe memory would actually be run. Then there are the myths and abuses that are common in real-world code, such as assumptions that "memcpy" runs forwards or works like "memmove". And there are no versions of the moves or copies that work on bigger block sizes - you are dependent on the quality of the compiler to figure out when larger sizes can be used. Good compilers can optimise some of this - if you have several "strcat" calls in a row, gcc can remember the length of each cat as a short-cut for the next one. It can sometimes inline memcpy, and other functions, giving better results. Poorer compilers implement these as external functions in a DLL, with correspondingly bad performance on small strings or memory blocks. The solution, of course, is a selection of non-standard additional string and memory functions that exist in some C libraries and not others, and sometimes have the same name but different functionality in different libraries. Oh, and there's the Annex K "bounds-checking" functions that MS pushed into the C standards that neither they nor anyone else implements, and no one would use even if they /were/ implemented. |
| Bonita Montero <Bonita.Montero@gmail.com>: Sep 21 03:30PM +0200 Am 21.09.2022 um 15:24 schrieb David Brown: > and missing functions (no strnlen). There are no volatile versions > which could be used to ensure that something like an "memset" to wipe > memory would actually be run. ... volatile ist mostly deprecated. atomics are used most of the time where you used volatiles before. And memcpy() memset don't need any behaviour like volatile or atomics since you could add memory barriers and you geet all you need. |
| scott@slp53.sl.home (Scott Lurndal): Sep 21 02:00PM >I always thought that std::strncpy() works exactly like std::strcpy(), >except that it stops early if the specified count is reached. Turns out >that I was gravely mistaken: I've always used memcpy. I've very seldom used any str* function other than strlen and once in a blue moon, strtok. I use snprintf in place of strcat, for instance. |
| Ben Bacarisse <ben.usenet@bsb.me.uk>: Sep 21 03:56PM +0100 > "If, after copying the terminating null character from src, count is not > reached, additional null characters are written to dest until the total > of count characters have been written." ... > "If count is reached before the entire string src was copied, the > resulting character array is not null-terminated." Yes, a famous oddity that stems from one very specific use in fixed-width Unix data structures (the most common use for a file name in a directory entry that had to be zero filled but need not be zero terminated). > I can't find any standard library (C or C++) function that does that, > so you'll just have to write your own. (Luckily it's trivial to do.) A common trick was to write *dest = 0; strncat(dest, source, N); or even (in an expression context) strncat((*dest = 0, dest), source, N) > Perhaps return to the caller some value telling if the string was > truncated. If your C library includes Annex K there is the rather gruesome strncpy_t function. And there is a very widely available, but non-standard, function called strlcat. -- Ben. |
| Muttley@dastardlyhq.com: Sep 21 03:36PM On Wed, 21 Sep 2022 10:04:09 -0000 (UTC) >very short, expecting it to be efficient... turns out you'll be writing >1 MB worth of data every single time. Which will make the thing quite >slow if you weren't aware of this. Useful to know. Wonder why they did it that way? Doesn't seem very logical. |
| Muttley@dastardlyhq.com: Sep 21 03:42PM On Wed, 21 Sep 2022 15:24:01 +0200 >forwards or works like "memmove". And there are no versions of the >moves or copies that work on bigger block sizes - you are dependent on >the quality of the compiler to figure out when larger sizes can be used. Also with some compilers the follow works: snprintf(mystr,some_max_len,"%s etc etc",mystr, etc etc) and with some it just produces garbage in mystr. Which is annoying as it saves a lot of mucking about with strcat when forced to use plain C. |
| Frederick Virchanza Gotham <cauldwell.thomas@gmail.com>: Sep 21 08:59AM -0700 On Wednesday, September 21, 2022 at 11:04:29 AM UTC+1, Juha Nieminen wrote: > "If count is reached before the entire string src was copied, the > resulting character array is not null-terminated." In my last job I programmed cameras with embedded Linux, and I outlawed 'strncpy'. I didn't put it in my own code reviews and I didn't accept code reviews containing it. Very poorly designed. |
| Bonita Montero <Bonita.Montero@gmail.com>: Sep 21 06:10PM +0200 > snprintf(mystr,some_max_len,"%s etc etc",mystr, etc etc) > and with some it just produces garbage in mystr. Which is annoying as > it saves a lot of mucking about with strcat when forced to use plain C. There's no real purpose for things like that so that it doesn't make sense to think about such things. |
| scott@slp53.sl.home (Scott Lurndal): Sep 21 04:12PM >snprintf(mystr,some_max_len,"%s etc etc",mystr, etc etc) >and with some it just produces garbage in mystr. Which is annoying as it saves >a lot of mucking about with strcat when forced to use plain C. It seems fraught to store into the source string. Better use of snprintf (and yes, this example will break if the input buffer is too small; in this example, that is guaranteed not to be the case). Easily fixed if necessary using std::min(bplen,bytecount) in the subtract. size_t c_processor::format_insn(struct _op *opp, c_environment *env, mem_addr_t ip, ulong afl, ulong bfl, c_operand *opa, c_operand *opb, c_operand *opc, bool symbolic, char **bpp, size_t bplen) { size_t bytecount = 0; char buf[10]; char *bp = *bpp; bytecount = snprintf(bp, bplen, "[%1.1lu/%4.4lu]%s:%6.6llu: %4.4s ", p_procnum, p_curtasknum, env->print(buf, sizeof(buf)), ip, opp->op_name); bplen -= bytecount, bp += bytecount; if (!opp->op_noafbf) { bytecount = snprintf(bp, bplen, "%2.2lu", afl); bplen -= bytecount, bp += bytecount; if (opp->op_bfhex) { bytecount = snprintf(bp, bplen, "%2.2lx ", bfl); bplen -= bytecount, bp += bytecount; } else { bytecount = snprintf(bp, bplen, "%2.2lu ", bfl); bplen -= bytecount, bp += bytecount; } } else { if (opp->op_opcode == OP_ACM) { bytecount = snprintf(bp, bplen, "%2.2lx ", afl); bplen -= bytecount, bp += bytecount; } } if ((opp->op_operands > 0) && (opa != NULL)) { bplen = opa->dump(&bp, bplen, symbolic); *bp++ = ' '; --bplen; } if ((opp->op_operands > 1) && (opb != NULL)) { bplen = opb->dump(&bp, bplen, symbolic); *bp++ = ' '; --bplen; } if ((opp->op_operands > 2) && (opc != NULL)) { bplen = opc->dump(&bp, bplen, symbolic); *bp++ = ' '; --bplen; } *bpp = bp; return bplen; } |
| Muttley@dastardlyhq.com: Sep 21 04:19PM On Wed, 21 Sep 2022 18:10:32 +0200 >> it saves a lot of mucking about with strcat when forced to use plain C. >There's no real purpose for things like that so that it doesn't make >sense to think about such things. You don't need to keep demonstrating that you've never done any programming outside of your ivory tower and certainly not in C, we already know. |
| Bonita Montero <Bonita.Montero@gmail.com>: Sep 21 06:27PM +0200 >> sense to think about such things. > You don't need to keep demonstrating that you've never done any programming > outside of your ivory tower and certainly not in C, we already know. If you do things like the above you're in the highest ivory tower ever. |
| David Brown <david.brown@hesbynett.no>: Sep 21 07:20PM +0200 On 21/09/2022 15:30, Bonita Montero wrote: >> versions which could be used to ensure that something like an "memset" >> to wipe memory would actually be run. ... > volatile ist mostly deprecated. Volatile is not deprecated at all. Overly complex expressions involving volatile were deprecated in C++20 as their semantics were unclear. > atomics are used most of the time > where you used volatiles before. Complete nonsense. Volatile accesses and atomics are different things, for different purposes. > And memcpy() memset don't need > any behaviour like volatile or atomics since you could add memory > barriers and you geet all you need. Following a memcpy() or memset() with a memory barrier (a relaxed order full fence) is certainly a possibility. But such fences can be a lot more expensive than using volatile writes. |
| Bonita Montero <Bonita.Montero@gmail.com>: Sep 21 07:33PM +0200 Am 21.09.2022 um 19:20 schrieb David Brown: >> volatile ist mostly deprecated. > Volatile is not deprecated at all. Overly complex expressions involving > volatile were deprecated in C++20 as their semantics were unclear. The semantics of volatile has been reduced with C++20: https://en.cppreference.com/w/cpp/language/cv >> where you used volatiles before. > Complete nonsense. Volatile accesses and atomics are different things, > for different purposes. Before C++11 volatile were partitially used where today you use atomics. But the whole semantics were platform-specific. F.e. there's a mode of MSVC where volatile reads have acquire semantics and volatile writes have release semantics. > Following a memcpy() or memset() with a memory barrier (a relaxed order > full fence) is certainly a possibility. But such fences can be a lot > more expensive than using volatile writes. volatile writes can't be substituted with fences. |
| Andrey Tarasevich <andreytarasevich@hotmail.com>: Sep 21 01:23PM -0700 On 9/21/2022 3:04 AM, Juha Nieminen wrote: > I always thought that std::strncpy() works exactly like std::strcpy(), > except that it stops early if the specified count is reached. Turns out > that I was gravely mistaken: The matter has been explained, explained and over-explained to death already, including here in comp.lang.* newsgroups. It is well-known that `strncpy` has never been intended as a "safe string copying" function. It is a niche function introduced for so called "fixed-width" string support. https://stackoverflow.com/questions/2886931/difference-fixed-width-strings-and-zero-terminated-strings It has never been intended for use with zero-terminated strings. -- Best regards, Andrey |
| Ben Bacarisse <ben.usenet@bsb.me.uk>: Sep 21 09:49PM +0100 > "fixed-width" string support. > https://stackoverflow.com/questions/2886931/difference-fixed-width-strings-and-zero-terminated-strings > It has never been intended for use with zero-terminated strings. Except for the quibble that a null in the source string is respected -- i.e. the destination is considered to be a fixed-width field but not the source. -- Ben. |
| David Brown <david.brown@hesbynett.no>: Sep 21 11:14PM +0200 On 21/09/2022 19:33, Bonita Montero wrote: >> unclear. > The semantics of volatile has been reduced with C++20: > https://en.cppreference.com/w/cpp/language/cv No, the page there says - as I said - that some uses of volatile in complex expressions were deprecated in C++20. >> Complete nonsense. Volatile accesses and atomics are different >> things, for different purposes. > Before C++11 volatile were partitially used where today you use atomics. If you used "volatile" thinking you got the effects of atomic access, you were wrong. Prior to C++11 (and C11), C and C++ did not have any concept of multiple threads - they did not have any need of "atomic" accesses in the language. People who write OS's and similar low-level code needed to implement atomics for the system, and "volatile" was /part/ of those implementations. People writing code for such OS's and using atomics, used the OS's functions, classes, and macros. If you use "volatile" when you mean "atomic", your code is wrong. If you use "atomic" when you mean "volatile", your code will probably work but will be much less efficient. "volatile atomic" is an extremely common combination. > But the whole semantics were platform-specific. F.e. there's a mode of > MSVC where volatile reads have acquire semantics and volatile writes > have release semantics. The details of volatile accesses are implementation-dependent, by necessity. And an implementation is allowed to make them stronger - though doing so is going to be inefficient and encourage misconceptions and unwarranted assumptions. >> order full fence) is certainly a possibility. But such fences can be >> a lot more expensive than using volatile writes. > volatile writes can't be substituted with fences. A fence implies a memory barrier - writes that are logically part of the source code must be completed before the barrier completes. That is part of why you have fences. |
| Keith Thompson <Keith.S.Thompson+u@gmail.com>: Sep 21 04:01PM -0700 > resulting character array is not null-terminated." > Perhaps return to the caller some value telling if the string was > truncated. (Replying in part to things that have been said in other posts in this thread.) strncpy() is not poorly designed. It's quite reasonably designed for the niche purpose for which it was intended, where the source is an ordinary null-terminated string and the target, an N-byte character array, hold a sequence of M significant non-null characters followed by exactly N-M null characters. It is poorly *named*. The name implies that, as strncat is a "safer" strcat, strncpy is a "safer" strcpy. Both strncat and strncpy let you specify the size of the target array, avoiding writing past the end of it, but strncpy treats its target as null-terminated string. I wrote about strncpy here (a lot of what I write has been covered in this thread): http://the-flat-trantor-society.blogspot.com/2012/03/no-strncpy-is-not-safer-strcpy.html Of course this is comp.lang.c++, so you should usually be using std::string, but sometimes you do need to deal with C-style strings. It's unlikely (but still possible), that strncpy() might be the right tool for the job. If it is, thoroughly comment the code so that the next person who maintains it doesn't break your assumptions. A digression: Quietly truncating the output, as strncpy and strncat do, is not always "safer". Sometimes it's exactly what you want, for example if you're printing data in fixed-width columns and it's going to be obvious that something has been truncated. Sometimes silent truncation can be worse than terminating the program, for example if a command string "rm -rf $HOME/tmpdir" is quietly truncated to "rm -rf $HOME/". If your code handles errors, always think about what that error handling will actually do. -- Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com Working, but not speaking, for Philips void Void(void) { Void(); } /* The recursive call of the void */ |
| Richard Damon <Richard@Damon-Family.org>: Sep 21 07:18PM -0400 On 9/21/22 4:49 PM, Ben Bacarisse wrote: > Except for the quibble that a null in the source string is respected -- > i.e. the destination is considered to be a fixed-width field but not the > source. Yes, it is to copy a "C String" (Null Terminated) into a fixed width field. |
| Lynn McGuire <lynnmcguire5@gmail.com>: Sep 21 02:04PM -0500 "Microsoft Azure CTO Mark Russinovich: C/C++ should be deprecated" https://devclass.com/2022/09/20/microsoft-azure-cto-on-c-c/ ""It's time to halt starting any new projects in C/C++ and use Rust for those scenarios where a non-GC language is required. For the sake of security and reliability, the industry should declare those languages as deprecated," he said on Twitter, expressing a personal opinion rather than a fresh Microsoft policy." Wow ! Bold. Lynn |
| "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Sep 21 12:28PM -0700 On 9/21/2022 12:04 PM, Lynn McGuire wrote: > https://devclass.com/2022/09/20/microsoft-azure-cto-on-c-c/ > ""It's time to halt starting any new projects in C/C++ and use Rust for > those scenarios where a non-GC language is required. Ohhh boy. Humm... > deprecated," he said on Twitter, expressing a personal opinion rather > than a fresh Microsoft policy." > Wow ! Holy... MOLY! > Bold. Wow!! Where a non-gc lang is required? Huh? I personally have some issues with GC. I don't really like it. security and reliability? Is it possible to write buggy code in Rust? I have to admit that I never used it. Shit. |
| Muttley@dastardlyhq.com: Sep 21 07:08AM On Tue, 20 Sep 2022 18:37:14 +0200 >That's all a matter of whether you've learned this or not. >If you learned it it's not hard to read. >And I don't know what you do in a C++ group, criticizing C++. Occasionally I learn something new. Unlike you however I'm not blind to C++'s faults and as a paid C++ developer the mess that is being made of the language annoys me. |
| Bonita Montero <Bonita.Montero@gmail.com>: Sep 21 09:51AM +0200 > Occasionally I learn something new. Unlike you however I'm not blind to C++'s > faults and as a paid C++ developer the mess that is being made of the language > annoys me. You _are_ blind with the faults since there's no fault in my code but you just don't know what's going on. |
| Muttley@dastardlyhq.com: Sep 21 08:47AM On Wed, 21 Sep 2022 09:51:03 +0200 >> annoys me. >You _are_ blind with the faults since there's no fault in my code >but you just don't know what's going on. Oh dear. |
| You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page. To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com. |
No comments:
Post a Comment