- 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
Thanks for the clear example. I guess some surprising errors can come up when people aren't paying attention to the data types.
I say of course sizeof() will give different results on a pointer and an array, but that doesn't matter if you know it's a pointer if it's an array and you write correct code. Also, in your example (Anonymoose), the person should have never used '&a' in the first place -- 'a' would have been correct (as you stated). I was thinking from my point of view rather than thinking about how bad someone could mess something up if they didn't know what was up.
Just my lack of experience in working on larger projects...
Admin
I just went and checked, and in my compiler (gcc 4.3), char foo[15] = "data data data" will result in a sequence of move-immediate instructions loading the string one dword at a time into its slot on the stack. The sprintf version emits a constant. If that same string constant is used a lot in other files or even just in other functions, then Winston's change will prevent any potential merging of constants. Which can be a lot of saved space, depending on the specific application in question.
Admin
I agree, but it happens more often then it should. Sometimes junior coders will use a cast to get rid of the warning.
Admin
Except for the part where the address is stored explicitly for b and c but not for a. Did you not notice the mov instructions when calling puts(b) and puts(c) but the lea instruction to call puts(a) (in order to compute the address of a, which it needs to do since it isn't stored anywhere), or are you ignoring that and saying it gets wrapped up in the "essentially"?
Because at least I would say that's a pretty damn big difference in how they are implemented. In source code, that's the difference between *x and x.
For further illustration, print a[2], b[2], and c[2]. I bet you that it will get a[2] from a memory load relative to rbp, but b[2] and c[2] from a memory load relative to another register (that it just finished loading b and c into).
Admin
How does initialising variables straight away cut down on bugs (especially when they are initialised with rubbish)?
Oh, that's right, people suppress compiler warnings, which (with some/many compilers) will give you warnings like 'variable my be used unitialised'...
I have worked on many projects which insist on declaring variables at the beginning of a method, and haveing a clear separation between declarations and functional code - some of these projects have required that variables are declared NULL or 0 (or not at all) and initialised in the code section.
I would say the WTF is (or was meant to be) one of: a) A major effort has gone into rewriting large amounts of code for no reason (and no benefit) b) An attempt at fixing a memory leak has simply changed variables on the stack - rather than variables allocated on the heap (which are the ones that may leak) when they aren't cleaned up properly c) 2 CD's - I want to hear more about how they even fit into the story d) The 'expert' who is such an expert that he absolutely has to modify working code because it doesn't fit in with the way he learnt to code (ie, much like (a) a lot of effort has gone into doing nothing simply because this dev obviously thinks he is an authority on the correct way to code things). e) probably more similar.....
subtle maybe, but almost a WTF - more so than some that have appeared here recently
Admin
Admin
For instance, with the example before with an initialization of a tremendously long string (or structs), gcc emits code that calls memcopy (pulling from a constant in the rodata segment) for the array version.
Admin
The only WTF I can find here is he changed thousands of lines of code just before a release, which is a huge no-no, no matter how trivial the changes to each of the thousands of lines of code are. There's a lot of potential for disaster from human mistakes, especially assuming he did this after it was already QA'd, which I assume it was, considering Christopher was about to burn the application to disk.
Of course, if it wasn't QA'd, then THAT's TRWTF.
Admin
Admin
(1) There are plenty of times when you have something reasonable to initialize with. (I agree that uninitialized is better than "initialized with crap", but I strongly feel that "initialized with something good" is better than uninitialized. I would far far rather see "int x = 0;" than "int x; .... x = 0;" with ... being straight line code.)
(2) It's easy enough to fool the compiler into either omitting such warnings or giving spurious ones.
(3) GCC, an (perhaps unfortunately given how many gripes I have about it) widely-used compiler will not emit said warning without optimization flags specified, so if you're working on developing you won't see them unless you explicitly build a release version. (This is a good idea anyway. That doesn't mean people actually do it.)
Admin
Good point. It was just example code (and to be honest, the kind of devs who don't bother to distinguish between a and &a don't really care about const-correctness). I was trying to highlight the danger of assuming char[] and char * are always interchangeable. The real situation was more like this:
Original code:
New, broken, code:
Admin
Ok, maybe the WTF is that he actually tried to take the initiative and fix the code, rather than to live with its less than perfect syntax.
If we all set about trying to fix our code, you never know what horrible thing might happen next. It might actually work, or it might just not crash enough and then who knows, maybe we'd have to layoff all of those consultants and support people.
Huge chunks of the industry could get ruined. Maybe, even if things got bad enough, we'd have to shut down our blogs complaining about how bad code is.
The consequences of just allowing people to consistently go off and refactor anything at all are seriously dire. Thank goodness he didn't figure out how to use a const to point at it in the data section instead of just duplicating it on the stack, that could have been even more devastating!
Admin
I know you wouldn't make this mistake. I know I wouldn't make this mistake ... well, not more than one or two times, and it only cost a coupla weeks' development effort and nearly losing a half million project ...
But. Would you really trust anybody else not to do it?
If your balls were in the vice?
Brave man, Stig.
Admin
Having a Versioning Control doesn't mean much unless you know how to use it. I have seen many many projects where every version continues on the trunk. For small projects with a single dev this wouldn't be a problem, but when there are 5 or 10 devs implementing different fixes on the main branch (in fact only branch - and not really a branch, since we never branched from anywhere) that when something goes wrong it can be difficult to implement which 'fix' actually caused the problem - especially when they are rolled into the production environemnt in quik succession
Admin
(This is why C99 has "blah" be char*, and why C++ permits that conversion in restricted cases.)
All that illustrates that arrays decay into pointers when you pass them as parameters. Pointers and arrays are still very different to the implementation.
Or do you think that the difference between dereferencing or not is essentially not there, or that the difference between accessing an element with an extra indirection instead of relative to the frame pointer is essentially not there?
I would say that your example no more shows that pointers and arrays are essentially equivalent (because when you pass an array as a parameter it gets converted to a pointer) then an example where you pass an 'int' to a function that takes a 'double' shows that ints and doubles are essentially equivalent (because when you pass that int as a parameter it gets converted to a double).
Admin
The pattern may not be unusual but it's stupid.... Doesn't bool initialise to false? We will always free If in doubt, initialise your bool first....
We could complicate the code by not using a bool
the added advantage is that we can't accidently free param here - if we don't initialise the local variable to NULL we still won't free param (although we might free something we never malloced which is always an interesting thing to do)
Or not. Argue away!
Admin
I see static locals at the pub all the time - some animated locals occasionally too (especially near closing time)
Admin
I'll grant you that this is a draft spec, not the final one (which costs money), but according to http://flash-gordon.me.uk/ansi.c.txt, C89 has the const qualifier. It's possible that this was done for compatibility with K&R C.
It is legal to convert const char * to char * in both C89 and C99. The restriction is that "If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined."
Admin
I see it all the time. Have you never needed to change arrays on the stack other than at initialization??
Admin
Ok, maybe I missed something, but in both examples static data was copied into an array of size 15 allocated on the stack. The strcpy in #1 isn't going to dynamically resize the stack, so both examples use the exact same bss string and the same stack data-struct. Yes?
The only way the size could have doubled is if the original code pointed directly to the string in the static data (bss) section.
If he didn't spec the size of the array, the usage would be less as in:
char data_string[] = "data data data";
but:
const char *data_string = "data data data";
is still much better, I think ...
Admin
Looks like I'm wrong on that one.
Admin
There's a wonderful (albeit old) book that some of you should read called "Expert C Programming - Deep C Secrets" that has a chapter dedicated to [] vs * (entitled "The Shocking Truth: C Arrays and Pointers are NOT the Same!").
Just because we have all become used to treating them the same, doesn't actually mean that they are (although in most cases they are interchangeable)
Admin
Admin
FAIL! Just because under the hood an attempt is made to create the appearance that they are treated the same does not mean that they are the same. In fact, the 'under the hood' stuff is very different
A pointer holds an address, an Array holds data (though this may seem to be the same thing, it is not even close).
Just because you (think you) can treat them as identical does not make them so....
Admin
char trio[9] = "da da da";
Admin
there's nothing inherently wrong with broadly cleaning up code... it's just the people least qualified to do it tend to want to do it the most.
Admin
No Offence 1)don't use terminology that you don't understand. The 'decays' is exactly what is saying that they are not the same thing but will (in some instances) be treated as the same thing. 2) The confusion has clearly already set in. How is the underlying nature identical when you agree that they are clearly treated totally differently? sizeof's handling is a perfect example - you cannot always expect the same behavior by changing an array to a pointer (assuming you hack in the required space allocation etc....
Try it!!!!
Admin
Have a look at that char a[10] you're talking about. Is it really a pointer? Really? "followed by 9 other".... Didn't you say earlier that contiguous is irrelevant (or was that someone else)?
Would you like to borrow a shovel?
Admin
I think that was precisely the OP's point.... the code above is fine (a pointer can point to an array) but if you instead had
Then (obviously, I think) you would have a problem. You cannot allocate a point an array to the pointer but you can point the pointer to the array....
I better read the rest of the posts 'cos I get the impression others may have already 'splained this
Admin
Admin
Hahahaha....Clearly!!!
At best you are not explaining yourself clearly. At worst you assume all sorts of behavior because you have observed some of it.
A Pointer is Not an Array. => Pointers and Arrays are not the same thing
Admin
I bet the article author just got the before and after code snippets reversed.
Admin
Surely there is some question (if you have read this far, at least) over whether his syntax is itself any closer to perfect than the original. If it ain't broke, why fix it?
He took initiative to fix something that in his opinion wasn't done the right way.
Does this mean it was done the wrong way? No! Was it? Depends who you ask.
The existing code was (by most people's standards) perfectly acceptable, and by some people even the correct way to do it. Just because I think things should happen a certain way doesn't (always) make that the right way to do things....
I think this WTF might break records for # of comments...
Admin
THIS!!!
Admin
Why the hell did a manager have commit privileges?
Admin
Everything is TRWTF.
The team for not having a process -- particularly not regression testing -- and adding a new programmer to the mix.
The new guy for making global stylistic changes.
The head developer for turning the new guy loose, promising an untested product to a client, expecting things to be fixed in his absence, and then trying to ship it without reviewing the changes.
The author for including a nonsensical example, and for failing to explain the significance of the second CD.
The commenters for getting into a huge food fight over stack variables based entirely on their conjecture of what imaginary problem might have existed in the code.
Admin
In C or C++, a local char[15] is created by programatically giving offset on the stack, no different than a int. Space is allocated by making sure that the offset of any other stack variable doesn't point to any of those 15 bytes. The only pointers involved are the register(s) pointing to the current position on the stack. A local char * or void * allocates however many bytes are needed for an address and the address value actually resides on the stack, in RAM.
Admin
"Under the hood" is quite nebulous. And besides, char[] isn't the issue, char[n] is.
char[n] is (locally) a stack allocated array. No pointer resides on stack. char[] is a pointer....well because it doesn't make any sense otherwise.
The issue can also become clearer when you start dealing with multi-dimensional arrays. It's been a while sense I've done C or C++, but neither char** nor char[] are equal to char[m][n]; char** points to an array of char* somewhere. char[m][n]; is a block of mn characters that reside on the stack (if local). IIRC, You can't just do char * p = char[m], you have to do char p = &char[m][0];
Admin
Nevermind.... after reading some of the other comments, and remembering some of the bugs I had encountered a long time ago... I'm going to even have to retract this statement.
Admin
It's amazing how someone can be so adamant about something that they are so blatently wrong about.
char* and char[] are definitely not the same. Arrays depreciate to a pointer to their first element (so, using a char[] without the [] will be a pointer to the first element, which has type char*), so in many cases you can use char* with char[]'s, but that's about the only real similarity.
Admin
yeah, the WTF is he did not do his job. He was assign a task of hunting down a tricky memory leak, but he did something else to all the files
Admin
Admin
I think I figured out what the WTF is and why this is a WTF. The company did not have a formal code process. The code was informally in a lock state prior to a major release. The code had been debugged and approved and a small list of issues remained, including a memory leak.
So the idea was to find the memory leak and fix it. Ideally, with a small "obviously correct" change that would minimize the amount of additional testing required.
Instead, he did the complete opposite. He made as many changes as possible, many not anywhere close to obviously correct. This undid all the effort in debugging and validating the code for release and set their release cycle back to the beginning. (And almost certainly neither found or fixed the memory leak.)
And the OP correctly points out that this points out why you do need a code cycle process, ideally with branches and defined phases. And the WTF -- even with this object demonstration of why you need code branches, release freezes, and the like, the solution was to ignore the problem.
Admin
Or using the Boehm/Demers/Weiss (sp?) garbage collector in heap analysis mode. Or just using it period and never missing the painful development overhead of tracing memory leaks ever again, for that matter.
Admin
Yes, 'char[]' is similar to 'char *'. But 'char[10]' is MUCH different than 'char *'. If you don't understand this, please pick up a copy of K&R (you SHOULD know what this is!) and read it throughly.
Admin
That would apply to local static variables - but not to local automatic variables as shown. They are not in the BSS section.
Admin
You'd think.
Last place I worked, they hired someone to "clean up" our code. Admittedly it was in dire need of it - it dated back to 1980, it all lived in one folder, and there were bits of it that predated the widespread use of header files. OTOH, despite being insanely complicated, it worked, and files contained functions that were related to each other; a good programmer could be up to speed in a week or so.
New guy did not take those weeks; new guy jumped straight into rearranging code. Half an hour before he went on a two week vacation, he checked in the changes he'd been working on for weeks. Not only did this involve moving functions into files that made sense to him - who'd never actually used said functions - he managed to give one of his header files the same name as a system header. So it did not build, which we found out on Monday.
Of course we backed out his changes, but our source code was so ancient that we also inadvertently lost one of my changes that I'd checked in mere minutes after he did - something I didn't learn until weeks later, when a supposedly fixed bug reappeared. Of course there was no trace of my original fix.
Not only did new guy not get fired, he's still there. I'm not. But I've got a better job now - and I don't have to work with him.
Admin
This is a better wtf than the wtf!
Admin
Right Mr. Formatter, now tell me how this would work with "cvs annotate" or "svn blame"?
For those who are still stuck in the GUI, these are commands that show you in which revision a given line was changed and are indispensable in hunting down why some line of code is what it is.
Idiots like this Mad Formatter here make these tools bloody useless. What you would see is the revision when the line was last formatted, instead of when it was really changed.
Admin
Why would a bool auto initialize to false? Its not a complex type. Anyhow, the fact that the fexternal code is buggy is not at issue - it was made to counter the assertion that there are no common code patterns in which an uninitialized variables could result in a memory leak.