An 'interesting' thing you can do in C++ - Page 1
Forum section: electronics / general

author: hamster_nz
author_rank: Super Contributor
timestamp: September 11, 2018, 03:42:07 am
content: Just when you think you have seen it all...Code:#include <stdio.h>int main(int argc, char *argv[]) {int a= 0, b = 0;(argc == 2 ? a : b) += 5;printf("%i %i\n",a,b);return 0;}I guess it could be useful...Has anybody used/found a use for this?

author: IanB
author_rank: Super Contributor
timestamp: September 11, 2018, 03:53:38 am
content: What am I looking at? Is there something to see here?

author: hamster_nz
author_rank: Super Contributor
timestamp: September 11, 2018, 04:13:27 am
content: Quote from: IanB on September 11, 2018, 03:53:38 amCode:(argc == 2 ? a : b) += 5;You can use the '?' operator, to select one of two different variables to be the target of the assignment.

author: ejeffrey
author_rank: Super Contributor
timestamp: September 11, 2018, 04:51:42 am
content: The point is I guess that the result of ?: can be an lvalue assuming arguments 2 and 3 are.This is only allowed in C++, not C. I'm not sure what motivation, if any, lead to the change.

author: hamster_nz
author_rank: Super Contributor
timestamp: September 11, 2018, 05:24:47 am
content: Quote from: ejeffrey on September 11, 2018, 04:51:42 amMy guess of the motivation is :"It's not feature complete, until it is Turing Complete"

author: boffin
author_rank: Unknown
timestamp: September 11, 2018, 05:39:45 am
content: And it's single lines of code like that, that make it damn near impossible to debug. When you go back two years later, it's not obvious what is happening

author: westfw
author_rank: Super Contributor
timestamp: September 11, 2018, 05:40:44 am
content: I find the "creeping featurism" of C++ to be ... alarming.Already, the Open Source programs you can find look nothing like the basic C++ that one learns in school, and frequently they don't look much like each other, either. And they keep going "no wait! There's a weird punctuation character we haven't used yet, George at U of X wants it, and it allows programmers to avoid doing an obscure thing bt doing this MORE OBSCURE thing instead!"No wonder so many people stick with C.

author: Berni
author_rank: Super Contributor
timestamp: September 11, 2018, 05:45:36 am
content: Oh wow, had no idea you could do that.But how useful it is? Im not so sure. It does condense down what would otherwise be an IF statement down into just one line, but its also kinda hard to read. Higher level compilers are made for the purpose of making code easier to read so i guess this is a bad feature to add.Then again its probably getting hard to come up with new features ideas for C since all the great features have already been put in there (Features that are in the spirit of C being close to the hardware that is)

author: TomS_
author_rank: Frequent Contributor
timestamp: September 11, 2018, 06:14:06 am
content: Quote from: boffin on September 11, 2018, 05:39:45 amThis ^^Have a read of the MISRA C spec. Sometimes I joke that it should be called MISRAble because its quite restrictive sometimes, but having been bitten by some of the very things it tries to protect against in my own personal projects, Ive taken some of it on board.e.g. I used to think it was nifty not having to wrap single statements following if or loop statements in braces, but then a couple of times while not really thinking about it I added some extra statements and forgot to add the braces and wondered why it wasnt working, only to spend a while debugging and then realising they werent part of the same block of code. So now I avoid that kind of syntax and use braces even for single statements. I could probably partially blame the fact I write a lot of python for that too (because they were indented to the same level).When you work as a professional programmer in any kind of capacity, you come to appreciate that readability trumps all, especially when you may have to re-read code months or even years down the line, or you have to read someone elses code, or someone else has to read your code.

author: maginnovision
author_rank: Super Contributor
timestamp: September 11, 2018, 06:34:56 am
content: That's just for consistency. Some of the guys in charge value consistency over... everything else. So this is an example of that. I probably wouldn't use something like that except for a lambda. Even then rarely.

author: SparkyFX
author_rank: Frequent Contributor
timestamp: September 11, 2018, 08:04:46 am
content: Quote from: hamster_nz on September 11, 2018, 04:13:27 amI like the question mark operator with colon as a short for an "if" "then" "else" construct replacement, it really speeds up things and makes the code fast to write, and even does enable new ways to write code like the example shown.Quote from: boffin on September 11, 2018, 05:39:45 amRelatively understandable for anyone with some Perl knowledge ... not exactly but close.

author: nfmax
author_rank: Super Contributor
timestamp: September 11, 2018, 08:18:19 am
content: So, what do you think would be the values ofaandbafter the following code runs (assuming all three variables are declared asint)?Code:a = 0;b = 0;ptr = 17;(++ptr == 18 ? a : b ) = ptr--;

author: hamster_nz
author_rank: Super Contributor
timestamp: September 11, 2018, 08:50:41 am
content: Quote from: nfmax on September 11, 2018, 08:18:19 amNot sure. Would need to test.Hunch would be that it is evaluated left to right, so a will be 18, b will be zero.But this is more a problem of the ambiguity of using ++ and -- at the same time as acessing the value of a variable.foo(++a, ++a, a--) is much the same.If it was written:(ptr+1 == 18 ? a : b ) = ptr+1;orptr++;(ptr == 18 ? a : b ) = ptr;ptr--;Then there is no problem.

author: SparkyFX
author_rank: Frequent Contributor
timestamp: September 11, 2018, 09:10:56 am
content: rewrite toif (++ptr == 18) { a = ptr-- } else { b = ptr-- };And you are not any smarter.It is not as if this stops someone from doing ambigous stuff, just because it is a different way of writing if ... else.At least i hope i got the topic correct and it is about this operator, not about returning variables for lvalue assignment, which is interesting in itself.

author: brucehoult
author_rank: Super Contributor
timestamp: September 11, 2018, 09:23:14 am
content: Quote from: boffin on September 11, 2018, 05:39:45 amWhy? What's difficult about it?You've always been able to do the same thing as...Code:*(argc == 2 ? &a : &b) += 5;

author: nfmax
author_rank: Super Contributor
timestamp: September 11, 2018, 10:03:50 am
content: The difficulty arises because it is unclear which of theptr-modifying expressions is evaluated first - the one in the rvalue or the one in the xvalue. The preincrement/postdecrement complication is just a red herring, intended to distract!Code should be written to be easy to read and understand!

author: taydin
author_rank: Frequent Contributor
timestamp: September 11, 2018, 10:56:17 am
content: Quote from: TomS_ on September 11, 2018, 06:14:06 amI can second this recommendation. Putting in braces even when not necessary helps clearly see the "if" actions and "else" actions and it really helps somebody else (or you, after you haven't looked at it for a while) maintain your code.Young programmers that are just starting out will often use clever, exotic features of the language in their code, in an attempt to prove that they are intelligent and proficient. But they just end up proving the opposite

author: cgroen
author_rank: Unknown
timestamp: September 11, 2018, 01:06:21 pm
content: Quote from: taydin on September 11, 2018, 10:56:17 amAnd old programmers will often stick with the old ways and totally ignore the new and better features as they are stuck in "good old times"PS: I have been a professional developer for 34+ years, so this probably also goes for me in some way

author: nfmax
author_rank: Super Contributor
timestamp: September 11, 2018, 01:29:02 pm
content: You can pull the same sort of stunt in plain old C:Code:#include <stdio.h>int main(int argc, char** argv) {int array[] = {0,1,2};int index = 0;printf("Before: array = [%d, %d, %d], index = %d\n", array, array, array, index);array[index++] = index++;printf("After: array = [%d, %d, %d], index = %d\n", array, array, array, index);return 0;}Which compiles (with a warning) and runs to give:Code:Before: array = [0, 1, 2], index = 0After: array = [0, 0, 2], index = 2So the rvalue is expression is evaluated first. However, the warning message is revealing:Code:wotsit.c:8:13: warning: multiple unsequenced modifications to 'index' [-Wunsequenced]array[index++] = index++;^ ~~1 warning generated.as it implies this behaviour is not guaranteed.Don't Do Things Like This!

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: September 11, 2018, 01:53:43 pm
content: Quote from: ejeffrey on September 11, 2018, 04:51:42 amIt compiles ~ fine in gcc 4.2/2007Code:#include <stdio.h>int main (int argc, char *argv[]) {int a= 0, b = 0;(argc == 2 ? a : b) += 5;printf("%i %i\n",a,b);return 0;}jorge@unibody:~/kk$ gcc --versioni686-apple-darwin10-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.6)Copyright (C) 2007 Free Software Foundation, Inc.jorge@unibody:~/kk$ gcc kk.c -o kk.outkk.c: In function ‘main’:kk.c:5: warning: target of assignment not really an lvalue; this will be a hard error in the futurejorge@unibody:~/kk$ ./kk.out0 5jorge@unibody:~/kk$ ./kk.out 15 0jorge@unibody:~/kk$

author: grumpydoc
author_rank: Super Contributor
timestamp: September 11, 2018, 01:57:30 pm
content: Quote from: ejeffrey on September 11, 2018, 04:51:42 amNo but in C one can write *(argc == 2 ? &a : &b) += 5;If you really wantYes I have done this, but I'm not proud of it (outside of intentionally obfuscated code).

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: September 11, 2018, 02:03:03 pm
content: Young programmers often ignore the reason why things are the way they are... and tend to use tabs instead of spacesand put blocks in ifs and loops when there's no needbecause a comma operator would dobut they just don't know itCode:while (young) make(), mistakes();

author: rstofer
author_rank: Super Contributor
timestamp: September 11, 2018, 02:08:48 pm
content: I must be getting old! Not only do I not know what some of these constructs will do, I don't even want to know. I would never write something like that! I really don't like 'clever' code.I'm of the opinion that I should write code as clearly as possible and let the compiler worry about optimization.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: September 11, 2018, 02:25:58 pm
content: Quote from: grumpydoc on September 11, 2018, 01:57:30 pmYep. That's clear and unambiguous and compiles without warnings. C++ is obscure, a mine field. Sometimes I wonder if Stroustrup eats mushrooms.

author: dferyance
author_rank: Regular Contributor
timestamp: September 11, 2018, 02:29:18 pm
content: In my experience young programmers don't make mistakes. They pip or npm install 1000 random packages in python or node hoping that some of them will do what they need. Glue together other people's work; writing code is so retro!On a more serious note, I get some of the frustration with the complexities of C++. Despite plenty of the downsides, i've learned to really respect C and C++ for not getting in your way. So many other programming languages and libraries want to "encourage" / force you to code a certain way. This kind of "opinionated software" is considered good. There are vastly different needs between engineering projects. While C++ doesn't help you much or hold your hand, at least it doesn't try to force all your projects to be written like a REST api, or document editor or whatever the language designer likes to code.

author: NorthGuy
author_rank: Super Contributor
timestamp: September 11, 2018, 02:45:47 pm
content: Quote from: GeorgeOfTheJungle on September 11, 2018, 02:03:03 pmCode:while (make(), mistakes(), !dead);

author: nctnico
author_rank: Super Contributor
timestamp: September 11, 2018, 02:48:03 pm
content: Quote from: boffin on September 11, 2018, 05:39:45 amI fully agree. Using these kind of constructs should be banned.

author: HoracioDos
author_rank: Frequent Contributor
timestamp: September 11, 2018, 02:53:06 pm
content: Quote from: GeorgeOfTheJungle on September 11, 2018, 02:25:58 pmI'm not a C++ programmer but one of his quotes would give me goosebumps. "C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off"

author: NorthGuy
author_rank: Super Contributor
timestamp: September 11, 2018, 03:02:08 pm
content: Quote from: nctnico on September 11, 2018, 02:48:03 pmBy whom?

author: dferyance
author_rank: Regular Contributor
timestamp: September 11, 2018, 04:08:53 pm
content: Whenever I've seen people attempt to ban language features, it never really worked well. Some features make sense in certain cases but don't in others. What constructs one developer thinks are horrible, often another developer finds straightforward.I've known programmers who like massive if/else chains to assign variable values. While I prefer simple variable assignment, sometimes with a ternary operator. I find the latter much simpler to reason about and understand as it is a formula instead of a large logic chain. But other developers like to think of it in terms of conditional statements and logic chains. Who's right here? Really there isn't a right or wrong.Its not possible to dictate good code through some codified rules. Good programming takes a lifetime to master. It would be nice if all it took was a few rules. There are very few if any language constructs I would say never to use. Unless something else fully replaces it, they often have their purpose. But it is important to use them as clearly as possible. All language constructs can be abused.

author: taydin
author_rank: Frequent Contributor
timestamp: September 11, 2018, 04:22:33 pm
content: Compared to other languages, C/C++ has very few rules. Successful programmers adopt a set of "best practices" and follow those. The MISRA recommendations, and Scott Meyers "effective C++" recommendations are good starting points for establishing ones own best practices.If one uses all the freedom that C/C++ gives, the result will be a unmaintainable mess, a money pit.

author: rstofer
author_rank: Super Contributor
timestamp: September 11, 2018, 04:41:22 pm
content: Mentioned above and bearing repeating, it remains an exercise to determine which C compilers produce predictable output for some of these more obscure constructs. What happens when the latest and greatest kicks some of these constructs to the curb?I know what ++var and var++ do when they are either on the right or left side of the '=' sign. I wouldn't want to predict what would happen if they are on both sides. I know what I think they should do but I wouldn't want to bet that any particular compiler will do what I think. Moreover, I wouldn't want to predict that they will ALL do what I think.I started reading the MISRA standard and perhaps I'll look into "Effective C++" but, basically, I stay completely away from C++.

author: taydin
author_rank: Frequent Contributor
timestamp: September 11, 2018, 04:53:58 pm
content: I use C++ often, but only to get the following advantages compared to C: Better type safety, templates, exceptions. Haven't used C++ OOP that much.

author: glarsson
author_rank: Frequent Contributor
timestamp: September 11, 2018, 05:00:08 pm
content: Don't try to predict undefined behavior. It is undefined!f(a++,a++); is undefined.a=a++; is undefined.It doesn't matter if the ++ is on the left or right side of = or the number of ++. What matters is the number of times a variable is modified between sequence points. More than one means undefined behavior.

author: NorthGuy
author_rank: Super Contributor
timestamp: September 11, 2018, 05:14:59 pm
content: Quote from: rstofer on September 11, 2018, 04:41:22 pmIn C, the order is not specified - the evaluation may happen in any order. So, the code like this:Code:x[a++] = a++;may be executed as eitherCode:temp1 = a;a++;temp2 = a;a++;x = temp2;orCode:temp2 = a;a++;temp1 = a;a++;x = temp2;or evenCode:temp1 = a;temp2 = a;a++;a++;x = temp2;

author: glarsson
author_rank: Frequent Contributor
timestamp: September 11, 2018, 05:34:07 pm
content: Or even a=42;.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: September 11, 2018, 05:46:35 pm
content: Quote from: glarsson on September 11, 2018, 05:34:07 pmWatt?

author: MK14
author_rank: Super Contributor
timestamp: September 11, 2018, 05:58:42 pm
content: Quote from: GeorgeOfTheJungle on September 11, 2018, 05:46:35 pmWhat I think he means, is that since it is UNDEFINED, anything can happen.

author: grumpydoc
author_rank: Super Contributor
timestamp: September 11, 2018, 05:59:32 pm
content: Quote from: GeorgeOfTheJungle on September 11, 2018, 05:46:35 pm"undefined" means just that, any result is within the guarantees set by the language, including turning your mother green and causing your computer to explode (OK not,terriblylikely but within the permitted behaviour).Quote from: MK14 on September 11, 2018, 05:58:42 pmYup.

author: ajb
author_rank: Super Contributor
timestamp: September 11, 2018, 06:06:53 pm
content: Quote from: grumpydoc on September 11, 2018, 05:59:32 pmWhile anything *could* happen, a sensible implementation should give you theexpected behavior.

author: glarsson
author_rank: Frequent Contributor
timestamp: September 11, 2018, 06:18:45 pm
content: What is the expected behavior? What is sensible from the point of the compiler will vary depending on compiler, CPU type, optimization level, the source code surrounding the undefined code,..., and the programmer having the expectation.Please don't argue that undefined behavior do have a defined behavior.

author: ajb
author_rank: Super Contributor
timestamp: September 11, 2018, 06:42:00 pm
content: Quote from: glarsson on September 11, 2018, 06:18:45 pmThat's explained in the link...

author: grumpydoc
author_rank: Super Contributor
timestamp: September 11, 2018, 06:52:48 pm
content: Quote from: ajb on September 11, 2018, 06:42:00 pmSee, I think turning your mother green is much better than having daemons fly out of your nose.Although, possibly, they could then turn your mother green as a parting shot.

author: glarsson
author_rank: Frequent Contributor
timestamp: September 11, 2018, 07:04:46 pm
content: Quote from: ajb on September 11, 2018, 06:42:00 pmNo. That discussion is about using sizeof() on an undefined struct. This must be diagnosed by the compiler (error message) and will therefore not have the opportunity to invoke undefined behavior at runtime. So no formatted disks.

author: ajb
author_rank: Super Contributor
timestamp: September 11, 2018, 07:09:55 pm
content: Perhapsthis linkwill clear things up.

author: MK14
author_rank: Super Contributor
timestamp: September 11, 2018, 07:16:19 pm
content: Quote from: ajb on September 11, 2018, 07:09:55 pmSorry!, It hasn't.My nose is blocked, I'm coughing, the lights have dimmed, strange things are buzzing round and a picture of my mother has just turned green.

author: glarsson
author_rank: Frequent Contributor
timestamp: September 11, 2018, 07:21:19 pm
content: Perhaps you shouldn't run your code on the DeathStation 9000.http://wikibin.org/articles/deathstation-9000.html

author: MK14
author_rank: Super Contributor
timestamp: September 11, 2018, 07:29:45 pm
content: Quote from: glarsson on September 11, 2018, 07:21:19 pmThat's brilliant!

author: nctnico
author_rank: Super Contributor
timestamp: September 11, 2018, 08:13:21 pm
content: Quote from: NorthGuy on September 11, 2018, 03:02:08 pmBy sane people ofcourseUnless you want to get stuck maintaining the same code forever you better write your code in a way a complete novice can understand it and not screw it up when making changes.

author: ogden
author_rank: Super Contributor
timestamp: September 11, 2018, 08:45:38 pm
content: Quote from: nctnico on September 11, 2018, 08:13:21 pmIndeed. There's very good advise inLinux kernel coding style document:"don’t break the internal parsers of those who will read the code"

author: hans
author_rank: Super Contributor
timestamp: September 11, 2018, 09:07:45 pm
content: Quote from: grumpydoc on September 11, 2018, 01:57:30 pmTo be honest the only objection I would have is the "inline" if statement for resolving the pointer. I don't really have a problem with having somewhat "complex" code on the left hand side of an assignment expression, for example I think this kind of code is quite common:Code:int& getRef(int x, int y ) {return buffer[x*1024 + y];}int* getPtr(int x, int y ) {return &(buffer[x*1024 + y]);}int main(int argc, char** args) {*getPtr(2, 5) = 10;getRef(3, 5) = 10;}I think far worse if you start to mix variable assignment statements with control statements. For example, this is a reconstruction of a code statement I had to work with last year (you could label the source of this project as "PhD-ware"):Code:static auto create(...) {if ((source=new (hw_src_addr) obj<T, ro>)) == NULL) {PRINT_DEBUG("failed!");return std::make_pair(nullptr, nullptr);} else if (hw_src_addr!=hw_dst_addr&&(destination=new (hw_dst_addr) obj<T, wo>) == NULL)) {delete source;PRINT_DEBUG("failed again!");return std::make_pair(nullptr, nullptr);} else {return std::make_pair(source, destination);}}Although it was a C++ project because of the templates, AFAIK this is also perfectly valid C to write. Who writes variable assignments in if statements?

author: NorthGuy
author_rank: Super Contributor
timestamp: September 11, 2018, 09:08:45 pm
content: Quote from: nctnico on September 11, 2018, 08:13:21 pmThat is essentially what is happening now. Everyone thinks he's sane and thus he follows his own rules, which may not be the same as yours, or even doesn't follow any rules at all.Otherwise, we would need a central authority who designates some people as sane, while the rest is treated as insane. You wouldn't want that, would you?Quote from: nctnico on September 11, 2018, 08:13:21 pmThe complete novice should have some level of competence, right? Which means that if he understands the task, he should be able to follow the code. If a ternary operator scares him, may be it wasn't a good idea to hire him in the first place. It is like an electronic engineer not being able to recognize a capacitor.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: September 11, 2018, 09:36:51 pm
content: Quote from: hans on September 11, 2018, 09:07:45 pmI like that, I used to do it very often, now I've changed my mind and don't like to do it anymore. Some compilers (or was it linters ?) don't complain/warn if you enclose the assignment in extra parenthesis, like this:Code:if ((b= c+i)) something();

author: nctnico
author_rank: Super Contributor
timestamp: September 11, 2018, 10:13:16 pm
content: Quote from: NorthGuy on September 11, 2018, 09:08:45 pmCapacitors come in many shapes and sizes and some are not very common. Just like obfustigated code constructs.

author: NorthGuy
author_rank: Super Contributor
timestamp: September 11, 2018, 10:14:59 pm
content: Quote from: GeorgeOfTheJungle on September 11, 2018, 09:36:51 pmIt is not very useful in ifs, because you can always re-write:Code:b = c+i;if (b) something();or using old style:Code:(b = c+i)&&something();but it is more useful in whiles:Code:while (b = some_complex_expression) something(b);where the re-write looks uglier:Code:while (1) {b = some_complex_expression;if (!b) break;something(b);}

author: NorthGuy
author_rank: Super Contributor
timestamp: September 11, 2018, 10:21:04 pm
content: Quote from: nctnico on September 11, 2018, 10:13:16 pmAre you suggesting banning capacitors which are not very common?

author: SparkyFX
author_rank: Frequent Contributor
timestamp: September 11, 2018, 11:01:04 pm
content: Quote from: ogden on September 11, 2018, 08:45:38 pmEvery time something is tried to be defined to the lowest common denominator or an average, someone comes around the corner, yells he does not understand it, therefore does not like it and therefore lowers that level even further. Which is where comments near the code or unit tests might come handy as an avoidance strategy.So at some point that bar needs to get raised, not lowered.

author: SiliconWizard
author_rank: Super Contributor
timestamp: September 11, 2018, 11:09:46 pm
content: Quote from: westfw on September 11, 2018, 05:40:44 amDefinitely. And it hurts readability in an awful lot of cases. Saving a couple lines of code in exchange for one line that almost looks like an obfuscated C contest win? I don't think so.Besides, there are more readable ways of doing the same with pointers, as some have shown.C++ has become such a monster that I highly doubt anyone actually knows and understands all of it. Many people tend to use their own subset of features, which makes C++ a very "fragmented" language in practice.

author: Mr. Scram
author_rank: Super Contributor
timestamp: September 11, 2018, 11:32:10 pm
content: I loathe fanciful notations in code. Too many code monkeys like to stroke their own egos by writing shorthand or unusual bits of code. Code that isn't as legible as it could be by using the most common expression suitable only serves to increase its dependency on documentation which we know is rarely fully up to date. It also decreases the pool of people who know what they're looking at and increases the chances of mistakes being made. Simple code is easy to maintain and that's good for everyone.There are cases where performance van be gained by going the fancy route, but that's generally just a sign of compilers not being up to snuff. Compared to other areas software can be very ego driven. You're not a genius because you contrive things.

author: ogden
author_rank: Super Contributor
timestamp: September 12, 2018, 12:12:07 am
content: Quote from: SparkyFX on September 11, 2018, 11:01:04 pmIt's not about "does not understand", buthow much effortis needed to comprehend particular piece of code. You can test your skills (without yelling!) here:https://www.ioccc.org/years.html.

author: NorthGuy
author_rank: Super Contributor
timestamp: September 12, 2018, 01:16:51 am
content: Quote from: ogden on September 12, 2018, 12:12:07 amYou don't really need to do investigative work on the code.When you design something, you don't design the code, you design data structures, algorithms, protocols etc. Then, once the design is done, you implement it. You can implement it in C, in other language (if you have a compiler), in assembler, whatever you see fit. Except trivial cases, the design is worth more than the implementation. If the design is worthless, the code is worthless too.The next person who's going to work on the code needs to understand the design. The design may be immediately obvious from the code, but it may not, in which case you need comments, documents etc. Once you understand the design, you can work with the code rather easily. But before you understand the design, you have zero chance of understanding the code.If you "inherited" code with unknown design, you can, of course, decipher the code and figure out the design. However, this is a lot of work. And after all this work you may find out that the design was mediocre and wasn't worth the investigative efforts. Often, it is much easier to abandon the code right away, re-design and re-write. But, the company which owns the code regards the code itself as an asset which they paid for and cannot abandon it for nothing. Thus, the company will not want to re-design and re-write, hence hours and hours of tedious work figuring out the code, trying to do some changes, often with unpredictable side effects. Such frankensteiny approach makes code more complicated, and often worse than the original. The code also gets more expensive, because all the countless hours spent on the unruly code are counted in. The company will value such code even more and will have even less desire to re-design and re-write, so the horrors go through years, through newly hired programmers, with no chance to stop. Meanwhile, parted from the original design, the code gets worse and worse, and working with it gets harder and harder.Does it really have anything to do with specific constructs used in the code? I don't think so.

author: amyk
author_rank: Super Contributor
timestamp: September 12, 2018, 01:56:13 am
content: For what it's worth, I'm a long-time C programmer and had no trouble at all understanding what the code in OP did. The ternary lvalue is a GCC extension for C, I believe.I don't get all the squeamishness (for lack of better term) about the ternary operator. It literally looks like you're asking a question and choosing one of two alternatives. Not hard at all.Templated classes and nested templates, on the other hand...

author: bson
author_rank: Unknown
timestamp: September 12, 2018, 03:06:44 am
content: Quote from: ejeffrey on September 11, 2018, 04:51:42 amProbably so you can easily do templates that have lvalues as type parameters.Code:// Function that returns the maxtemplate <typename T>T max(T a, T b) {return a > b ? a : b;}// Function that returns the mintemplate <typename T>T min(T a, T b) {return a <= b ? a : b;}uint32_t num_nodes;uint32_t num_values;void clamp() {max<uint32_t&>(num_nodes, num_values) = min(num_nodes, num_values);}

author: bsfeechannel
author_rank: Super Contributor
timestamp: September 12, 2018, 05:10:45 am
content: Quote from: GeorgeOfTheJungle on September 11, 2018, 01:53:43 pmInteresting. gcc-4.8 could not be tricked.Code:$ cat > ternop.c#include <stdio.h>int main (int argc, char *argv[]) {int a= 0, b = 0;(argc == 2 ? a : b) += 5;printf("%i %i\n",a,b);return 0;}$ make ternopcc ternop.c -o ternopternop.c: In function ‘main’:ternop.c:5:25: error: lvalue required as left operand of assignment(argc == 2 ? a : b) += 5;^make: **  Erro 1g++-4.8 has no problem with it. As expected.Code:$ mv ternop.c ternop.cpp$ make ternopg++ ternop.cpp -o ternop$ ./ternop0 5$ ./ternop 15 0

author: nfmax
author_rank: Super Contributor
timestamp: September 12, 2018, 07:55:07 am
content: Quote from: NorthGuy on September 12, 2018, 01:16:51 amThe problem is the design is what the program is intended to do, whereas the code is what it actually does. They are not necessarily the same thing.

author: nfmax
author_rank: Super Contributor
timestamp: September 12, 2018, 08:03:34 am
content: Quote from: amyk on September 12, 2018, 01:56:13 amIt's not the issue of the ternary operator: the problem is with expressions occurring on both sides of assignment operator, that have side effects. Another example, valid in C & C++:Code:int a = 0;a += ++a;Will the pre-increment happen before or after the assignment operator takes the 'initial' value ofa?

author: ogden
author_rank: Super Contributor
timestamp: September 12, 2018, 08:52:52 am
content: Quote from: NorthGuy on September 12, 2018, 01:16:51 amRight. What if your do not have to read the code or when you don't have code at all?Obviously code shall be easily readablewhen you need to understand it, for whatever reason (bugfixing, quality control, taking over the job and so on) which is not that actually important here in this context.

author: Siwastaja
author_rank: Super Contributor
timestamp: September 12, 2018, 10:18:16 am
content: Quote from: ogden on September 12, 2018, 08:52:52 amOf course.I think thepointNorthGuy is making that we tend to give way too much attention to the readability of small pieces of expressions, while the "big picture" is orders of magnitude more important. Design, and documentation thereof. Data structures, algorithms (which are described in comments preceding functions, and at the top of .c file -whatthe code does)...I have had to modify/fix some projects with almost zero "hard-to-read" expressions, with all "style guides" followed, but due to bad higher-level design and documentation, and general bloatness, they have been major PITA - we easily talk about several human-months of work just to fix a trivial bug. OTOH, I have seen projects that reek like "fancy C oneliners", which look visually like crap and every line out of ten requires serious brain power to decode. Guess what? These projects can beorders of magnitudeeasier to work with, than the previous type!This is because we are talking about two different problems which are not strongly correlated, and while both are meaningful, they are in a completely different level of being a problem.Consider fixing a simple bug or adding a trivial feature by a fairly experienced outsider. A broken-by-design implementation with neat coding style easily takes 100-200 hours to do anything with; a properly designed, but crudely written ?:*(++[]--)&* mess may take 10 hours of brain pain to do the same. In an ideal world, an ideal implementation would be studied and fixed in a few hours.- - -When in university, I was disappointed at the basic programming courses that mostly focused on bashing "goto" and the ?: operator, trying to prove that ?: automatically and always generates unreadable code; while things like higher-level software design, unit design and testing, documentingwhatthe code is doing in the comments was completely missing from the course."Software design" was a separate course, of course, but it was all about writing long user case documents (with little touch to the actual user or the actual coder), drawing UML diagrams and generating hundreds of pages of free-form textual representation. There is a glaring canyon between these two worlds:1) Abstract, very high level "design" world, often not performed by actual coders, nor someone close to actual end-users. A lot of word documents and powerpoint slides. UML class generation happens with too little surface to the actual implementation, in a waterfall model (first two years drawing UML diagrams, then two years writing the implementation).3) Implementation of said model (which cannot be modified at that point), where things like ?: operators or using "break" are The Evil, and if the end result is unmaintainable mess, it's not because of 1), but because of the bad coder who used ?: operator.I can see this sounds quite extreme, but this line of thinking really happens and we really did see it in university.I left 2) out. It would be a practical level of mid-level design, performed by the coders who are responsible for writing the code. There, the programmer thinks about their data structures, is allowed to modify the higher-level implementation specification, is given freedom and responsibility of designing the class structure, documents their modules and functionsin the source code comments(not Word document), to make the code readableas a whole. And not only readable - but small and efficient, something that can only be done when the person closest to the computer running the resulting binary is allowed to participate in the design.Single "hard to read" constructs are a ridiculously minor detail in this big picture.Of course, a good programmer makes a good design, documented properly, and,in addition, doesn't produce a single line of hard-to-read *?:&[++--] code.On the other hand,banningcertain constructsneverhelps anyone produce better code, because the better code exists on a higher level than that. If a certain person produces total unreadable crap, after banning the most ugly constructs, the code will still be crap; only a slight improvement is seen. But now it passes the stupidly simple and trivial corporate "style requirements", and we can pretend everything is OK.There is a case of experienced programmers sometimes using constructs they understand very well, but beginners have trouble with. Banning such constructs may improve code readability, butvery slightly- after all, the beginner learns these constructs fairly quickly because they are just simple syntax, and their number if very limited - theycanbe learned easily, unlike complex computer program design, which is a difficult task.The world is full of totally shitty computer programs, yet the discussion about the reasons always ends up to some trivial syntactical detail which is almost meaningless in the big picture.

