Analyzing Output [ SOLVED ]

Irving

Joined Jan 30, 2016
5,123
WHERE is this defined by the standard? The word "reference" appears 24 times in the current draft standard for C23 (N3096) and not one of them has anything to do with passing information.

So which section of which document states that an inline prefix INC is passed by reference?
See my post #13 above..
 

WBahn

Joined Mar 31, 2012
32,844
See my post #13 above..
Which document is that?

It clearly is NOT the C language standard, since it is talking about constructs that do not exist in C. C++ is not C. The C++ standard is free to impose additional constraints that C is not bound by.
 

Irving

Joined Jan 30, 2016
5,123
WHERE is this defined by the standard? The word "reference" appears 24 times in the current draft standard for C23 (N3096) and not one of them has anything to do with passing information.

So which section of which document states that an inline prefix INC is passed by reference?
OK, I can't find the document where I found that reference, but I did find the equivalent in C23, section 6.3.2.1 para 4

1706020287064.png

However, having dug a little more into the language definition, I see that putting the mul(++x) into a printf() raises a flag in that C does not proscribe the order of parameter usage in a function call and therefore the expansion of mul(++x) to (++x) * (++x) is, as you stated, actually undefined behaviour and could result in one or other ++x not being evaluated (though one is always evaluated), and so 42 or 49 are both possible outputs. And strangely no compiler warning as such. Indeed, the original compiler I tried this with, giving 49, is now resolutely giving the result as 42!!!

Even weirder, this code:

#include <stdio.h>

#define mul(a) (a) * (a)

int main()
{
int x=5;

printf("%d\n", mul(++x));

// int *y = &x;
}


gives 49 (for now!) but if I uncomment the last line it returns 42 despite that line having no direct impact on the printf() output.

Incidentally the operation ++x is tagged with a " memory_order_seq_cst " tag which means the underlying machine representation has to implement it in a thread-safe manner, with either an atomic read-modify-write instruction or prevent the load-modify-store sequence being interrupted eg using a mutex or similar.
 

WBahn

Joined Mar 31, 2012
32,844
OK, I can't find the document where I found that reference, but I did find the equivalent in C23, section 6.3.2.1 para 4

View attachment 313408
Perhaps I'm not reading sufficiently between the lines, but I don't see how para 4 has anything to do with the preincrement or predecrement operators. It's talking about whether or not a function designator is an expression that has "function type" or one that has a type of "pointer to a function returning a particular type". Even the three specifically listed operators in that paragraph do not include the prefix increment/decrement operators, just the sizeof(), typeof, or the address of (unary &) operator.

The forward reference lists both the post and prefix increment and decrement operators only because they are mentioned in para 2, which is dealing with lvalue conversion.

However, having dug a little more into the language definition, I see that putting the mul(++x) into a printf() raises a flag in that C does not proscribe the order of parameter usage in a function call and therefore the expansion of mul(++x) to (++x) * (++x) is, as you stated, actually undefined behaviour and could result in one or other ++x not being evaluated (though one is always evaluated), and so 42 or 49 are both possible outputs. And strangely no compiler warning as such. Indeed, the original compiler I tried this with, giving 49, is now resolutely giving the result as 42!!!
And, hence, the danger of inferring how something behaves based on experience with how it has behaved in the past -- and we are always in danger of doing this when we don't realize that what we are doing is actually invoking undefined behavior, which is, unfortunately, all-too-easy to do since there is so much undefined behavior in C.

Even weirder, this code:

#include <stdio.h>

#define mul(a) (a) * (a)

int main()
{
int x=5;

printf("%d\n", mul(++x));

// int *y = &x;
}


gives 49 (for now!) but if I uncomment the last line it returns 42 despite that line having no direct impact on the printf() output.
I've run into similar things before. Compilers, particularly optimizing compilers, are looking for identifiable patterns and the code they produce is different depending on whether or not they recognize a pattern that they know how to optimize. For defined behaviors, all possibilities must exhibit the defined behavior. For implementation-specified behaviors, all possibilities must exhibit the same behavior (i.e., be internally self-consistent), though this behavior need not be the same as other compilers. But for undefined behaviors, all bets are off and there is no requirement that they behaviors even be self-consistent.

How the compiler recognizes various patterns is up to the little sorcerer that was ground up and sprinkled into the compiler code, and -- as you observed -- changes to code that, from our perspective, wouldn't seem to have any possible connection to the behavior that is being affected can because it changes the compilers ability to spot the same pattern that it did last time.

We ran into this when writing a macro do to a bit rotate operation on a 64-bit integer. Sometimes the compiler recognized this as a rotate operations and used the ISA's rotate instruction, other times it implemented the logic exactly as we had expressed it with bit-banging operations. The difference came down to the definition of a completely unrelated macro located a few lines above it. Even examining the preprocessor output didn't shed any light on what the compiler was homing in on that could explain the difference -- and it was a difference that mattered because this was in highly performance-sensitive code. We couldn't use in-line assembly because we needed this code to be portable (at the end of the day, that was much more important than the performance), so all we could do was document the issue in the comments so that others would be aware that this would be a good place to insert in-line assembly for their platform.
 

Irving

Joined Jan 30, 2016
5,123
https://forum.allaboutcircuits.com/attachments/1706020287064-png.313408/
Perhaps I'm not reading sufficiently between the lines, but I don't see how para 4 has anything to do with the preincrement or predecrement operators. It's talking about whether or not a function designator is an expression that has "function type" or one that has a type of "pointer to a function returning a particular type". Even the three specifically listed operators in that paragraph do not include the prefix increment/decrement operators, just the sizeof(), typeof, or the address of (unary &) operator.

The forward reference lists both the post and prefix increment and decrement operators only because they are mentioned in para 2, which is dealing with lvalue conversion.


Hmm, the way I read this was that except for the 3 items mentioned all the others list were of type 'function designator' and therefore passing in an expression 'a' is converted to a pointer to that expression '*b', where *b = &a.

++x is such a function type, therefore the pointer substitution is carried out. The equivalent function definition (where <T> is the type of x), I believe is:

*<T> f(*<T>x) {
*x += 1; // the reqired side effect
return &x;
}
 
Top