Wednesday, September 21, 2022

Digest for comp.lang.c++@googlegroups.com - 25 updates in 3 topics

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: