- 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
My first concern is why "snprintf() and open() and write() and close()" instead of simply "fopen() and fprintf() and fclose()".
Admin
I programmed almost exclusively in C between 1984 and 1990. I saw a lot of that &buf[0] thing back then. I lump it in with things like "if ( a == true )" or using pointers in C++ when a reference is what you want. They are things that work versus best practices and actual understanding.
Admin
No.
They are not pointers to the first object in the array. The name of the array, if interpreted, in and of itself, as a value,"returns" a pointer to the first object in the array, but that pointer can be found exactly nowhere in the array variable.
Admin
You must be new here.
Admin
Oh dear Lord, is this one of those posts where people are going to be more pedantic than Steve the Cynic?
"Actually, in ANSI C, ..."
"Do you mean C99 or any of the ISO/IEC 9899?"
"Arrays don't have a name. A variable is ..."
Admin
Probably just me, but when given a choice between "implicitly refer to the array as the first element" and "explicitly refer to the first element"; I would go for explicitness every damn time.
Admin
But their goal isn't to get the first element- their goal is to access the array pointer itself.
snprintf
takes an array pointer, which by specification is a pointer to the first element, but we expectsnprintf
to operate on the entire array contents, not the first element alone.Admin
Technically no,
snprintf
takes a "pointer to character", akachar*
, which is distinct from "pointer to array", which would be something likechar (*)[100]
.Arrays decay into pointer to their first elements in many contexts, but they are not exactly the same thing.
The technical details of arrays are messy in C/C++.
Admin
Basically, the reason that arrays decay to a pointer to the first element is precisely so you don't have to write that verbose
&array[0]
expression all the time. It's technically correct, but it's not idiomatic.I cringe similarly when I see code like
*(array+i)
instead ofarray[i]
.Admin
O_CREAT without the access right bits, sounds ominous.
Admin
Without the access bits, it uses defaults. Given that it's into
/tmp
which has some special flags set anyway, that's not a problem.Not using
fopen()
andfprintf()
for this sort of task though... not great. Not checking for errors properly... very not great.Admin
TRWTF is C.
For proof: (a) it would be impossible to have the pedantic discussion above about any sensible high-level language. (b) writing a single line to a file (and throwing an exception if there was any error doing so) should be a one-liner
Admin
Along with everything else that everyone else is wondering about, I wonder why they have the 10x retry feature. Yes, it's badly implemented with no waits, no error handling, etc. But why does it exist at all?
Some kind of work-around for a badly handled multithreading issue in this app? A race condition with whatever other app is reading and then deleting
/tmp/conf
? Boilerplate mindless added for ... mindless reasons?Whatever the reason, it doesn't do it for me.
Admin
If I recall correctly, in STL you could get the address of a vector by doing &myVec[0] and then treat it as a C-style array. Maybe that knowledge spilled over here.
Admin
There are no default arguments in C. If you omit the third argument in an open(...,...|O_CREAT) call, you get undefined behaviour because the function tries to access a parameter that is uninitialized (if the file needs to be created).
Admin
Except ignoring the pedantry can bite you on the arse. For example
There's a whole class of Stack Overflow questions that stems from the confusion of what
sizeof array
and sizeof pointer` return.Admin
I’m sure someone will correct me if I’m wrong, but doesn’t snprintf not take in to account the trailing null on the string? So using sizeof(buf) as the size limit might write a null to the next byte after buf?
Admin
You remember correctly. It was in use before data() was introduced. IIRC between C++98 and C++03.
Admin
I do not believe this is quite the WTF it's being presented as as I have had occasion where not quite so crazy code was actually needed.
There's a consumer out there that's going to read the file. If the consumer is currently reading the file the write is going to fail. However, if the consumer simply quickly reads and then closes the retries on the write here make sense--it's looping long enough that if the consumer is working as intended the write will go through (and why we are referencing a global here--this is what I consider a only mildly evil use of globals: configuration information read at startup and never written again. Sure, gathering them up into a structure makes unit testing a lot easier, but otherwise it's not an issue) but if the file is truly locked this will not hang.
My case involved a program that knew nothing of networks and thought it was running on an ordinary PC. If I was quick enough about it I could replace it's entire state save without it noticing--the locked read got a couple of retries before failing and so long as I let go fast enough it would never have a problem. Said program wasn't ours, it couldn't be taught modern concepts.
As for the buffer--while the reference is obviously wrong the rest could be a dingbat consumer issue--it's expecting the text to be padded to some standard size. It reads X bytes rather than to EOF. Again, this could easily be messing with the internals of some other program.
Admin
i[array]
Admin
The reason arrays decay is pointer arithmetic.
Canonically, if x is an array of say, 10 things, x[0] is syntatically identical to *(x+0). Thus, you can abuse things somewhat in C where instead of say x[5], you can do 5[x] instead - this is because x[5] is *(x+5) which can be rewritten as *(5+x), or 5[x].
&x[0] is equivalent to &(*(x+0)), so you're getting X, adding 0, dereferencing the pointer to get at the item, then getting the address of the item back.
Of course, in C++ this nonsense doesn't exist and is a syntax error since C++ expects an array object that can take the [] operator.
Admin
Everything is a one liner, in any programming language, if it has been coded as a subroutine in advance. There is always a point at which the developer has to take up where the library leaves off.
Admin
Along your line of comment, I always love asking C programmers if the following will compile:
char c = 2["Hello!"];
The vast majority say that it won't compile or that it needs to be in a C++ program.
Admin
I'm afraid to ask, but what the hell would that do? I'm guessing nothing that won't summon nasal demons.
Admin
I'm afraid to ask, but what the hell would that do? I'm guessing nothing that won't summon nasal demons.
Admin
It's the same as writing "Hello!"[2].
Admin
Why capture the length written if not going to use it all?
That bugs me. Maybe there was a print or since check, and it got removed? Or maybe that was copied from another place that was checking the len? Or maybe the writer intended to and then forgot?
Also why not break the 10 iteration loop on success? If this can write the file, it's going to write it 10 times? This means it could be possible that 9 writes succeed but the 10th one opens, thus erasing the previously properly written file, and fails silently.
Admin
Which in turn is exactly the same as writing
'l'
Admin
There's some logic behind this : if someone is quickly scanning some code, this syntax makes it obvious we're dealing with the address of the first element of an array. Whereas quickly scanning "buf" requires one to check what is buf and how it is declared.
Now don't kill the messager, I don't necessarily subscribe to that school but there is some argument there, especially for non string arrays.
That said for a string buffer, a printf(buf) is still IMHO the most readable syntax. I personally don't consider a char* to be an "array" but semantically to be a pointer on a string buffer, thus making that &buf[0] nonsense pointless. Just a question of semantics obviously. Ymmv.
Admin
All depends on intent.... "fixed" or "flexible".... using "&buf[0]" and "buf" are the same for an array (in this case of char)... but what if the declaration of buf changes but the usage would e semantically valid...
auto thing = GetThing(); thing.TurnOn();
if thing is currently a light switch, but later refactored to a nuclear bomb -- shrug...
[personally I prefer explicit and strict typing]
Admin
@Ralf ref
I see a
break;
on success. Do you?Admin
Exactly! When I wrote C, I'd use buf to pass the pointer to the whole array for modification, and &buf[0] to pass the pointer to the first element for modification. It's the exact same value, but the difference in notation makes the intent clearer. So, using &buf[0] to pass the array pointer just... hurts my eyes.
Admin
Check sizeof "Hello!"[2] and compare with sizeof 'l' to see a difference.
Admin
Please don't do that, that's an immediate security issue. Use
printf("%s", buf)
instead.Admin
I've been a C programmer for over 20 years and I use the exact syntax as presented here.
As for if ( a == true ). Don't like exactly that syntax but... if ( ( flag & FOO_FL_CREAT ) == 0 ) if ( ( flag & FOO_FL_CREAT ) != 0 ) are good and clear ways to check a bit, even if the == 0 syntax is unnecessary. As an exaple, it allows you to check several bits in one test using the same syntax.
I feel in C it's always best to be as clear as possible. It reduces bugs. Don't do "smart" language tricks just because you can. In the end, it's just a style that sticks when you find the reasons, reasons for you, to stick with it.
Admin
My guess is the program is running under systemd and has its own fake /tmp.
Also:
Nope, the write will still succeed.
Admin
"Why capture the length written if not going to use it all?"
Because write() is declared attribute((warn_unused_result)). If you don't capture the result, the compiler will raise a warning.
Apparently this code was compiled with optimization turned off, otherwise another warning would be raised because len is set but not used.
Admin
I think I know the author of that code!
I've worked with people whose coding, debugging, and validation strategies were all "try it 10 times in a row".
It leaves.... much to be desired.
Admin
The really funny thing about arrays in C is that array[i] is equivalent to i[array], eg it is perfectly valid to write 5["hello there"]. The reason being that a[b] is just syntactic sugar for *(a+b) and + is commutative.