PDA

View Full Version : Buggy scope of a variable



chamster
09-24-2007, 11:40 PM
I have code as follows (yes, it's pseudo-code).

For i = 1 to 5
Dim j As Integer
' j = 0
j = j + 1
MsgBox j
Next i


I'd expect one and the same value to be redisplayed all the five times. It doesn't happen; the j grows gibber for every loop-run.

Now, in ANY normally working and self-respecting language i've ever met, the variable j will be out of scope at the end of the loop-run. Not in VB. On the other hand, the complainer does compile the code and let's me (re?)declare the variable... That's a new one too...

What's going on here? Is the variable redeclared at every loop-run and if so - why does it retain it's value, if not - why doesn't it nag?!

Bob Phillips
09-25-2007, 12:42 AM
Why would you expect the code to increment the variable but the variable value does not change? That seems pervers in the extreme to me.

I know not what languages you refer to that, but the variable is in scope for the whole of the procedure/module/application that it is declared within, as it does with all other languages that I know. I have come across some where the value of a loop index is not guaranteed after exiting a loop, but VB/VBA is not one. So what? Why do you expect everything to be the same as some other (obscure?) language that you have experienced?

johnske
09-25-2007, 01:39 AM
No, it's not redeclared at each loop-run. It's only declared the once (and that's normally at the head of the code) with the Dim statement. THIS is declaring it twice - try to run that and Visual Basic will complain


Sub aaa()
Dim i As Long
Dim j As Integer
Dim j As Integer

For i = 1 To 5
' j = 0
j = j + 1
MsgBox j
Next i
End Sub
the good news is that the writers of Visual Basic anticipated that there may be some idiots who would try to declare a variable inside a loop and have built in error handling to handle that situation - now that's good coding eh? :)

Bob Phillips
09-25-2007, 01:50 AM
No, it's not redeclared at each loop-run. It's only declared the once (and that's normally at the head of the code) with the Dim statement. THIS is declaring it twice - try to run that and Visual Basic will complain


Sub aaa()
Dim i As Long
Dim j As Integer
Dim j As Integer

For i = 1 To 5
' j = 0
j = j + 1
MsgBox j
Next i
End Sub
the good news is that the writers of Visual Basic anticipated that there may be some idiots who would try to declare a variable inside a loop and have built in error handling to handle that situation - now that's good coding eh? :)

I see what he means about redeclaring now that I see your answer. It never occurred to me that someone might think that putting the Dim statement within the loop meant it would be refreshed each iteration, it just seems so obvious to me that it is just declaring the variable. After all, the compiler does its stuff BEFORE the run.

YellowLabPro
09-25-2007, 04:48 AM
Chamster,
My two cents. It seems to me that you really mean reset the variable, not redeclare. You wrote about it remaining in scope after the loop has completed its instructions. As Bob and John wrote, in other words, the variable is assigned to memory and the type of variable once you declare it, the value is something entirely different.
This is where w/inside the loop if you wanted a different value, you would reset it, not redeclare it.

chamster
09-25-2007, 04:57 AM
Hmmm... This will be interesting, i think. C++ and Java syntax will be in this example as follows.

for (int i; i <= 5; i++) {
int j;
j++;
std::cout << j << std::endl; // for C++
System.out.println (j); // for Java
}

Every time one enters the loop there will be a new declaration of j, hence killing the stored value. In fact, the value will be garbage collected already at the closing bracket.

Of course, since it's required not to use uninitialized variables, we'll get errors but as pointed out above - it's pseudo-code, to show my point.

Perhaps i'm unclear about the exact result of a Dim-clause. I thought it was a declaration. Since it's being done multiple times, i'd expect it to do exactly one of the below.
a) Crash, boom and bang caused by a redeclaration.
b) Reinitialization due to a new declaration as the variable went out of scope.

rory
09-25-2007, 05:07 AM
That is true and I take your point, but VB/VBA does not work that way. (and you can't use ReDim to redeclare the variable either) Just one of those things.

chamster
09-25-2007, 05:37 AM
That is true and I take your point, but VB/VBA does not work that way. (and you can't use ReDim to redeclare the variable either) Just one of those things.

Are you implying that there's actually a "protective" clause somewhere in the deeps of source code for VBA that will change the declaration to only be executed once and then skipped over every time i intentionally re-execute it, by assuming that i ment otherwise?!

Rule numero uno of variable declaration is to do so as close to the actual usage point as possible (the other school is to declare everything at the head of the method, i know, i know). It's stated everywhere from the Bible, the Coran, Karmasutra to the manual of my motorcycle!

Well, as long as i know it's this way in VBA, i can live with that. Perhaps i'm too damaged with previous experience to do this job... :)

rory
09-25-2007, 05:52 AM
Yup!
Rule number 2 is not to rely on implicit initialisation (which is what you are doing) and to explicitly initialise any variable that you use. It serves no purpose to repeatedly declare the variable's interface/data type, after all. All you need to do is add a j=0 line.
If you can do C++, VBA should be a piece of cake, other than the odd curve ball... :) No pointers to worry about (unless you want to).

chamster
09-25-2007, 05:56 AM
No, it's not redeclared at each loop-run. It's only declared the once (and that's normally at the head of the code) with the Dim statement...
...the good news is that the writers of Visual Basic anticipated that there may be some idiots who would try to declare a variable inside a loop and have built in error handling to handle that situation - now that's good coding eh? :)

At first i thought you were sarcastic. Now, that i read the other posts, i'm starting to fear that you actually think it's a good idea. I guess it's a matter of taste (good or bad, let's not get into discussion about whos is which, :devil2: ). So, how should one go about if one actually does want to declare a variable inside a loop? I'm assuming it's possible. Should i not?

chamster
09-25-2007, 06:05 AM
Yup!
Rule number 2 is not to rely on implicit initialisation (which is what you are doing) and to explicitly initialise any variable that you use. It serves no purpose to repeatedly declare the variable's interface/data type, after all. All you need to do is add a j=0 line.
If you can do C++, VBA should be a piece of cake, other than the odd curve ball... :) No pointers to worry about (unless you want to).