author: newbrain
author_rank: Super Contributor
timestamp: September 12, 2018, 01:02:17 pm
content: Quote from: nfmax on September 12, 2018, 08:03:34 amNo.The syntax is correct, granted.But this not valid C or C++ in any standard revision, as it is the textbook example of undefined behaviour.E.g., from theC99 standard, Annex J.2:QuoteIn this example 'a' is clearly modified twice, and the expression does not contain any internal sequence point.The ?: ternary operator, on the contrary, has a sequence point at the '?', so your original example:Code:(++ptr == 18 ? a : b ) = ptr--;is perfectly defined.Would I write code like this? Maybe under torture. But it's correct code nonetheless.

author: nfmax
author_rank: Super Contributor
timestamp: September 12, 2018, 01:34:04 pm
content: In C, the first example is valid (compiles with warnings) but has undefined results. The second is not valid (fails to compile). Itisvalid C++, but still generates a warning because the value ofptris modified on both sides of the assignment statement, and neither C nor C++ guarantees the order of modification.

author: Mr. Scram
author_rank: Super Contributor
timestamp: September 12, 2018, 02:02:46 pm
content: Quote from: Siwastaja on September 12, 2018, 10:18:16 amWhy do you feel "break" is evil? When applied correctly it can make code more legible. The MISRA coding guidelines have been updated to the same effect. I agree that using "break" in long or nested loops can be confusing and obfuscating.

