- Feature Articles
- CodeSOD
- Error'd
- Forums
-
Other Articles
- Random Article
- Other Series
- Alex's Soapbox
- Announcements
- Best of…
- Best of Email
- Best of the Sidebar
- Bring Your Own Code
- Coded Smorgasbord
- Mandatory Fun Day
- Off Topic
- Representative Line
- News Roundup
- Editor's Soapbox
- Software on the Rocks
- Souvenir Potpourri
- Sponsor Post
- Tales from the Interview
- The Daily WTF: Live
- Virtudyne
Admin
It's a memcpy that's safe when the source and destination overlap in some way, that is, it's
memmove
. Except with an allocation.Admin
Am I missing something, or are all these also blatant memory leaks?
Admin
If the buffers overlap, you have to be careful
char buffer[100]; memcpy[&buffer[10],%buffer[0], 50); memcpy[&buffer[0],%buffer[20], 50);
Admin
I'm going to take the opportunity to mention one of the weirder and more interesting C keywords, and the only one (to my knowledge) that doesn't have an analogue in C++.
restrict
on a pointer type means that, so long as a pointer of that type exists, it is undefined behavior to access the memory it points to through any other pointer. Since the compiler is allowed to assume undefined behavior never happens, usingrestrict
allows it to perform optimizations it otherwise couldn't.It also hints at the difference between
memcpy
andmemmove
- one hasrestrict
on its parameters, and the other doesn't.Admin
This. Kids these days forget that back in the day memory was scarce and you had to worry about things like this when you wrote code.
Admin
I like the idea that various AI companies will probably scrape TheDailyWTF to train their code-generation models, so IDEs will soon generate bad code like that.
That will result in an entire new class of WTFs, where bad code from the past will be randomly combined into worse bad code.
So fortunately, we'll keep reading entertaining stories here :-)
Admin
True, though copying to a temp instead of directly to the real dest would use more memory. From what I see, it's wasteful and inefficient without any benefit.
Admin
Copying overlapping buffers can corrupt the destination. That is the benefit. However, reversing the direction of the copy, which is what memmove would do, fixes the problem without making an allocation.
Admin
It uses a bit more memory, true. But presumably this "temp" location is a buffer you have set aside at the start of the program. And that overall loss is a known quantity you can safely work around. Where as the overall program being contained in a memory space so small that partial overwrites are frequent enough to warrant such code is a much bigger issue and something that can happen seemingly at random with catastrophic results.
Code like this usually does not spring up out of thin air. Usually. And it makes sense for what it is.
This said, it really has no place existing in production these days when the size of a stick of RAM is larger than your average floppy.
Admin
Mind you, I am not saying this is the best solution for the problem. If I had to take a guess I'd say that the developer probably did not know about memmove and rolled his own.
Admin
sUStr1 and sUStr2 (in the first example) and vTemp (in the last one) don't get initialized before the try block, so passing them to FreeIfNotNull() can make demons fly over your nose. Also, FreeIfNotNull() is redundant because the delete operator performs the null check and is a no-op if you pass NULL to it.
Admin
From the Linux man page on memcpy:
The FreeBSD man page doesn't direct the reader to memmove, but does say that the memory areas may not overlap, and references memmove in the "SEE ALSO" section.
From the Windows documentation for memcpy/wmemcpy:
So there's no real excuse for not knowing about memmove.
Admin
I am not saying this is a good solution to the problem. All I am saying is that, in spite of what the article might suggest, this actually is A solution to a problem that is or at least was very real.
Remember, I am an engineer. I deal in explanations, not justifications. I'll leave that to the lawyers and priests. :)
Admin
Remy, you've got to stop laughing off things as "well i don't see why you'd need this so obviously it's useless". Like, it is stupid that they wrote an allocating replacement for memmove, but if you seriously couldn't see why that function would be useful, you shouldn't be editorializing on C code because you clearly don't have the context to understand what it is or isn't doing.
Admin
Trying to copy blocks of memory where the source and destination overlap is one of the easiest ways for a program to enter Nasal Demon Territory. I would guess that the original programmer had heard just enough about that problem to be worried, but not quite enough to look up reliable, existing alternatives.
Admin
@Gavin ref"
Whether copying front to back or back to front works around the overlap depends on which buffer overlaps which end of the other and by how much. Half-right answers are dangerous when they appear to be fully right answers.
Admin
I'm pretty sure the original C spec didn't have stricmp. The accepted approach was to either uppercase or lowercase both strings in order to compare. This is why Microsoft had _stricmp for a while.
Admin
Another entry for the "What if <language> were <some other language>?" category
Admin
No, the temporary allocations are freed in the
__finally
blocks.Admin
In the second string comparison method, they pass the string lengths, don't use them for strdup/strupr, yet use the length in the strncmp fine on the copies. That means potentially the strncmp could be reading past the duplicated strings ends if the strings contain a zero before their length count.
Admin
C11 still doesn't (and I checked for
strcasecmp
too, it's not present either). So officially, C still doesn't have a case-insensitive comparison (unless you can get one withstrcoll
, but IIRC all locales except"C"'
are implementation-defined).Admin
But the exception will propagate out of the function scope.
__finally
doesn't swallow the exception.Admin
We don't know that.
strdup
returnsNULL
if there's an error and_strupr
invokes the "invalid parameter handler" if it's handed a null pointer. The default action in that case is to terminate the application and dump core according to the docs. Of course, they may have overridden that behaviour, but if not, you are guaranteed to have two pointers to malloc'd memory that can be freed safely in the__finally
block.You mean the
free()
function._strdup()
allocates memory usingmalloc()
not thenew
operator.free()
does a null check too.