I went something like

Dim d as Double
d = 0

and got corrected just a few days back on this very forum as d was supposed to get the value of zero by itself. I'm feeling a bit misguided here. :( Now that i think of it, i believe i understand what was ment.

I'd also argue that it sure serves a purpose to declare/gc/redeclare because you're freeing system resources. On the other hand, for speed sake, perhaps one shouldn't use VBA in the first place... :think:

Bob Phillips
09-25-2007, 06:15 AM
Ever hear of garbage collection?

rory
09-25-2007, 06:20 AM
It may well do, but you don't have any say in when (or if) VBA does its garbage collection.
Although most people don't bother with explicit initialisation of variables (as opposed to resetting them), I'd be surprised if someone told you you shouldn't do it!

chamster
09-25-2007, 10:08 AM
Perhaps i misunderstood that person. Still, to me it looks like a redeclaration and should be reported by the compiler as an error not hidden behind some "let's help the idiots"-approach.

Thanks for everybody for helping, by the way.

johnske
09-25-2007, 05:50 PM
The Dim statement declares variables and allocates storage space i.e. it 'creates' a variable.

Once a procedure level variable has been declared/created it's available for the life of the procedure and that's its' scope - a procedure level variable has the smallest scope (it may help you to read the VBA help files regarding scope in Visual Basic)