author: newbrain
author_rank: Super Contributor
timestamp: September 12, 2018, 02:19:42 pm
content: Quote from: nfmax on September 12, 2018, 01:34:04 pmOf course the second example is only for (recent) C++, no doubt about it: in C the ? operator does not return a modifiable lvalue.What we disagree upon is the "valid" adjective for the first example, unfortunately the standard does not define it unambiguously.That code violates a "shall" (the one in the second clause of 6.5), so it has undefined behaviour - I think this is not in dispute.According to my reading of Ch 4, a program that contains UB is neither (strictly) conforming nor correct.This leaves us with only syntactical validity, i.e. the compiler can make sense of the sequence of symbols - it's even kind enough to emit a non mandatory diagnostic for the UB - but I would not call it "valid".Moreover, note 8 of 5.1.1.3 clearly states that implementations can successfully translate an invalid program.I would simply call it wrong.But we are probably just saying the same thing, just with different words...BTW: I've always been puzzled by people who expects a specific result from code like that, to my mind it has always looked meaningless, even before reading the standard.

author: nfmax
author_rank: Super Contributor
timestamp: September 12, 2018, 02:30:29 pm
content: I don't actuallyrecommendusing code like either of the two examples!Quote from: newbrain on September 12, 2018, 02:19:42 pmThe take home from this: treat all warnings as errors (since the warning message isrequiredby section 5.1.1.3 itself).

author: Siwastaja
author_rank: Super Contributor
timestamp: September 12, 2018, 02:35:56 pm
content: Quote from: Mr. Scram on September 12, 2018, 02:02:46 pmTotally beats me. That's what everybody were taught, on compulsory programming introduction courses (in C++), in the university I attended. (To be fair, we wereactuallytaught that "break" is notasEvil as goto or ?:, but stilla bit Evil.)Of course, writing practical code in real life while completely avoiding ?: or break or even goto is nearly impossible, or at least detrimental to code quality and readability, as well as psychological productivity.That said, now I remembered an additional detail: they had an exception that itisallowed to use break inside switch-case, but only there. They figured out switch-case doesn't work without it, and were not bold enough to claim switch-case is Evil.This is all, but at the same time, it's quiteas well.(Also note that creating such "order" of Evil-ity is ridiculous. In some contexts, goto results in much more readable result than break, yet it's down the list of things to be "frowned upon". It's always dependent on usage. But I guess we have no debate about this here.)

author: NorthGuy
author_rank: Super Contributor
timestamp: September 12, 2018, 04:17:28 pm
content: Quote from: newbrain on September 12, 2018, 02:19:42 pmCompiler may not know that the program is invalid. For example:Code:x = x++;This code is invalid (and result is undefined) only if a == b. Otherwise, the code is absolutely fine. It would be possible to come up with rules which make the result always defined. But this would be a burden on the compiler. The compiler would have to generate the code which is less efficient to handle many unusual situations which would never happen in real life. Instead, the standard deems the result undefined to free up the compiler and let it generate more efficient code. At the same time, the programmer's mind is freed from remembering extra rules. It is up to the programmer to ensure that the situation where a == b never happens.It is the same as division by zero - the compiler simply generates the division code and it is up to you to make sure that you don't divide by zero.If you feel the checks are needed, it is up to you to remove the ambiguity, such as:Code:y = x++;x = y;

author: nfmax
author_rank: Super Contributor
timestamp: September 12, 2018, 05:02:25 pm
content: Ah yes, the aliasing problem. Interestingly, with default switches this code doesn't generate a warning. On my system, the assignment occursafterthe post-increment, thereby clobbering it.Code:byrd:ctest max$ gcc --versionConfigured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/4.2.1Apple LLVM version 9.1.0 (clang-902.0.39.2)Target: x86_64-apple-darwin17.7.0Thread model: posixInstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/binI think we can add the pre/post increment/decrement operators to the Dodgy C Constructs list! You can always write:Code:array = array;array = array + 1;Which is unambiguous

author: newbrain
author_rank: Super Contributor
timestamp: September 12, 2018, 05:38:45 pm
content: Quote from: NorthGuy on September 12, 2018, 04:17:28 pmOf course!Quote from: nfmax on September 12, 2018, 05:02:25 pmI don't see anything dodgy in those operators, following the line of thought "It's dodgy as it can result in undefined behaviour" one could also include all the arithmetic operators in the category.As NorthGuy correctly described, UB is in most cases not something that resulted from sloppy thinking but enabled the compilers and the resulting code not to be burdened by excessive runtime checks, trading safety for efficiency.Fortran, for example, has a different set of rules for aliasing, allowing an even higher degree of shortcuts and optimizations (and biting my ass more than once in faraway times).If one wants thoughtless safety, other languages are more suitable.

author: RoGeorge
author_rank: Super Contributor
timestamp: September 12, 2018, 05:45:44 pm
content: Quote from: NorthGuy on September 12, 2018, 04:17:28 pmIf a = b, then the value of x will remain unchanged.The rule I know is thatthe right side of an assignment is evaluated first, then the left side, then the assignment is made.x = x++ is the same as n = n++. Let's say n=10.- First we read the value (10) stored at address n, in order to further use it in the calculation of the right side expression. Other said, we make a copy of n.- Then, we increment the content of address n. Now n will contain the value 11- Then we continue the evaluation of the right side expression (in our case, nothing to do), so the right side result is 10. In memory, at address n is stored 11.- Now, it's time to evaluate the left hand side of the assignment (For n=n++, there is nothing to evaluate in the left side. For x=x++, in the left side we will evaluate &x+y*sizeoff(x) )- Then, the assignment: Our n, which is now 11, will receive the value calculated in the right hand side, which is 10.So, we will end up with an unchanged value. No undefined situation. What am I missing?Isn't the right side of an assignment always evaluated first?

author: dmills
author_rank: Super Contributor
timestamp: September 12, 2018, 05:59:04 pm
content: That is NOT the rule in C!The problem comes from the fact that all side effects are only guaranteed to be evaluated before the next sequence point, and an assignment (=) is NOT a sequence point!Consider the canonically broken i = i++;There is only ONE sequence point, the semi colon.i will be assigned to a temporary.i will be incremented sometime before that sequence point.the temporary will be assigned to i sometime before that sequence point, but there is no guarantee about which order these last two operations will occur in.Here be Nasal Demons.Regards, Dan.

author: glarsson
author_rank: Frequent Contributor
timestamp: September 12, 2018, 06:00:37 pm
content: Quote from: RoGeorge on September 12, 2018, 05:45:44 pmYou are missing what is written in the C standard. Left vs. Right does not matter. The simple rule is that you may not modify s variable more than once between sequence points. The assignment is not a sequence point; the next one is at the semicolon.For a=a++; the compiler is allowed to Instruct the processor to execute a=a and a++ at the same time for efficiency. In this case this creates a race condition or nasal daemons.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: September 12, 2018, 06:04:00 pm
content: Yep, that makes sense IMO, and in the same vein (1st evaluate the right side, then the left side)Code:#include <stdio.h>int main (int argc, char *argv[]) {int a= 0, b = 0;a+= ++a;b+= b++;printf("%i,%i\n",a,b);return 0;}Gives 2,1. At least with my compiler!Then there's whatever the C Standard says, but out of comp.lang.c and comp.unix.programmer, who reads that?

author: ralphrmartin
author_rank: Frequent Contributor
timestamp: September 12, 2018, 06:19:03 pm
content: You could do this in Algol68, as in 1968...

author: glarsson
author_rank: Frequent Contributor
timestamp: September 12, 2018, 06:21:04 pm
content: Quote from: GeorgeOfTheJungle on September 12, 2018, 06:04:00 pmThat's stupid.You might get another result in the next program because the compiler is allowed to generate different code depending on circumstances, e.g. if you add more complex code near the ub-invoking code, the compiler might run out of registers and generate different code.

author: newbrain
author_rank: Super Contributor
timestamp: September 12, 2018, 06:24:32 pm
content: Quote from: GeorgeOfTheJungle on September 12, 2018, 06:04:00 pmWho wants working code.The very case of a[ i] = a[i++] changed behaviour in a recent(ish) update of gcc, at the same level of optimization.I still remember my friend complaining loudly that "the new gcc in Ubuntu is broken"...Edit: [ i ] eaten bydogforum SW

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: September 12, 2018, 06:26:30 pm
content: Quote from: glarsson on September 12, 2018, 06:21:04 pm$gcc --versionCopyright (C) 2007 Free Software Foundation, Inc.Go tell them!

