int var declared in "for" not local?

R

Robbie Hatley

I'm getting a bizarre error with this code:

...
case WM_COMMAND:
{
switch (LOWORD(wParam)) // switch (control ID)
{
...
case IDC_RAIN_ENABLE_SIMPLE:
{
if (BN_CLICKED == HIWORD(wParam))
{
for ( int i = 0 ; i < 11 ; +i)
{
ShowWindow(GetDlgItem(hDlg, RainControls), SW_SHOW);
}

for ( int i = 11 ; i < 25 ; +i) // !!! ERROR !!! ERROR !!!
ERROR !!!
{
ShowWindow(GetDlgItem(hDlg, RainControls), SW_HIDE);
}
}
break;
}
...
break;
}
...
break;
}
...
break;
...


My compiler here at work (MS VC++ 6.0) balks at the line marked
"ERROR" above. It says this is a "redefinition" of i .
How can this be? Isn't i local to each for statement? Doesn't
the closing curly brace of the "0 to 10" for statement make i
go out of scope? So defining a new i in the next for statement
should be perfectly ok, shouldn't it?

Am I missing something here, or is this a bug in MS VC++ 6.0?
 
M

mlimber

Robbie said:
I'm getting a bizarre error with this code:

...
case WM_COMMAND:
{
switch (LOWORD(wParam)) // switch (control ID)
{
...
case IDC_RAIN_ENABLE_SIMPLE:
{
if (BN_CLICKED == HIWORD(wParam))
{
for ( int i = 0 ; i < 11 ; +i)
{
ShowWindow(GetDlgItem(hDlg, RainControls), SW_SHOW);
}

for ( int i = 11 ; i < 25 ; +i) // !!! ERROR !!! ERROR !!!
ERROR !!!
{
ShowWindow(GetDlgItem(hDlg, RainControls), SW_HIDE);
}
}
break;
}
...
break;
}
...
break;
}
...
break;
...


My compiler here at work (MS VC++ 6.0) balks at the line marked
"ERROR" above. It says this is a "redefinition" of i .
How can this be? Isn't i local to each for statement? Doesn't
the closing curly brace of the "0 to 10" for statement make i
go out of scope? So defining a new i in the next for statement
should be perfectly ok, shouldn't it?

Am I missing something here, or is this a bug in MS VC++ 6.0?


It's a non-conformancy in VC++ 6. Their standard solution is to upgrade
to VC.NET, which might be expensive or otherwise undesirable, to wrap
for loops in a set of curly braces, which is ugly, or to use a macro:

#define for if(0);else for

Cheers! --M
 
R

Robbie Hatley

It's a non-conformancy in VC++ 6.