Once created, the procedure level variable's only destroyed (released from memory) when the procedure ends. The created variable is thus quite robust and you cannot just destroy/create, destroy/create willy-nilly like you seem to be trying to do (and i completely fail to see any point in trying to do that either - if you're worried about memory, you can effectively 'redeclare' variables by reinitializing them, i.e. put your long/integer variable equal to zero, your string variable equal to "", your variant variables equal to empty, set your object variable equal to nothing, etc... {but all that's just over the top IMO})

mikerickson
09-25-2007, 06:11 PM
If Excel VBA were interpreted rather than compiled, multiple type declarations might make sense.

Dim (like Sub and End Sub) are more instructions TO the compiler than instructions for the compiler to act on.

ReDim, on the other hand, is an instruction at the same level as Set, DateSerial and most other VB statements. Use dynamic arrays (Dim myArray() as Integer) and ReDim will act the way you want Dim to.

johnske
09-25-2007, 06:34 PM
Addendum to post #15...

Note that these variables are automatically destroyed as they go out of scope, so there is no need for you to destroy them, with one exception...

Application-level object variables used in conjunction with the New keyword or application-level object variables created using 'CreateObject' are not automatically destroyed, and these do need to be set equal to nothing by the coder.

{Application-level object variables as described above are used to create and open new (maybe 2nd, 3rd, etc) hidden instances of another application such as Word, PowerPoint, etc.}

chamster
09-25-2007, 10:42 PM
The created variable is thus quite robust and you cannot just destroy/create, destroy/create willy-nilly like you seem to be trying to do (and i completely fail to see any point in trying to do that either)...

Perhaps it's a matter of coding conventions. The golden rule i've been :whip to use is "if you're not using it (right now), don't occupy it". I guess it's of more importance if you're on a system with scarse resources. Or, perhaps, it's a matter of taste or langauge. I'm not exactly an expert at VB so my surprise is by comparison to other languages, not implying that it's valid in VB.


Once a procedure level variable has been declared/created it's available for the life of the procedure and that's its' scope - a procedure level variable has the smallest scope...

If you say so. I can use it that way, no problems. Still, the idea of such a limitation (not being able to control the scope into the tiniest detail by myself) feels unfamiliar to me. On the other hand, it's not exactly a deal breaker, just an unusual approach. I guess that explains why my

For i = 1 As Integer To 5

didn't work (except for the syntax, that is). According to you, i couldn't be able to declare the integer i with scope of the for-loop anyway. I think i get the general idea. Thanks.

chamster
09-25-2007, 11:00 PM
It may well do, but you don't have any say in when (or if) VBA does its garbage collection.
Although most people don't bother with explicit initialisation of variables (as opposed to resetting them), I'd be surprised if someone told you you shouldn't do it!

I started to fear that i began to "develop facts" so i went browsing and i've found the post. It's in "Declaring a variable and assigning a value to it at once" at http://www.vbaexpress.com/forum/showthread.php?t=14987, the third post.

However, before we start scratching eachothers eyes out, i can calm the situation by pointing out that it was an misunderstanding. Norie ment that initialization wasn't needed but that was most likely under assuption that the example was to be placed in the beginning of a method (rightfully so, according ro Johnske). So, i guess there's no problem anymore.

rory
09-26-2007, 01:49 AM
As we have seen, it doesn't matter where the Dim statement is - initialisation of a variable only occurs once per procedure, and at this point, your integer will be given an initial value of 0. Whether this is true in the future we don't know (though it seems likely to me that numeric values would be initialised as 0) so it's probably better to explicitly assign the value you want in your code; for the example you have cited, you have no choice but to do that! :)
It has, I think, long been known that VB(A) does not give you the low-level control of something like the C languages; on the other hand, it's a hell of a lot easier to code in for most people. If you want to use C++, create .dll or .xll files for use with Excel?

johnske
09-26-2007, 03:05 AM
If you have something that you want to work with but don't want anything relating to this thing being retained in memory, in visual basic you have choices other than using a so-called 'local' variable, one obvious choice that immediately springs to mind being the with/end with statement.

BTW, local variables such as you describe can be the source of problems, e.g. according to the C++ standard, the scope of the variable that declares inside the "for" loop is limited to the loop. In other words, this code is legal according to the C++ standard, but some compilers, such as Visual C++ 6, can't compile it successfully. The easiest solution (http://www.codeguru.com/cpp/cpp/cpp_mfc/portability/article.php/c4051/) to this problem is to put the variable declaration outside the scope of the for loop to make all compilers happy

i.e.


const int iMax = 10;
for (int iIndex = 0; iIndex < iMax; ++iIndex)
{
// do something
}
for (iIndex = 0; iIndex < iMax; ++iIndex)
{
// do something
}

becomes...


const int iMax = 10;
int iIndex;
for (iIndex = 0; iIndex < iMax; ++iIndex)
{
// do something
}
for (iIndex = 0; iIndex < iMax; ++iIndex)
{
// do something
}

If you look at the two pieces of code above you can note that the 1st piece of code is quite similar to visual basic code where undeclared variables are in use, and the 2nd piece is quite similar to visual basic code where declared variables are in use.

Declaring all variables is always best practice, and using Option Explicit forces the explicit declaration of all variables, so it is highly recommended that you always use Option Explicit.

chamster
09-26-2007, 03:53 AM
...on the other hand, it's a hell of a lot easier to code in for most people. If you want to use C++, create .dll or .xll files for use with Excel?

I don't want to do that. I know of lost souls complaining about how their favorite language is better doing this or doing that. That's sort of childish and i hope nobody gets that idea about me. I often compare to other languages but not as "it's better in XXX" but rather "it's different in XXX". I'm only expression surprise, not diapproval. After all, smarter people than me have been creating and using VB so i'm definitely in no position to pass judgment.

Having said that, i really do appreciate all help i'm receiving.

chamster
09-26-2007, 04:02 AM
...local variables such as you describe can be the source of problems, e.g. according to the C++ standard, the scope of the variable that declares inside the "for" loop is limited to the loop. In other words, this code is legal according to the C++ standard, but some compilers, such as Visual C++ 6, can't compile it successfully. The easiest solution (http://www.codeguru.com/cpp/cpp/cpp_mfc/portability/article.php/c4051/) to this problem is to put the variable declaration outside the scope of the for loop to make all compilers happy...

I disagree. The simples solution is to add a switch (can't recall what it was, perhaps \Z...?) to the compiler command list. That forces it to compatibility with the standard (regarding this issue) and as far i've been informed, it's a "situation" admitted by MS to be a mistake.

Using the solution you suggested leads to the variable being still alive AFTER the for-loop which is, in my opinion, sign of bad judgement. I don't know how widely spread that is. I've checked with a few of my previous co-workers as well as my old aquintence at the local university.