author: newbrain
author_rank: Super Contributor
timestamp: September 12, 2018, 06:28:00 pm
content: Quote from: GeorgeOfTheJungle on September 12, 2018, 06:26:30 pmObvious troll is obvious.

author: glarsson
author_rank: Frequent Contributor
timestamp: September 12, 2018, 06:41:29 pm
content: Quote from: GeorgeOfTheJungle on September 12, 2018, 06:26:30 pmNo. I didn't say the compiler was stupid. It is allowed to give that result. I meant that it is stupid to try to figure out how a compiler handles undefined behavior. That information is useless as you shouldn't use it/depend on it.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: September 12, 2018, 06:52:37 pm
content: But the C standard != gcc documentation. Perhaps it's un-undefined somewhere there (in gcc). My gcc 4.2 also lets me use an rvalue as lvalue (the OP code runs fine), and that's not in the standard, so... what happens when UB (by the std) becomes defined somewhere else (e.g. gcc)? Can one use that or it's a sin?Edit: Not this case.$ gcc -O3 -Wsequence-point kk.ckk.c: In function ‘main’:kk.c:5: warning: operation on ‘a’ may be undefinedkk.c:6: warning: operation on ‘b’ may be undefined

author: RoGeorge
author_rank: Super Contributor
timestamp: September 12, 2018, 06:55:13 pm
content: Now I noticed that the title says C++, and I was thinking about C all the time. I don't know about C++. My bad, sorry.Please let me ask again: I always thought that in C (not C++), the postfix ++ is anatomicread-modify-write operation. Being atomic will also imply to calculate the increment on the spot, the ++ can not be postponed until the next ";". Example:x=10;y=x++ + x++;will end IMO with x is 12, and y is 21 (NOT 20).Is this correct for C?Is this undefined for C++?

author: newbrain
author_rank: Super Contributor
timestamp: September 12, 2018, 07:04:15 pm
content: Quote from: RoGeorge on September 12, 2018, 06:55:13 pmNo, it's not atomic. Its only guarantee is that the side effect (increment) will happen as if it was carried out before the next sequence point.If the incremented variable is volatile, we have the guarantee that it actually happens before the next sequence point.(But I should check the fine print).So, it's never correct, in neither C or C++.BTW, this is really, really, the most common (and asked about) case of UB, and it's clearly spelled out and described in the C standard as such (6.5, J.2).Maybe GeorgeOfTheJungle is right, and I'm the only one who enjoys reading the standard (and the rationale! that was really a good read!)

author: Siwastaja
author_rank: Super Contributor
timestamp: September 12, 2018, 07:21:44 pm
content: Even if you didn't want to read the standard, these particular Undefined Behavior (as well as aliasing rules) examples are discussed to deatheverywhere on the 'net; Google easily gives you answers, rationale, and discussion.C is an interesting language because it's quite braindead..ish, and the standard more or less sucks; but it's not catastrophic enough to be unusable, and most "elegant" "replacements" end up being more fundamentally problematic (or just don't gain traction); so C remainssurprisinglyusable decade after decade, in unforeseeable future; and I don't think it's completely due to legacy code. A lot of completely new projects, even from young, new developers, spawn in C all the time. (As a side note, I almost exclusively write in C, as well.)Same is true with C++, except with 100x more bloat, 100x more uncertainty and 100x more issues - and even less "elegance". Unlike C, where the horror show has stalled to a stable state, in C++, the horrors continue developing to the Next Levels all the time, directed by a very productive committee. Somehow, it still remains both usable and popular as well. This is almost magical, IMHO.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: September 12, 2018, 07:39:07 pm
content: http://gcc.gnu.org/onlinedocs/gcc-3.1/gcc/Warning-Options.htmlQuote from: Free Software Foundation

author: glarsson
author_rank: Frequent Contributor
timestamp: September 12, 2018, 07:44:31 pm
content: C is not braindead unless you have the wrong expectations. It was designed to write operating systems, device drivers etc. It will live on for a long time as it is very hard to design a competing language that is sufficiently better.In my view C is like a small sports car with manual gearbox, no ABS, no airbags, no traction control etc. If you can handle the challenge you will drive very fast on the twisty roads, but it will not be a SUV with all the latest driver aids. If you need that kind of support you need a different language, e.g. Ada. :-)

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: September 12, 2018, 07:48:31 pm
content: Quote from: newbrain on September 12, 2018, 07:04:15 pmI have never read it. In my defence, there was no such thing when I started (1986) programming in C (only the C book by Kernighan). But I've had to read the EcmaScript one more than once, and that's a brick.

author: glarsson
author_rank: Frequent Contributor
timestamp: September 12, 2018, 07:55:36 pm
content: I have read the C standard. I bought a copy from ANSI. Sometimes I think I'm the only one that have read it...

author: newbrain
author_rank: Super Contributor
timestamp: September 12, 2018, 08:04:26 pm
content: Quote from: glarsson on September 12, 2018, 07:55:36 pmAnt that makes two!Sweden - Rest of the world: 2 - 0

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: September 12, 2018, 08:17:29 pm
content: Wow, this is even cool, look:Quote(fromhttp://www.open-std.org/jtc1/sc22/wg14/www/docs/n927.htm)Code:#include <stdio.h>int main () {int a, b;b= (a = 1) * (a = 2);printf("%i,%i\n", a, b);return 0;}$ gcc -O0 -Wsequence-point kk.c -o kk.outkk.c: In function ‘main’:kk.c:5: warning: operation on ‘a’ may be undefined$ ./kk.out2,4With a well chosen bunch of these ~WTFs someone could write a winner for the obfuscated C contest!

author: ejeffrey
author_rank: Super Contributor
timestamp: September 12, 2018, 08:29:26 pm
content: Quote from: GeorgeOfTheJungle on September 12, 2018, 06:52:37 pmNote that you can't rely on this warning. The compiler will try to help if you ask but it can't identify all undefined behavior.Quotegcc doesn't define the behavior. When you write code with potentially undefined behavior gcc will pick some concrete implementation that is correct if no undefined behavior actually happens. It makes no promises to do the same thing every time and in every circumstance. That's the devil of undefined behavior: it might work the way you expect most of the time but can change based on unpredictable circumstances.The fundamental issue here is that there are a lot of machine optimizations that are non-obvious, especially on modern superscalar processors. If you want the best performance you have to give the compiler some freedom to rearrange things without changing the meaning of the code. Its hard to do this without creating some undefined behavior.The real problem is with aliasing. a = a++ is undefined by the standard but would be easy for the compiler to detect and do something sensible. The problem is when you have a = b++ where 'a' and 'b' are expressions that might or might not alias to the same variable. The C standard says that the compiler has to assume that any two expressions of the same type might be aliases (unless it can prove otherwise) but that only blocks reordering across sequence points. Between sequence points it is your job to make sure that you don't violate the rules.

author: RoGeorge
author_rank: Super Contributor
timestamp: September 12, 2018, 08:29:38 pm
content: I'm not a programmer, so I go to standards only in very rare occasions, when something does not work how I expected. So far, my thumb rules kept me safe, especially because I don't do stunts, yet I was so wrong. Thank you all for helping me clarify this.Volatile or not, it does not work how I supposed.I just tested now, and to my surprise if a=10, then any of the followingb=a + a++b=a++ + ab=a++ + a++returns b as 21, no matter if a is volatile or not, which is quite a surprise for me. Thanks again!Especially the first one, seems straight broken. I would have swear it should return 20, not 21.

author: newbrain
author_rank: Super Contributor
timestamp: September 12, 2018, 09:42:20 pm
content: Quote from: RoGeorge on September 12, 2018, 08:29:38 pmSorry for bringing up the volatile qualifier, is a bit of a red herring in this case.What I poorly tried to explain is that inx++the actual increment might happen in any moment, even after the semicolon sequence point, as long as the result of the program is the same (when I checkxsometimes after this expression, I find it incremented).Ifxis volatile, this side effect is guaranteed to happen before the sequence point.From "inside" the program, in this case, the volatile qualifier makes absolutely no difference.See also the last paragraph of ejeffrey's post.I don't see why you would expect the first case to be 20, there is no strictly defined right to left or left to right evaluation order in C (APL, e.g., is strictly right to left, forth left to right).And no, using parenthesis to force things is useless.The most important concepts here are:Side effects:Things that happen to your variables, such an assignment or an increment, also a read if the object is volatile.Sequence points:They represent the anchor points that define the ordering of the side effects.Semicolons, but also the ? in the conditional operator, the comma operator, && and ||, function calls.Clause 2 in 5.1.2.3:QuoteNote thatbetween two sequence pointsthe standard does not impose any ordering requirement.Don't do stunts (such as the ones you posted), and you'll be mostly safe.

author: NorthGuy
author_rank: Super Contributor
timestamp: September 12, 2018, 11:14:33 pm
content: Quote from: glarsson on September 12, 2018, 07:44:31 pmNo, that's assembler

author: brucehoult
author_rank: Super Contributor
timestamp: September 12, 2018, 11:23:39 pm
content: Quote from: glarsson on September 12, 2018, 06:00:37 pmNo, that's not nasal demons. After "a=a++;", a will either have the same value as before, or will be incremented by 1. There are no other options. The program will not crash. Your computer will not catch on fire. Demons will not fly out of your nose (or your arse). The result is boundedly undefined.

author: MK14
author_rank: Super Contributor
timestamp: September 12, 2018, 11:41:55 pm
content: Quote from: NorthGuy on September 12, 2018, 11:14:33 pmAssembler = Motorbike.That is why windows can't be written in assembler. Because motorbikes never (usually) have doors, so there is no room for any windows.

author: NorthGuy
author_rank: Super Contributor
timestamp: September 12, 2018, 11:50:47 pm
content: Quote from: brucehoult on September 12, 2018, 11:23:39 pmNot sure about C++, but the plain C allows the compiler to do practically anything:"Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation orexecution (with the issuance of a diagnostic message)."Of course, the real compiler will perform the operations one way or another, but it doesn't have to.

author: westfw
author_rank: Super Contributor
timestamp: September 13, 2018, 12:16:56 am
content: QuoteQuoteA agree that this particular example is pretty easy to understand, and indeed my first reaction was more along the lines of "oh? Didn't that work before? Oh, right...")But I don't really see any reason to specifically ADD the ability to do this to the language specification, either.Haven't we outgrown the whole "COBOL is much too verbose; we're going to write APL/Forth/C so that you'll have to type a lot less!" thing?

