I came across code similar to that below which is legal, albeit not very clever. Two stack variables in the same function in scope at the same time with the same name is legal but it can cause problems. (Read: I just wasted half an hour debugging this). At warning level 4 on VS2010 (the highest level), I would have hoped it would have caught this kind of thing. Am I missing something, or is it time to hit to whole code base with lint for a once over? Would a static analysis tool such as lint even pick up name clashes like this?
char *x = strchr(Buffer,' ');
if (x)
{
*x = 0;
x++;
char *x = strchr(x,' ')
if (x)
*x = 0;
}
PC-Lint will detect this. The following code:
main (int argc, char *argv [])
{
char *x = 0;
if (x)
{
char *x = 0;
}
}
Gives a warning:
main.cpp 6 Warning 578: Declaration of symbol 'x' hides symbol 'x' (line 3)
Edit: I didn't notice this when I wrote the original answer (below). The code you've posted is illegal, and causes undefined behavior. The offending line is this one:
char *x = strchr(x,' ');
Here the x within the strchr call does not refer to the x defined in the enclosing scope, but to the x defined previously on the same line. Hence that line reads from an uninitialized variable, which results in undefined behavior. From the C++ standard,
§3.3.2/1 [basic.scope.pdecl]
The point of declaration for a name is immediately after its complete declarator (Clause 8) and before its initializer (if any), except as noted below. [ Example:
int x = 12;
{ int x = x; }
Here the second x is initialized with its own (indeterminate) value. —end example ]
GCC does complain if the corresponding line in the example below is changed to
int x = 21 + x; // generates "warning: x is used uninitialized in this function"
And duplicating your strchr example on VS2012 generates this warning (at /W1 and above):
warning C4700: uninitialized local variable 'x' used
Original answer follows (not entirely accurate):
There's nothing illegal about the code. You've introduced a new scope by adding braces, and you're allowed to define variables within the new scope even if those variable names have been previously defined in the enclosing scope.
All references to the variable after the new definition will refer to the local variable instead of the one in the enclosing scope, until the lifetime of the local variable ends. The following code produces no warnings on GCC even when compiled with -pedantic -Wall -Wextra
#include <iostream>
int main()
{
int x = 42;
{
std::cout << x << '\n';
int x = 21;
std::cout << x << '\n';
}
std::cout << x << '\n';
}
Output:
42
21
42
I don't know whether lint or another static analysis tool will pick out such things either. It is legal, but not advisable.
Related
I'm studying the Declarations in Conditions topics in C++ and faced the below problem.
#include <iostream>
int main() {
int x;
std::cin >> x;
if(int a = 4 && a != x) {
std::cout << "Bug fixed!" << std::endl;
}
}
I declared and then initialized the variable a. In the The C++ Programming Language by Bjarne Stroustrup Ed.2011, it is said:
The scope of variable declared in if statement extends from its point of declaration to the end of the statement that the condition controls.
That's what I did, I declared and initialized the variable a, but when I try to compare it against x, compiler gives uninitialized local variable a used error. Why, what's the problem?
I can do
int a = 4;
if (a != x)
// ...
but I would like to do it in one line if possible.
In the expression inside the if condition
int a = 4 && a != x
what the compiler actually sees is
int a = (4 && a != x)
where the value of a is clearly being used before it's initialized (which is what the error is saying), and is not the intent of the code.
From C++17, you can use if-with-initializer syntax to achieve the effect you want
if (int a = 4; a != x)
// ...
This question already has answers here:
Using a variable with the same name in different spaces
(4 answers)
Closed 6 years ago.
I have following code:
int X = 1;
if (int X = X)
{
printf("%d\n", X);
if (int X = X)
{
printf("%d\n", X);
}
}
My expectation is X should be 1, 1. However, the output is:
1818935350
32767
Anyone know what's going on here? Compiled with clang-800.0.42.1
Edit: I tried the following code with a bit tweak, and now it behaves as I expected.
int X = 1;
if (int Y = X)
{
printf("%d\n", Y);
if (int Z = X)
{
printf("%d\n", Z);
}
}
One guess is when you use variable on RHS of a declaration inside if statement, it might not refer to the variable with the same name declared at parent scope, instead it's referring to the variable that's being defined...
When you say int X = X, both X's refer to the same int. That is, the one you are declaring right there in that line. So you are initializing X with itself, which is undefined behavior, since it was (of course) not already initialized. The X that you initialized with 1 is never printed in your code, since it is declared in a parent scope and shadowed by the ones in the inner scopes.
In C, this if (int X = X) is plain wrong because the if statement expects a control expression - but NOT a declaration.
6.8.4.1 The if statement
The controlling expression of an if statement shall have scalar type.
I'm not sure about clang, but gcc gives this code an obvious error (not a warning) even without any compiler flag (ie without -Wall -Wextra)
error: expected expression before ‘int’
Change all the
( Int X =X) to (X)
It is creating new variables and possibly returning old data stored in that location.
Both clang and g++ seem to be compliant with the last version of the paragraph [expr.const]/5 in the C++ Standard. The following snippet prints 11 for both compilers. See live example:
#include <iostream>
void f(void) {
static int n = 11;
static int* temp = &n;
static constexpr int *&&r = std::move(temp);
std::cout << *r << '\n';
}
int main()
{
f();
}
According to my understanding of this paragraph both compilers should print 2016 for the code below. But they don't. Therefore, I must conclude that the code shows undefined behavior, as clang prints an arbitrary number and g++ prints 0. I'd like to know why is it UB, taking into consideration, for example, the draft N4527 of the Standard? Live example.
#include <iostream>
void f(void) {
static int n = 11;
static int m = 2016;
static int* temp = &n + 1;
static constexpr int *&&r = std::move(temp);
std::cout << *r << '\n';
}
int main()
{
f();
}
Edit
I have a habit of not being satisfied with an answer that just says the code is UB, or shows undefined behavior. I always like to investigate a little deeper, and sometimes, as now, I happen to be lucky enough to understand a little bit more, how compilers are built. And that's what I found out in this case:
Both clang and GCC seem to eliminate any unused variable, like m, from the code, for any optimization level greater than -O0. GCC seems to order local variables with static storage duration, the same way variables are placed on the stack, i.e., from higher to lower addresses.
Thus, in clang, if we change the optimization level to -O0 we get the number 2016 printed as expected.
In GCC, if in addition to that, we also change the definition of
static int* temp = &n + 1;
to
static int* temp = &n - 1;
we will also get the number 2016 printed by the code.
I don't think there's anything subtle here. &n + 1 points one-past-the-end of the array-of-one as which you may consider the location n, and so it does not constitute a dereferenceable pointer, although it is a perfectly valid pointer. Thus temp and r are perfectly fine constexpr variables.
You could use r like this:
for (int * p = &n; p != r; ++p) { /* ... */ }
This loop could even appear in a constexpr function.
The behaviour is of course undefined when you attempt to dereference r, but that has nothing to do with constant expressions.
You've apparently expected that you can:
obtain a pointer to a static storage duration object
add one to it
get a pointer to the "next" static storage duration object (in declaration order)
This is nonsense.
You'd have to eschew all standard-backed guarantees, relying only on an unholy combination of UB and implementation documentation. Clearly you have crossed the UB threshold long before we ever even entertain discussions about constexpr and std::move, so I'm not sure what relevance they were intended to hold in this question.
Pointers are not "memory addresses" that you can use to navigate your declaration space.
This code compiles, but I have a run time error in Visual Studio:
Run-time check failure #3 - the variable 'x' is being used without being initialized...
int x = 15;
int main()
{
int x = x;
return 0;
}
I don't understand that behavior... in the error box when I click continue the program resumes and x has a corrupted content (like -8556328 instead of 15).
Why does this code work without a problem, and the int array is well declared?
const int x = 5;
int main()
{
int x[x] = {1,2,3,4};
return 0;
}
x is defined at the left of =.
so in x[x], [x] refer to the global one,
whereas in x = x;, x hides the global x and initializes from itself -> UB.
When you declare a new variable, its name becomes visible right here
int x =
// ^- there
because it is at that point the variable is fully declared, and as such; its name means something. At this point in time any other (previously declared variable) in a surrounding scope will be hidden.
There is no scope resolution operator in C, so you may not be able to use
int x = x;
in your program.
please use SRO( Scope resolution operator ::) to tell compiler which x is real x in your mind. As user defined names are mangled( Names are decorated) something like this to avoid ambiguity at it's level, these are just names used by compiler that best suits it
int x = 15;// Real name = gui_x
int main()
{
int x = x;// lui_x
return 0;
}
In this way run-time will know which version you are using but to avoid ambiguity it expects from you to use specific names. Sometimes above problem arise where you don't know that you are using already used names. For this C++ has created SRO.
Now in case of array x is address & not integer that stores something, that's why compiler didn't jumbled. You need to write
namespace abc //now all global variables are belongs to this ns abc
int x = 15;// Real name = gui_x
int main()
{
int x = abc::x;// lui_x
return 0;
}
I hope this is not a duplicate, I've read a number of related questions but no one seemed to cover this case:
#include <iostream>
int* return_dangling_p()
{
int x = 1;
return &x; // warning: address of local variable 'x' returned
}
void some_func()
{
int x = 2;
}
int main(int argc, char** argv)
{
// UB 1
int* p = return_dangling_p();
std::cout << *p; // 1
some_func();
std::cout << *p; // 2, some_func() wrote over the memory
// UB 2
if (true) {
int x = 3;
p = &x; // why does compiler not warn about this?
}
std::cout << *p; // 3
if (true) {
int x = 4;
}
std::cout << *p; // 3, why not 4?
return 0;
}
I thought these are two cases of the same undefined behaviour. The output is 1233 while I (naively?) expected 1234.
So my question is: why doesn't compiler complain in the second case and why the stack isn't rewritten like in the case of 12? Am I missing something?
(MinGW 4.5.2, -Wall -Wextra -pedantic)
EDIT: I'm aware that it's pointless to discuss outputs of UB. My main concern was if there's any deeper reason to why one is detected by the compiler and the other isn't.
I'm not sure why the compiler doesn't complain. I suppose it's not a very common use-case, so the compiler authors didn't think to add a warning for it.
You can't infer anything useful about behaviour you observe when you are invoking undefined behaviour. The final output could have been 3, it could have been 4, or it could have been something else.
[If you want an explanation, I suggest look at the assembler that the compiler produced. If I had to guess, I'd say that the compiler optimised the final if (true) { ... } away entirely.]
why doesn't compiler complain in the second case
I am not sure. I suppose it could.
why the memory isn't rewritten like in the case of 12
It's undefined behaviour. Anything can happen.
Read on if you're really curious...
When I compile your code as-is, my compiler (g++ 4.4.3) places the two x variables in UB 2 at different locations on the stack (I've verified this by looking at the disassembly). Therefore they don't clash, and your code also prints out 1233 here.
However, the moment I take the address of the second x, the compiler suddenly decides to place it at the same address as the first x, so the output changes to 1234.
if (true) {
int x = 4; // 3, why not 4?
&x;
}
Now, this is what happens when I compile without any optimization options. I haven't experimented with optimizations (in your version of the code, there's no reason why int x = 4 can't be optimized away completely).
The wonders of undefined behaviour...