I see! Thanks for the info. Looks like MS is handling
var. decls. in for parens. the same way as some old
implimentations of C did, if I remember correctly.
(Wasn't this the way K&R C did it?) I'll just have to
remember that the scope of i is the block ENCLOSING
the for statement.

I think the way I'll handle that is like so:

case BLAT:
{
int i;
for ( i = 0; i < 11; ++i )
{
DoSomeStuff(Splat);
}
for ( i = 11; i < 25; ++i )
{
DoOtherStuff(Splat);
}
break;
}

Yes, that works.
 
M

Marcus Kwok

mlimber said:
Robbie said:
for ( int i = 0 ; i < 11 ; +i)
{
ShowWindow(GetDlgItem(hDlg, RainControls), SW_SHOW);
}

for ( int i = 11 ; i < 25 ; +i) // !!! ERROR !!! ERROR !!!
ERROR !!!
{
ShowWindow(GetDlgItem(hDlg, RainControls), SW_HIDE);
}

My compiler here at work (MS VC++ 6.0) balks at the line marked
"ERROR" above. It says this is a "redefinition" of i .
How can this be? Isn't i local to each for statement? Doesn't
the closing curly brace of the "0 to 10" for statement make i
go out of scope? So defining a new i in the next for statement
should be perfectly ok, shouldn't it?

Am I missing something here, or is this a bug in MS VC++ 6.0?


It's a non-conformancy in VC++ 6. Their standard solution is to upgrade
to VC.NET, which might be expensive or otherwise undesirable, to wrap
for loops in a set of curly braces, which is ugly, or to use a macro:

#define for if(0);else for


Additionally, even with VC.NET 2003, you must pass a commandline option
to the compiler to enforce this rule:

/Zc:arg1[,arg2] C++ language conformance, where arguments can be:
forScope - enforce Standard C++ for scoping rules
wchar_t - wchar_t is the native type, not a typedef
 
M

Mike Smith

Marcus said:
Additionally, even with VC.NET 2003, you must pass a commandline option
to the compiler to enforce this rule:

/Zc:arg1[,arg2] C++ language conformance, where arguments can be:
forScope - enforce Standard C++ for scoping rules
wchar_t - wchar_t is the native type, not a typedef

Well, yes and no. If you don't set that option, the VC++ 2003 compiler
will try to figure out what you mean. I.e. if you do this:

for (int i = 0; i < 10; ++i) a = i;
cout << i << endl;

then the output will be "10", but if you do this:

for (int i = 0; i < 10; ++i) a = i;
int i = 37;
cout << i << endl;

then the output will be "37".
 
M

Marcus Kwok

Mike Smith said:
Marcus said:
Additionally, even with VC.NET 2003, you must pass a commandline option
to the compiler to enforce this rule:

/Zc:arg1[,arg2] C++ language conformance, where arguments can be:
forScope - enforce Standard C++ for scoping rules
wchar_t - wchar_t is the native type, not a typedef

Well, yes and no. If you don't set that option, the VC++ 2003 compiler
will try to figure out what you mean. I.e. if you do this:

for (int i = 0; i < 10; ++i) a = i;
cout << i << endl;

then the output will be "10"


Yes, though this is not standard C++.
but if you do this:

for (int i = 0; i < 10; ++i) a = i;
int i = 37;
cout << i << endl;

then the output will be "37".


Yes. I get a warning, but the output is still 37 as you say:

#include <iostream>

int main()
{
int a[10];

for (int i = 0; i < 10; ++i) { // <-- line 7
a = i;
}

int i = 37; // <-- line 11

std::cout << i << '\n'; // <-- line 13

return 0;
}

test.cpp(13) : warning C4288: nonstandard extension used : 'i' : loop
control variable declared in the for-loop is used outside the for-loop
scope; it conflicts with the declaration in the outer scope
test.cpp(11) : definition of 'i' used
test.cpp(7) : definition of 'i' ignored


With /Zc:forScope:

test.cpp(13) : warning C4258: 'i' : definition from the for loop is
ignored; the definition from the enclosing scope is used
test.cpp(7) : definition of 'i' ignored
test.cpp(11) : definition of 'i' used
 
R

Robbie Hatley

Roland Pibinger said:
The official Microsoft workaround [for the "variables
declared inside for-loop parentheses are not local" bug]
is:

#define for if(0);else for

http://support.microsoft.com/default.aspx?scid=kb;en-us;167748

Ok, thanks for the info,

However, I don't think I'll use that particular fix
globally, because the program I'm maintaining at work
is about 325,000 lines of poorly-written C code, converted
to C++ (by me), with another 25,000 lines of C++ code added
in over a couple years by three different maintainance
programmers (including myself). I'm afraid somewhere,
someone has actually used-and-depended-on the "non-local-for-
loop-variables" effect. If they did this:

int Borogoves(int Brillig[], int Gyre, int Gimble)
{
for (int Mimsy = 0; Mimsy < Gimble; ++Mimsy)
{
if (Gyre == Brillig[Mimsy]) break;
}
return Mimsy;
}

and if I did this:

// in main.h (included in all *.cpp files):
#define for if(0);else for

that would break Borogoves() . It would no longer
even compile.

But I'll keep the workaround in mind for use in smaller
files with few for loops.
 
O

Old Wolf

Robbie said:
However, I don't think I'll use that particular fix
globally, because the program I'm maintaining at work
is about 325,000 lines of poorly-written C code, converted
to C++ (by me),

What do you mean by "converted to C++" exactly ?
Renaming .c files to .cpp and changing malloc to new,
is a great way to produce unmaintainable code.

It's probably too late now, but IMHO it is better to leave
C files as C and write new stuff as C++ (and link the two
together, which is simple) , until you are ready to fully
re-write a particular unit as good C++.
with another 25,000 lines of C++ code added
in over a couple years by three different maintainance
programmers (including myself). I'm afraid somewhere,
someone has actually used-and-depended-on the
"non-local-for-loop-variables" effect. If they did this:

int Borogoves(int Brillig[], int Gyre, int Gimble)
{
for (int Mimsy = 0; Mimsy < Gimble; ++Mimsy)
{
if (Gyre == Brillig[Mimsy]) break;
}
return Mimsy;
}

and if I did this:

// in main.h (included in all *.cpp files):
#define for if(0);else for

that would break Borogoves() . It would no longer
even compile.

Then you would get compiler errors in every place that
relied on the non-standard behaviour, and you could quickly
fix it all.

However a more subtle problem is:

int foo()
{
int mimsy = 3;
if ( bar() )
{
for (int mimsy = 0; mimsy != 5; ++mimsy)
qux();
return mimsy;
}
}

where the behaviour would silently change with this 'fix'.
Perhaps you could set your compiler to generate assembly
output, then make the fix and do a diff on the generated
assembly. You'd need to be heavily optimising of course,
so that it skips out the extra "if..else" .
 
R

Robbie Hatley

Old Wolf said:
What do you mean by "converted to C++" exactly ?
Renaming .c files to .cpp and changing malloc to new,
is a great way to produce unmaintainable code.

Oh believe me when I tell you, this code I'm maintaining
was plenty "unmaintainable" as a C program, and continues
to be equally "unmaintainable" as a C++ program. Which
makes maintaining it loads of <sarcasm>fun</sarcasm>.

I didn't change any malloc to new. There weren't any,
actually. Lots of GlobalAlloc(), which is the Windows
version of the same thing. I left those alone.

The reasons I converted the program to C++ were two:
1. To get the much stronger typing of MS-VC++ 6's C++
compiler, in place of the weak typing of it's C compiler.
(Which involved fixing about 5000 compiler errors and
10000 linker errors after the switch, due to the author's
mind-boglingly sloppy handling of data types.)
2. To allow use of the C++ STL, especially maps and deques.
(We were adding features that needed maps of structs,
keyed by ints, where the structs contained deques. Doing
that in C would have been unwieldy, but in C++, it was
a breeze.)
It's probably too late now

Waaaaaaay too late.
but IMHO it is better to leave C files as C and write new
stuff as C++ (and link the two together, which is simple)

Why? See above under "stronger typing". After converting
to C++ and fixing the many thousands of compiler and linker
errors which immediately cropped up, my co-workers and I
found that many of the bugs which customers and field-service
reps had be complaining about over the years mysteriously
vanished overnight. The key was, taking the time to figure
out why each data-type conflict was occuring, and to try to
solve it at its source, rather than bandaid it with a
typecast, which is a good way to hide bugs rather than fixing
them.

I agree that if you have a WELL-WRITTEN C program, and
want to add C++ stuff to it (STL, containers, algorithms,
templates, classes, polymorphism, whatever), it's best to
leave the C files alone, and write the new stuff as separate
C++ files and just link 'm in. (Taking measures to ensure
stuff links right, of course, which involves heavy use of
"extern C" among other things.)

However, this particular C program was NOT well-written,
so converting the whole #! to C++ actually helped.
until you are ready to fully re-write a particular unit as
good C++.

That's not going to happen in the life of this program.
The money's not there. The bosses want bug fixes and new
features, not re-factoring.

I wrote about the possible use of the "non-local for vars"
in the program, and my fear of breaking such uses, and
Old Wolf replied:
Then you would get compiler errors in every place that
relied on the non-standard behaviour, and you could quickly
fix it all.

Maybe, maybe not. As you later admited in the same post:
However a more subtle problem is:

int foo()
{
int mimsy = 3;
if ( bar() )
{
for (int mimsy = 0; mimsy != 5; ++mimsy)
qux();
return mimsy;
}
}

where the behaviour would silently change with this 'fix'.

Which is exactly why I'm not going to impliment MS's
recommended fix globally. Like I say, I may use it
in some individual files with few for statements, where
I can clearly see it won't break anything.
Perhaps you could set your compiler to generate assembly
output, then make the fix and do a diff on the generated
assembly. You'd need to be heavily optimising of course,
so that it skips out the extra "if..else" .

Unless one knows i386 assembly rather well, I don't
see how that would help. Two versions of a C++ program
that generate different assembly outputs may be functionally
different... or they may be functionally the same. I don't
know assembly language well enough to tell the difference.
I'd be squinting, mumbling "PUSH here, POP there, MOV here,
ADD there, and what's this NOP doing? Oh, right, nothing...
Um... HELP!!!" No, I think I'll stick to C++.

--
Cheers!
Robbie Hatley
Tustin, CA, USA
email: lonewolfintj at pacbell dot net
web: home dot pacbell dot net slant earnur slant
 
O

Old Wolf

Robbie said:
Unless one knows i386 assembly rather well, I don't
see how that would help. Two versions of a C++ program
that generate different assembly outputs may be functionally
different...

The same source code would generate the same assembly each
time it's compiled. If this 'fix' is the only difference, it seems
plausible to me that the assembly generated would be the same
in every case, or similar enough that perusing the results of a 'diff'
will tell you which changes made no difference and which ones
suddenly referenced a different variable.
 
O

Old Wolf

Robbie said:
The reasons I converted the program to C++ were two:
1. To get the much stronger typing of MS-VC++ 6's C++
compiler, in place of the weak typing of it's C compiler.
(Which involved fixing about 5000 compiler errors and
10000 linker errors after the switch, due to the author's
mind-boglingly sloppy handling of data types.)

A good reason. While we're on this topic, one way that has
succeeded for me in the past is to compile the source with
GCC, and use the free gnu-win32 package to provide the
windows headers so that it will compile. (Or even write your
own header if the project doesn't use much winapi stuff).
GCC is an excellent lint tool :)
 
N

n2xssvv g02gfr12930

Old said:
Robbie Hatley wrote:




A good reason. While we're on this topic, one way that has
succeeded for me in the past is to compile the source with
GCC, and use the free gnu-win32 package to provide the
windows headers so that it will compile. (Or even write your
own header if the project doesn't use much winapi stuff).
GCC is an excellent lint tool :)

You probably well aware of this already, but I've seen code using C++
objects inside memory allocated using malloc() and free(). Unfortunately
, as far as I know it still exists along with all the hanging allocated
memory that it causes, not to mention no constructor calls! So, good
luck and take great care, as the task you're undertaking is fraught with
difficulties that can catch the best of us, (I can only hope that I'm
one of the best).

JB
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top