author: Siwastaja
author_rank: Super Contributor
timestamp: September 13, 2018, 06:25:03 am
content: Quote from: glarsson on September 12, 2018, 07:44:31 pmNot bad!I'd add that maybe C is like a 70's to 80's Toyota Corolla. Widely used in its simplest form, most people aren't super excited about it, but it does the job fine and economically time after time. At the same time, others have found out you can tune the shit out of it to extract performance and do "tricks" that weren't in the original "marketing" - and doing this is relatively simple. While it doesn't have airbags, it's not the most inherently unsafe thing ever, either.And, while expensive and fast "sports cars" do exist, they are heavy, clumsy toalsoperform everyday tasks with, are black boxes of complexity, and tend to suddenly catch fire while sitting in traffic lights, or just offer so much power in one place that their young, inexperienced owners tend to epidemically crash them (to make headline videos on internet video news sites designed for envious, poor people to laugh at). I don't know which programming language these cars would represent, but IMHO, at least not C. Maybe C++?Me? I drive a 1988 Toyota Carina II, rusty as hell but does the job, and write in C, rusty as hell but does the job.

author: MK14
author_rank: Super Contributor
timestamp: September 13, 2018, 06:36:37 am
content: Quote from: Siwastaja on September 13, 2018, 06:25:03 amThat's good.So an interpreted/scripted language = Horse and Cart.Because a Horse and Cart is very simple and somewhat quick to assemble/make. Not fast, by any stretch of the imagination.VHDL/VerilogFPGA = Jet AircraftASIC or full custom IC = Space Rocket

author: MK14
author_rank: Super Contributor
timestamp: September 13, 2018, 07:13:39 am
content: Quote from: brucehoult on September 12, 2018, 11:23:39 pmThe thing is that the compiler, has been designed to a certain defined specification (The C or applicable, standard). So, since the operation is UNDEFINED, the compiler has NOT (necessarily) been designed to include the capabilities to correctly compile such code.So you can't (with 100% reliability), say "it will always be the same or same plus 1 value, never anything else".Since you have gone outside of the allowable limits of the compiler (exceeded Absolute Maximum values, if you want a datasheet analogy).Therefore, absolutely anything can happen.Just like exceeding the maximum capabilities of a transistor.The overloaded transistor, may carry on working, it may let out the magic smoke, it may catch on fire and explode.It may eject a high copper content vapour, which when your mother breathes it in, changes her colour to GREEN, and when you breath in the magic smoke you may cough out terrible demons.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: September 13, 2018, 07:47:58 am
content: Nope, I'm with brucehoult on this one, our mothers are safe.

author: newbrain
author_rank: Super Contributor
timestamp: September 13, 2018, 08:07:19 am
content: Quote from: GeorgeOfTheJungle on September 13, 2018, 07:47:58 amI'm partially in total disagreement.Our mothers might be safe, but hyperbole is a man's best friend.Unfortunately I have lost the example (code, specific version of gcc and flags used) where a similar line was completely ignored by the compiler.Skipped, no code emitted, no warning.The result was that a block local variable would keep its random uninitialized value.

author: glarsson
author_rank: Frequent Contributor
timestamp: September 13, 2018, 08:12:16 am
content: Quote from: brucehoult on September 12, 2018, 11:23:39 pmAnything can happen.Only fools think that a=a++; is a reasonable thing to write. I don't know what they thing it will accomplish. A more reasonable, but also sinister, situation is when a and a has different names and are pointers pointing at the same variable. On a supetscalar processor the instructions might then bemove @a,@badd @a,@b,1with both instructions executing at the same time. When both instructions retire and both attempt to update what a points to (@a) at the same time, the processor will detect this and raise some low level exception. If this happens in your nose transplant then nasal daemons is a reasonable result.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: September 13, 2018, 08:32:50 am
content: Quote from: glarsson on September 13, 2018, 08:12:16 amThat much is clear! (I hope)Quote from: newbrain on September 13, 2018, 08:07:19 amLOLQuoteIf a was uninitialized before that line, it would have to remain so after it, so that's ok, isn't it?

author: hamster_nz
author_rank: Super Contributor
timestamp: September 13, 2018, 08:44:48 am
content: Quote from: glarsson on September 13, 2018, 08:12:16 amI too am with Bruce Holt. "a = a++;" is perfectly valid C code. It might be stupid code, but I expect that no C compiler will kill my dog because I run this code.The only think in dispute is what it's outcome will be, as there is no implicit sequence to the "load", "store" and "increment" operations. The issue is that the compiler and CPU is free to schedule store and increment order it likes, and can change that order depending on a whim.Likewise there is nothing wrong with this code:Code:*p1 = (*p2)++;but if p1 == p2, then the value pointed to be p1 then the program's memory may not change as I expect. My dog will still be safe.All languages have undefined corners and rough edges, they are fun to peak into when bored, but I am sure we all agree in general you stay away from them so your code works reliably, predictably and can be maintained.Integer addition can lead to undefined behavior, as can subtraction, and multiplication, and division... you really don't have much left that is always guaranteed to never shoot yourself in the foot, regardless of language - you just have to know when you are in the "safe operating area" of the language.

author: glarsson
author_rank: Frequent Contributor
timestamp: September 13, 2018, 08:55:02 am
content: Quote from: hamster_nz on September 13, 2018, 08:44:48 amThe 6800 microprocessor had a couple of undefined instructions. Using them invoked undefined behavior. For the earliest revisions of the 6800 one of the consequences of invoking undefined behavior was to enable two buffers to drive the internal data bus at the same time. The practical consequence was that you had to buy a new 6800 — after the processor had short circuited itself to death.

author: newbrain
author_rank: Super Contributor
timestamp: September 13, 2018, 09:00:17 am
content: Quote from: hamster_nz on September 13, 2018, 08:44:48 amI sincerely hope my (and anyone else's) physical and financial health will never depend on your code.

author: hans
author_rank: Super Contributor
timestamp: September 13, 2018, 09:06:41 am
content: Quote from: glarsson on September 13, 2018, 08:12:16 amhttps://en.wikipedia.org/wiki/Hazard_(computer_architecture)#Write_after_write_(WAW)These hazards are common and it's the job of a CPU architecture engineer. Superscalar processors use register renaming to determine what register values are actually alive at which point, and is free to reorder instructions in order to do so. But aboveall, it will know that the value of 'a' will contain the addition of b+1 in your example, so any future references of 'a' must wait on the ADD instruction and not the MOV.A compiler will need to output it's result in a sequential set of instructions. There is no concept of "at the same time" here. This is a trick done in hardware to speed up the average execution of typical processor programs.If you argue that the compiler may arbitrarily choose the order of mov or add ; I think that's ill founded. I don't know of any construct in a programming language that allows you to run codeoutsideof the scope of your function. You could model the ++ operator as a function like:Code:void postinc(int& a) {int t = a;a++;return t;}int main(int argc, char** args) {argc = postinc(argc);return argc;}I don't see any reason why a compiler would reorder any of these operations. This program will return the value of argc unmodified.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: September 13, 2018, 09:07:00 am
content: Quote from: newbrain on September 13, 2018, 09:00:17 amIt's even got a mnemonic: IncrementPerhaps INCPRHPS

author: MK14
author_rank: Super Contributor
timestamp: September 13, 2018, 09:17:52 am
content: Quote from: GeorgeOfTheJungle on September 13, 2018, 09:07:00 amI think on some cpus, it is called MAYBEincA, and on some others it is called DOGkill, or MOTHERgreen.There is this mysterious one, from China, with the NOSEdemon instruction, HEX code 666.But I can't understand itsdevelishdescription.

author: hamster_nz
author_rank: Super Contributor
timestamp: September 13, 2018, 09:43:56 am
content: Quote from: newbrain on September 13, 2018, 09:00:17 amUm... Sorry, too late. - but I ensured I compared the MD5 hash at both the data at source and destination, just to be sure."a = a++;" it is dumb code, stupid code, I hope I never see it in a program, and perhaps an unintentional random behavior generator depending on which way the wind blow, but it is valid C code, it will compile, and it will run.Code:$cat pp.c#include <stdio.h>int main(int argc, char *argv[]){int a = 5;a = a++;switch(a) {case 5:printf("Option 1\n");break;case 6:printf("Option 2\n");break;default:printf("The world has ended\n");break;}}$gcc -o pp pp.c -pedantic -O4$./ppOption 1$Yes, I know that with -Wall it does generate a warning...

author: hamster_nz
author_rank: Super Contributor
timestamp: September 13, 2018, 09:53:43 am
content: Quote from: hans on September 13, 2018, 09:06:41 amIt is more that the compiler can chose what machine code to use, and they are not all guaranteed to have identical results - the end result may change depending on the compiler used, the compiler options used and the target architecture. For example, maybe the compiler decides to inline some code, and is then can optimize away the ++ operation....

author: hans
author_rank: Super Contributor
timestamp: September 13, 2018, 10:24:46 am
content: Well that's right. A common optimization step in write-after-write hazard is not carry out the first write at all. The first write is useless, as the result gets overwritten anyway without any intermediate use.So the ++; operation is redundant and can be removed. This is a valid optimization step.However erroneous behaviour occurs if the compiler or CPU decides to reorder these write instructions, as that will influence the functional behaviour of the program. This is not called undefined behaviour, but a serious bug.Hence, if you test out every compiler on godbolt.org (or any other compiler explorer you've got) and turn on the optimizer, then I bet all of them will return the incoming value.Most assembly outputs I've seen do not touch the incoming argument at all with an add instruction, e.g. on x86 all I get is:QuoteAnd also no warnings or errors, by the way, with -Werror -Wall turned on. Even if the program is written as:Quote

author: newbrain
author_rank: Super Contributor
timestamp: September 13, 2018, 10:52:04 am
content: Quote from: hans on September 13, 2018, 09:06:41 amThat program does not exhibit undefined behaviour, so I don't understand your point: you've just provided a possible, naïve, implementation.But, in general, a compiler is free to reorder operations as it likes (even across SPs, if no volatile object is involved), as long as the abstract machine shows the same behaviour it would have had before the reordering.For UB, this last constraint is voided.Quote from: GeorgeOfTheJungle on September 13, 2018, 08:32:50 amIIRC, it was along the lines of y = z[n++], with 'y' a local array.It was definitely not OK...

