Variables - using Global, Local and ByRef
From AutoIt Wiki
These 3 keywords confuse many coders - this tutorial should help make their use clear.
In most programming languages you can define the scope of variables - this determines the visibility or accessibility of the variable from different parts of the program.
In AutoIt there are 2 possibe scopes: Global and Local.
- A Global variable is visible throughout your script - any function can both read its value and, importantly, change it.
- A Local variable exists only within the function in which it is declared and is destroyed when the function terminates. It is invisible to any other function unless it is passed as a parameter.
There is also a third scope - Static - but that is still experimental and this tutorial will be updated when and if required.
Why do we need to scope variables? Well, it is often useful to have a single variable used throughout the script - perhaps the name of an Ini file which if declared as Global can then be read by any function that requires it. And Local variables allow us to save resources by only using variables as long as we need - for example if we create a large array which is only needed within a single function.
We scope variables by using the Global and Local keywords when the variables are first declared - look at the examples below. By default, AutoIt scopes any variables declared in the main body of a script (that is not between a Func and EndFunc pair) as Global and any variables declared within function declarations as Local. But it is a good idea to explicitly declare your variables to make sure that you get what you want. For example, you can declare a variable as Global within a function - although you may get a gentle reminder when you run your script within SciTE that this is not recommended. But there is no point declaring any variables in the main body of a script as Local - they will be Global regardless. If you use the AutoItSetOption("MustDeclareVars", 1) directive you MUST declare the scope of your variables or you will get error messages.
In the Help file pages for Global and Local you will also see mention of Dim and ReDim. Using Dim will declare a variable and satisfy the AutoItSetOption just mentioned, but will let AutoIt scope the variable using the default settings as described above. It is much better practice to use Global/Local to explicitly set the scope you require. Despite its similar name, ReDim is only used to resize arrays without destroying the content and is outside the scope of this tutorial.
Let us now look at a simple example when we read and change Global variables in a function:
Global $Var_1 = "Variable 1" Global $Var_2 = "Variable 2" ; Read the variables MsgBox(0x40040, "Reading", "In The Main Script" & @CRLF & @CRLF & "Variable 1 = " & $Var_1 & @CRLF & "Variable 2 = " & $Var_2) ; Read the variables in a function _Function() ; And now read the variables again - see that $iVar_2 has changed MsgBox(0x40040, "Reading", "Back In The Main Script" & @CRLF & @CRLF & "Variable 1 = " & $Var_1 & @CRLF & "Variable 2 = " & $Var_2) Func _Function() ; Read the variables MsgBox(0x40040, "Reading", "In The Function" & @CRLF & @CRLF & "Variable 1 = " & $Var_1 & @CRLF & "Variable 2 = " & $Var_2) ; Now let us change one of the variables WITHIN the function $Var_2 = "Changed Variable 2" EndFunc
Here is a simple example showing how Local variables are only visible INSIDE their own function:
Global $Var_1 = "Variable 1" ; Read the variable MsgBox(0x40040, "Reading", "In The Main Script" & @CRLF & @CRLF & "Variable 1 = " & $Var_1) ; Now run a function _Function() ; And now try to read Variable 3 OUTSIDE the function MsgBox(0x40040, "Reading", "Back In The Main Script" & @CRLF & @CRLF & "Variable 1 = " & $Var_1 & @CRLF & "Variable 3 = " & $Var_3) Func _Function() ; Declare a LOCAL variable Local $Var_3 = "Variable 3" ; Read the variables MsgBox(0x40040, "Reading", "In The Function" & @CRLF & @CRLF & "Variable 1 = " & $Var_1 & @CRLF & "Variable 3 = " & $Var_3) EndFunc
You should have got errors telling you that $Var3 was "possibly used before declaration" and an "undeclared global variable". This is because $Var3 exists only WITHIN the function - when the function ends it is destroyed.
This means that we can use the same variable name in different functions without causing a problem - very useful for simple variables which are only used temporarily:
Function_1() Func Function_1() ; Declare the variable in this function Local $sString = "Variable in Function One" ; Read the value MsgBox(0x40040, "Reading", "In Function One" & @CRLF & @CRLF & "Variable = " & $sString) ; Now run Function Two Function_2() ; Now read the variable again - we still get the variable from this function ; The variable in Function 2 has been destroyed now the function has terminated MsgBox(0x40040, "Reading", "In Function One" & @CRLF & @CRLF & "Variable = " & $sString) EndFunc Func Function_2() ; Declare a variable with the SAME name Local $sString = "Variable in Function Two" ; Read this variable - see how we get the value in this function, not the value in Function One MsgBox(0x40040, "Reading", "In Function Two" & @CRLF & @CRLF & "Variable = " & $sString) EndFunc
AutoIt even allows you to use the same name for Global and Local variables, although this is NOT recommended as it could easily lead to confusion. If there is a naming conflict of this kind, AutoIt uses the Local value as you can see here:
; Declare the variable as Global Global $sString = "Global Variable in the Main Script" ; Read it MsgBox(0x40040, "Reading", "In the Main Script" & @CRLF & @CRLF & "Variable = " & $sString) ; Run the functions which declare Local variables with the same name Function_1() ; And now see that the Global variable still exists unchanged MsgBox(0x40040, "Reading", "In the Main Script" & @CRLF & @CRLF & "Variable = " & $sString) Func Function_1() ; Declare a variable with the SAME name as Local to this function Local $sString = "Variable in Function One" ; Read the value - we get the value of the Local variable in this function, not the Global one MsgBox(0x40040, "Reading", "In Function One" & @CRLF & @CRLF & "Variable = " & $sString) EndFunc
As a general rule, try to have as few Global variables as you can. Remember that you can share Local variables between functions - and even change their values - by passing them as parameters as we will now see.
Passing variables as parameters using ByRef
When you call a function in AutoIt, you can pass parameters to the function by including them in the parentheses following the function name like this - Function(Parameter_1, Parameter_2). Passing variables in this way means that you can use variables that have been declared as Local within other functions. Note that the parameters are automatically declared as Local variables within the called function and so do not need to be declared again in the code.
Variables can be passed as parameters to a function in 2 ways:
By Value - This is the default. The parameter is treated as a Local variable within the function to which it is passed and any changes made to it are lost when the function ends. So no changes are made to the Local variable within the original function.
By Reference - This is used when ByRef is added BEFORE the variable name in the parameter list of the function declaration. Now any changes to the variable made in the function to which it is passed also affect the Local variable in the original function. If you do understand why this such a powerful capability, you need to read the Global/Local section again!
Here is a small example to show how parameters are passed when calling a function:
Func_A() Func Func_A() ; Declare variables Local $iVar_A1 = 10 Local $iVar_A2 = 20 ; And read them MsgBox(0x40040, "Read", "Initial read in Func A:" & @CRLF & "Var A1 = " & $iVar_A1 & @CRLF & "Var A2 = " & $iVar_A2) ; Now pass these variables to another function - we use the names we have already declared in this function Func_B($iVar_A1, $iVar_A2) ; We changed the parameters in Func_B - but nothing happened to the variables in this function MsgBox(0x40040, "Read", "Second read in Func A:" & @CRLF & "Var A1 = " & $iVar_A1 & @CRLF & "Var A2 = " & $iVar_A2) EndFunc Func Func_B($iVar_B1, $iVar_B2) ; There is no need to declare the variables as parameters are automatically Local in scope ; Now read these variables - note that they have the same value as the variables in Func_A MsgBox(0x40040, "Read", "Initial read in Func B:" & @CRLF & "Var B1 = " & $iVar_B1 & @CRLF & "Var B2 = " & $iVar_B2) ; Let us change them $iVar_B1 = 100 $iVar_B2 = 200 ; And confirm that they have changed MsgBox(0x40040, "Read", "Second read in Func B:" & @CRLF & "Var B1 = " & $iVar_B1 & @CRLF & "Var B2 = " & $iVar_B2) ; Now return to the other function EndFunc
As you can see, the Local variables within Func_A are read perfectly by Func_B when passed as parameters, even though they were declared as Local in Func_A. However, although Func_B changes the value of the variables, the values of the original variables within Func_A are not changed.
If we want to change the value of the variables in Func_A from within Func_B, we need to use the ByRef keyword as explained above and illustrated here:
Func_A() Func Func_A() ; Declare variables Local $iVar_A1 = 10 Local $iVar_A2 = 20 ; And read them MsgBox(0x40040, "Read", "Initial read in Func A:" & @CRLF & "Var A1 = " & $iVar_A1 & @CRLF & "Var A2 = " & $iVar_A2) ; Now pass these variables to another function - but this time we will pass $iVar_A2 By Reference Func_B($iVar_A1, $iVar_A2) ; We changed the parameters in Func_B - this time we see that we have changed $iVar_A2 MsgBox(0x40040, "Read", "Second read in Func A:" & @CRLF & "Var A1 = " & $iVar_A1 & @CRLF & "Var A2 = " & $iVar_A2) EndFunc Func Func_B($iVar_B1, ByRef $iVar_B2) ; Note the ByRef keyword in the function declaration for the second parameter ; Now read these variables - they have the same value as the variables in Func_A MsgBox(0x40040, "Read", "Initial read in Func B:" & @CRLF & "Var B1 = " & $iVar_B1 & @CRLF & "Var B2 = " & $iVar_B2) ; Let us change them $iVar_B1 = 100 $iVar_B2 = 200 ; And confirm that they have changed MsgBox(0x40040, "Read", "Second read in Func B:" & @CRLF & "Var B1 = " & $iVar_B1 & @CRLF & "Var B2 = " & $iVar_B2) ; Now return to the other function EndFunc
ByRef is a very powerful tool - but use it carefully, or you may find that you have changed a variable when you did not want to!