1

I've just spent the last day debugging something in which I ultimately ended up on an offending line. The path to this line wasn't exactly clear, and there was a slo-mo face palm once I spotted it...

int v = func(&v);   // why am I allowed to do this.

Should this not generate some sort of 'var used before defined' warning? It was incredibly annoying because there was no compiler error or warning? and of course, no red suiggly line under the syntax :(. So I skipped right over it...

oddly, it worked in most cases until it was spotted.. lucky with the UB I guess?

Admitedly, my knowledge of compiler intricacies isn't great, so I am probably missing something obvious, but it looks to me like I just used a var before defining it, it could be argued I used it before declaring it!

Why is this valid C++ syntax?

lfgtm
  • 1,037
  • 6
  • 19
  • 1
    Yes, it is odd, but why *shouldn't* it be valid? The compiler sees `int v` first and so knows the type and storage of `v` by the time it sees `&v`, so it knows where `v` is stored and how to get its address by the time `func()` is called. It *might* be valid code, depending on what `func()` actually does with the pointer from `&v` (the C `time()` function comes to mind, where it outputs the same value via both return value and output parameter, if assigned). Sorry, I'm not a language lawyer, so I can't quote the spec that would make this (il)legal code one way or the other. – Remy Lebeau Jun 26 '18 at 23:29
  • 1
    Closely related to [Is passing a C++ object into its own constructor legal?](https://stackoverflow.com/q/32608458/1708801) this is legal ... you are allowed to use v here and it could be valid if all you use is it's address. [The point of declaration for a name is immediately after its complete declarator and before its initializer](http://eel.is/c++draft/basic.scope.pdecl#1) – Shafik Yaghmour Jun 26 '18 at 23:35
  • There's nothing wrong with this code at all. – David Schwartz Jun 26 '18 at 23:36
  • 2
    "Why is this valid C++ syntax?" because it is valid C syntax and C++ inherited it – Slava Jun 26 '18 at 23:36
  • @RemyLebeau yeah, I phrased that badly, I can see it not neccessarily being invliad syntax (at least not worthy of a compiler error), but I would have thought at least a C4700 warning should be issued? i.e similar to what would happen if `int y; int g = y + 1;` – lfgtm Jun 26 '18 at 23:37
  • 1
    @lfgtm You could possibly get such a warning, if the definition of `func` is in the same translation unit and optimization is enabled. – aschepler Jun 26 '18 at 23:39
  • @ShafikYaghmour ah ok, passing addresses I guess could circumvent certain checks the compiler might do? – lfgtm Jun 26 '18 at 23:40
  • @DavidSchwartz, tell that to the last 24hours of my life ;) – lfgtm Jun 26 '18 at 23:41
  • @Slava lol, I guess you can take the ++ out of C, but not the C out of.... o.0 – lfgtm Jun 26 '18 at 23:41
  • 1
    @lfgtm The difference is that in your example, the value assigned to `g` is undefined. The value of `&v` passed to `func`, however, is correct and well-defined. Whatever your error is, this must have only been part of it because there's nothing wrong with this. – David Schwartz Jun 26 '18 at 23:42
  • @DavidSchwartz ah ok, thanks, hence no C4700? Hopefully one day compilers may look at the bigger picture... or perhaps not, that might put me out of the job. – lfgtm Jun 26 '18 at 23:44
  • @lfgtm, there's a difference between `int v = func(&v);` and `int y; int g = y + 1;`. The latter is a problem because you're using an uninitialised *value* `y`, there is nothing the least bit uninitialised about the *address* `&v`. A better match would be `int y; int *g = &y;` which is also perfectly okay. – paxdiablo Jun 27 '18 at 01:00
  • In any case, we'll *always* have a job until all programs just have `main` calling the library function `doWhatIWant()`. Of course, they'll need someone to maintain the `doWhatIWant()` function :-) – paxdiablo Jun 27 '18 at 01:02
  • Related: https://stackoverflow.com/q/50663893/1848654 – melpomene Jun 27 '18 at 01:04

1 Answers1

2

A variable name is in scope as soon as its declarator has appeared. The code is not undefined behaviour per se; it is fine to use the address of a variable that has not yet been initialized.

This is covered in the paragraph 1 of the C++17 6.3.2 Point of declaration [basic.scope.pdecl] section (paraphrased):

The point of declaration for a name is immediately after its complete declarator and before its initializer (if any).

There's also a specific example given there of valid (albeit not very smart) code:

unsigned char x = x;
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
M.M
  • 138,810
  • 21
  • 208
  • 365