author: dmills
author_rank: Super Contributor
timestamp: September 13, 2018, 11:17:02 am
content: That one is fairly classic, the other good one is that if you do a pointer null check after dereferencing the pointer the compiler is free to take the fact that you have dereferenced the pointer as guaranteeing that the pointer is non null.x=p->foo;if (p == NULL){...... // This block will be removed as dead code because the dereference above says that p cannot be null without involving UB.Yes, the null check was in the wrong place, but the some of the security types still got sniffy when this behaviour was introduced.Regards, Dan.

author: hans
author_rank: Super Contributor
timestamp: September 13, 2018, 11:30:44 am
content: Quote from: newbrain on September 13, 2018, 10:52:04 amBecause:https://en.cppreference.com/w/cpp/language/eval_order#Undefined_behavior_2I'm bringing this point up because people seem not to understand what language specifications are.I agree, UB happens in the circumstances that you describe. But in the example of "a=a++" that doesn't need to happen.

author: glarsson
author_rank: Frequent Contributor
timestamp: September 13, 2018, 11:49:04 am
content: Quote from: hans on September 13, 2018, 10:24:46 amUnless the variable is declared volatile, then "both" writes must happen.But then again, if you invoke undefined behavior anything can happen.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: September 13, 2018, 12:25:10 pm
content: Quote from: newbrain on September 13, 2018, 10:52:04 amOk, good, but then that's not the "a= a++;" we were talking about!

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: September 13, 2018, 12:29:56 pm
content: Quote from: hamster_nz on September 13, 2018, 08:44:48 amSyntactically yes, but semantically it's... ambiguous.

author: macboy
author_rank: Super Contributor
timestamp: September 13, 2018, 02:09:01 pm
content: Quote from: Siwastaja on September 12, 2018, 02:35:56 pmI have no qualms about using break (or even continue) but only in cases where it makes the program flow easier to construct and easier to read/understand. Yes there are such cases. Especially when writing low level code as I do, there are times when you need to terminate a loop "early" due to some unforeseeable error and/or timing information from hardware. My use of continue is much more rare, but there are occasions where it, too, just makes sense. I have literally never used a goto in C/C++, and never will. I cringe when I see one. On the other hand, when writing assembly code, program flow essentially all boils down to goto/branch statements, so that's that.I restrict use of ?: to where it makes code cleaner. A prime example is where I check a flag and print a different substring based on it. e.g.:Code:printf("some operation state is %s\n", (someState == state_invalid_c)? "Invalid" : "OK");I like this better than printing the first part of the string, then doing a if/else to print one of the two different status strings.

author: RoGeorge
author_rank: Super Contributor
timestamp: September 13, 2018, 02:33:49 pm
content: Once I counted how many goto were in the Linux Kernel. I don't remember the number, but there were A LOT of goto lines.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: September 13, 2018, 02:39:57 pm
content: There's a (name hidden) database for the Mac with a Pascal-like language that has no return nor break nor continue nor of course goto.

author: NorthGuy
author_rank: Super Contributor
timestamp: September 13, 2018, 03:00:46 pm
content: Quote from: dmills on September 13, 2018, 11:17:02 amCompilers are very good at optimizing horrible code because it's easy to do. And, over the years, they find newer and better ways to do this. Not so much with reasonably well-written code.

author: Bassman59
author_rank: Super Contributor
timestamp: September 13, 2018, 05:55:50 pm
content: Quote from: GeorgeOfTheJungle on September 13, 2018, 12:29:56 pmFor what it's worth, Xcode (which uses LLVM) puts up a warning as soon as you type the line.

author: brucehoult
author_rank: Super Contributor
timestamp: September 13, 2018, 09:37:58 pm
content: Quote from: newbrain on September 13, 2018, 08:07:19 amThat's absolutely fair. If a has an undefined value then so does a++.

author: MK14
author_rank: Super Contributor
timestamp: September 14, 2018, 04:39:28 pm
content: Let's ask an extreme world class expert on compiler design (particularly C and C++), if UNDEFINED, can do ANYTHING, or if it is limited to one or two reasonable/obvious values ?"Hello, Dr GCC, can you please answer this question for us" ?Here is my program...#include <stdio.h>int i;int main(void){i = 0;i *= (i *= ++i) * (i *= i) * (i *= i) * (i *= i) * (i *= ++i);printf("\nDemo1 = i = %d\n", i);i = 0;i *= (i *= ++i) * (i *= i) * (i *= ++i) * (i *= i) * (i *= ++i);printf("\nDemo2 = i = %d\n", i);}"So, Dr GCC, what is the answer" ?gcc eevblog.c -oeevblog./eevblog"No flags were changed from default, and no errors or warnings came up"Demo1 = i = 0Demo2 = i = -1554387584"Thanks, DR GCC".tl;drGoing from 0, to -1554387584, because of one extra ++, in the middle of an expression (disappointingly, GCC (very recent version) gave no warnings or errors). Seems to me (and Dr GCC), that ANYTHING, can happen, when you use UNDEFINED things in your programs.Now, to really prove a point. Let's do it again, and just tidy up the code. The results will be the same, surely ?#include <stdio.h>int main(void){int i = 0;i *= (i *= ++i) * (i *= i) * (i *= i) * (i *= i) * (i *= ++i);printf("Demo1 = i = %d\n", i);i = 0;i *= (i *= ++i) * (i *= i) * (i *= ++i) * (i *= i) * (i *= ++i);printf("Demo2 = i = %d\n", i);}gcc eevblog.c -oeevblog./eevblog..."Again, no errors or warnings"Demo1 = i = 0Demo2 = i = -368195456So ANYTHING has changed its mind, again. Just because the variable was moved from globals to locals (presumably).

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: September 14, 2018, 05:01:39 pm
content: Use -Wsequence-pointkk.c: In function ‘main’:kk.c:6: warning: operation on ‘i’ may be undefinedkk.c:9: warning: operation on ‘i’ may be undefined

author: MK14
author_rank: Super Contributor
timestamp: September 14, 2018, 05:07:42 pm
content: Quote from: GeorgeOfTheJungle on September 14, 2018, 05:01:39 pmOriginally, I was using -Wall and had the optimiser on (-O3), which also produced the warnings and the same results, at least for the first program.But I removed any unnecessary clutter, to reduce the size of the demo program, for the forum post.

author: MK14
author_rank: Super Contributor
timestamp: September 14, 2018, 08:15:06 pm
content: The code for the second program is typed in and ready for anyone to try or change (press FORK-THIS), online, here:https://onlinegdb.com/HyBUB9KuQCode:#include <stdio.h>int main(){int i = 0;i *= (i *= ++i) * (i *= i) * (i *= i) * (i *= i) * (i *= ++i);printf("Demo1 = i = %d\n", i);i = 0;i *= (i *= ++i) * (i *= i) * (i *= ++i) * (i *= i) * (i *= ++i);printf("Demo2 = i = %d\n", i);}Results:Code:Demo1 = i = 0Demo2 = i = -368195456...Program finished with exit code 0Press ENTER to exit console

author: MK14
author_rank: Super Contributor
timestamp: September 14, 2018, 08:49:54 pm
content: If you change the variable type todouble, and initial value to pi. Done here:https://onlinegdb.com/ryiQCqY_Q]It goes even crazier.Demo1 = i = 84465015249278344875802672090097198053720064.000000Demo2 = i = 446308403328.000000Demo1, rapidly increases if you increase the initial value, yet demo2 remains apparently constant.Weird, really weird.

author: brucehoult
author_rank: Super Contributor
timestamp: September 14, 2018, 10:17:48 pm
content: Quote from: MK14 on September 14, 2018, 04:39:28 pmYou put 12 (or 13) arithmetic operations between your sequence points, with 2 (or 3) of those operations having side-effects. The compiler is limited to doing those operations, in some order.The result is going to be some number.The result is *not* going to be your computer catching on fire.

author: brucehoult
author_rank: Super Contributor
timestamp: September 14, 2018, 10:29:18 pm
content: Quote from: MK14 on September 14, 2018, 08:49:54 pm446308403328 is 2^7 * 3^20. 446308403328 % 2^32 - 2^32 = -368195456Nothing strange here, it's just the result of incrementing 0 twice and three times and doing a little multiplying and squaring -- exactly as you told it to. And then truncating the answer to a 32 bit signed integer.

author: MK14
author_rank: Super Contributor
timestamp: September 14, 2018, 10:30:56 pm
content: Quote from: brucehoult on September 14, 2018, 10:17:48 pmIt depends on the computer hardware, and what it is connected to.If it is an autonomous self-driving car, driving at high speed on a motorway.Then it could cause an accident, which results in the vehicle (and hence the computer), catching on fire.But I agree, in general, in most cases, serious software bugs, will not (usually) set the computer on fire.

author: MK14
author_rank: Super Contributor
timestamp: September 14, 2018, 10:40:58 pm
content: Quote from: brucehoult on September 14, 2018, 10:29:18 pmThanks for giving that insight. I did not realise, that is what it was doing.But I still believe that since it is "undefined", it should NOT be used in normal/real coding practice.Also, if you change the language (but keep the source code the same), via those online links I posted. Those numbers, change quite a lot, between C and C++, language selections!Since i starts out as 0 (in earlier programs), and it is basically i *= ... lots of stuff ***It possibly should be evaluating to 0 (basic maths 0 x stuff = 0).But I am NOT familiar enough with the full C and C++ language specifications, to know how it is suppose to handle that situation.Presumably, because -Wall, says UNDEFINED. It is undefined, and can be anything it likes.EDIT: Original(C):https://onlinegdb.com/HyBUB9KuQDemo1 = i = 0Demo2 = i = -368195456...Switching language to C++https://onlinegdb.com/Sy1pRhYOX, source code kept same....Results now have changed to (via C to C++ language switch) ...Demo1 = i = 16Demo2 = i = 5345344In my perception (ignoring UNDEFINED means ANYTHING can happen),going from C to C++ (source code kept IDENTICAL), should not really change the output values.But I guess some of the changes between C and C++, have caused this, so it is not unreasonable, just a bit weird.

author: brucehoult
author_rank: Super Contributor
timestamp: September 14, 2018, 11:09:31 pm
content: Quote from: MK14 on September 14, 2018, 08:15:06 pmI get exactly the same result with gcc for any of i386, RISC-V or ARM.I get exactly the same result with -O0 (in which case the arithmetic is done at runtime, in main) or with -O1 (in which case the arithmetic is done by the compiler and the answer is simply printed.Here is the RISC-V code for -O0. The others are the exact equivalent. They just keep some temporary values in registers and save and load values of i to its stack slot in memory.Code:# i=0sw zero,-20(s0)lw a5,-20(s0)addi a5,a5,1sw a5,-20(s0)lw a3,-20(s0)lw a5,-20(s0)addi a5,a5,1sw a5,-20(s0)lw a4,-20(s0)lw a5,-20(s0)addi a5,a5,1sw a5,-20(s0)lw a5,-20(s0)lw a2,-20(s0)mul a3,a2,a3sw a3,-20(s0)lw a2,-20(s0)lw a3,-20(s0)mul a3,a2,a3sw a3,-20(s0)lw a2,-20(s0)lw a3,-20(s0)mul a3,a2,a3lw a2,-20(s0)mul a4,a2,a4sw a4,-20(s0)lw a4,-20(s0)mul a3,a3,a4lw a2,-20(s0)lw a4,-20(s0)mul a4,a2,a4sw a4,-20(s0)lw a4,-20(s0)mul a4,a3,a4lw a3,-20(s0)mul a5,a3,a5sw a5,-20(s0)lw a5,-20(s0)mul a5,a4,a5lw a4,-20(s0)mul a5,a4,a5sw a5,-20(s0)#printlw a1,-20(s0)lui a5,0x1baddi a0,a5,2016 # 1b7e0 <__clzsi2+0x4a>jal 1044e <printf>Clang gives a different answer to gcc (5345344 = 2^6 * (2^4 + 1)^4) , but it gives the same answer across different CPU types and different optimisation levels, and again does the actual calculation in machine code with -O0 but just prints the final answer directly with -O1.While you might not find the answer easy to predict, it seems there really are only a couple of likely answers. Certainly no explosions.

author: MK14
author_rank: Super Contributor
timestamp: September 14, 2018, 11:16:45 pm
content: Wow, that's an amazing piece of work you have just done.I can now better understand, your point of view.C/C++ source code, changing its behaviour, between GCC, CLANG and presumably other compilers. Would still cause all sorts of practical difficulties, and the immense difficulty in predicting what the code really does (how it gets to the answers), would make it a nightmare or at least difficult/tricky to change in the future.But having primarily two different values, is not impossible to handle.E.g. Conditional Compiling and/or early tests in the source code to see which way round such things will be handled by the compiler.A bit like, C/C++, is a bit weak as regards if the integer default is 32 or 64 bits.So, some clever source code systems, do some checks early on and set things up, so it can handle the various possibilities.Just to be clear. I still think UNDEFINED is a bad idea to code with, and can still potentially cause at least two different outcomes. Presumably it still can be ANYTHING, but you have presented some nice arguments as to why it may be often less than anything.

author: brucehoult
author_rank: Super Contributor
timestamp: September 15, 2018, 12:32:28 am
content: Quote from: MK14 on September 14, 2018, 11:16:45 pmIt's a VERY VERY bad idea to write code like that. It's bad programming.But you wrote three of ++i and there are three of "addi x,y,1" in the generated code.You wrote ten of "*" or "*=" and there are ten "mul" in the generated code.All that changes is the order of those thirteen operations and which results are saved and used again later, and when.I don't know how many reasonable answers there are. It's probably dozens or hundreds. But the answer is a number, not an explosion.I've shown that if you use the same compiler (and compiler version) across different CPU types you'll get the same answer. Different compilers can produce different answers, but there are't a lot of different compilers in use these days.Different versions of the same compiler *could* produce different answers, but this will be less common. The gcc versions I used were 7.3.0 for i386 and ARM, and 7.2.0 for RISC-V. I just tried another x86 machine with gcc 5.4.0 and got the same answer (and the same for both i386 and x86_64)As you showed, you also get the same answer for int and for double precision FP, once you take into account the range limitations of int. The same operations are done in the same order.But anyway DON'T CODE LIKE THIS!! Don't use ++ or -- on a variable if that variable appears more than once in the statement (right AND left sides).

author: brucehoult
author_rank: Super Contributor
timestamp: September 15, 2018, 12:49:01 am
content: Quote from: MK14 on September 14, 2018, 10:40:58 pm16 and 5345344 are the same answers clang gets for both C and C++, for all CPU types I tried. It's an interesting question why gcc and g++ would get different answers.

author: MK14
author_rank: Super Contributor
timestamp: September 15, 2018, 02:06:44 am
content: Quote from: brucehoult on September 15, 2018, 12:49:01 amAnother AMAZING change of value (which the concept is very likely to be how you described, where it is changes in order of the calculations, has a big impact on the final answers), is when you leave the source code unchanged.Except for where the integer variable i, is declared. Between before main(), and declaring it within the main() {} codeblocks.When it is before main(), the value changes to (as detailed in a much earlier post of mine, in this thread):Demo1 = i = 0Demo2 = i = -1554387584EDIT: Unable to reproduce these strange values, using the online compiler, as it comes up with the same values as before. But I can still reproduce these values by using my local Linux machines GCC version:Code:gcc --versiongcc (Ubuntu 7.3.0-16ubuntu3) 7.3.0So it is probably because the online version uses strange memory models,or my local GCC has bug(s), applicable to this issue or some other explanation.End of EDIT.I'm amazed, that just changing where/how an integer is declared, changes its calculated value, like that.Usually, such a change in allocation, would be invisible to the C/C++ code. Unless other C/C++ files attempt to access the variable and/or its operations are timed extremely carefully. I've seen slight (tiny) timing variations, because it is on the stack, heap, somewhere in memory or in registers, depending on the compiler, cpu, implementation, optimisation level, etc.Maybe it is a "race hazard", between different initialisation sections of the C/C++ code, or other peculiarities ?But there are many other possible explanations.If I get the inclination, I may spend time, and try and find out why this happens.

author: westfw
author_rank: Super Contributor
timestamp: September 15, 2018, 03:32:32 am
content: I've noticed an annoying trend.In the old days, if you used an ambiguous syntax that the spec claimed was "undefined", you would tend to get a result that made sense, more or less. It might not be the same on all systems and all compilers, but each result would be ... sensible, looked at from some perspective.More recently, less so. The compiler developers mumble about optimization and etc, but I think they're doing it out of spite.Some of the cases are particularly annoying to embedded developers.I've run into situation where common constructs no longer work as expected:Code:uint8_t time = 0 - delaytime;while (++time != 0) { //stuff.}"The behavior of C WRT integer overflow is undefined. The compiler decided that incrementing a non-zero value would never reach 0, and made the loop unconditional and infinite."Code:while (1); // stop the program"A while loop may be assumed to terminate. Since the loop is empty and doesn't do anything, the code was entirely omitted."https://stackoverflow.com/questions/16436237/is-while1-undefined-behavior-in-cGrr.

author: newbrain
author_rank: Super Contributor
timestamp: September 15, 2018, 11:49:30 am
content: Quote from: westfw on September 15, 2018, 03:32:32 amI'm a bit puzzled by both of these statements:The behaviour of overflow in unsigned integral types is actually well defined (that's the major point for using them, IMO), optimising that away it's simply wrong.If someone uses a signed integer there, it's their fault.while(1) does not fall in the 6.8.5/6 clause added in C11, so it has defined (and expected) behaviour.That clause refers to loops where the controlling expression isnota constant expression (and etc. etc.)*.One of the good reason to use a volatile object.But, yes, an earlier version was more problematic.If they are doing that out of spite for the use of well known undefined constructs, I'm with them.Better fail in a more visible way rather than instilling a misleading sense of confidence on behaviour that is not guaranteed in any way.I actually would make your sound card go, and declare that a diagnostic (not that one is needed with UB).And now is the time for the dreaded, misleading and easily rebuttable car analogy:Using UB is like driving on the highway in the wrong direction. You might survive it, but it's something you should strive to avoid.* Note: I only have access to the final draft of C11 (n1570), not the actual standard. That draft was approved without remarks, AFAIK.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: September 15, 2018, 02:40:57 pm
content: Quote from: MK14 on September 14, 2018, 08:15:06 pmI've tried out of curiosity and usingint* i= malloc(sizeof(int));and*ieverywhere instead ofi, it always gives 0 no matter what, with gcc in Intel and ARM.

author: MK14
author_rank: Super Contributor
timestamp: September 16, 2018, 12:11:41 am
content: Quote from: GeorgeOfTheJungle on September 15, 2018, 02:40:57 pmI tried it (Malloc and * etc, I changed the syntax a bit, so it supports C and C++) using the online GCC, and it came up with the same values as before (i.e. made no difference). But I'm not sure if the online GCC, uses strange memory models (i.e. they hacked/changed it, or it doesn't use the stack/heap system, etc etc), so that it can run using little/limited server resources.So doing it on a real machine (I can well believe), could give different results.The problem if I do it on my machine, is I can't directly share the results on the forum, for others to mess/experiment with it. Because we are probably running different OS versions, different GCC's etc.https://onlinegdb.com/SJz0RMjO7

author: brucehoult
author_rank: Super Contributor
timestamp: September 19, 2018, 11:44:09 pm
content: Quote from: westfw on September 15, 2018, 03:32:32 amThose are pretty rubbish ways to do things, especially assuming processor speed (MHz and IPC) to get a delay. Much better to use a cycle count register. Alas some simpler MCUs don't have one. RISC-V *requires* all implementations to have a 32 bit cycle counter.However, if you insist on coding that way, popping a 'asm volatile ("")' inside your loop will convince gcc at least to never optimise it away.

author: newbrain
author_rank: Super Contributor
timestamp: September 20, 2018, 07:51:47 am
content: Quote from: brucehoult on September 19, 2018, 11:44:09 pmBut do gcc or other compilers really optimise this things away?In my reading of the standard this would definitely be non-conforming, have I overlooked something?In the first case, if there are actually no statements with side effects inside the while ( "// stuff" ), the compiler could just assign 0 to delay and be done with that (since it's not been declared volatile), but cannot simply omit the loop, as its controlling expression has a visible side effect.The second case is a bit hairier, but still it's not UB, and as such should be honoured. The compiler cannot assume it terminates and remove it as it has no side effects.

author: SiliconWizard
author_rank: Super Contributor
timestamp: September 20, 2018, 01:31:11 pm
content: Quote from: newbrain on September 20, 2018, 07:51:47 amAn infinite loop such asCode:while (1) ;should obviously not be optimized away as it has a clear an unambiguous effect: halting the execution.