Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should variable be initialized before call to function?

When I call Functions to get a value, I usually initialize varible, in case function fails or doesn't return anything and I want to avoid dealing with uninitialized variable. I do the same for string, integer or any other type.

Example for integer variables:

vPropValue := 0;
vPropValue := GetPropValue(vObject,'Height');

IF vPropValue > 0 Then
...

this it the most common how I use it.

I know I could use:

If GetPropValue(vObject,'Height') > 0 Then
...

but with first example I avoid multiple calls to function, if I need result again later in the code.

Same for string (even though i know local strings are initialized to empty string, while integers are not an can hold any value)

vName := '';
vName := GetObjectName(vObject,'ObjectName');

IF Trim(vPropStrValue) <> '' Then
...

I know I could take steps to avoid duplicate value assignment,like making sure Function returns 0 if everything fails. But I have 100s of functions and I can't rely I never made a mistake how functions handle everything and I'm sure some don't return 0, if everything fails.

I'm trying to understand why this is not desirable practice and how to best avoid it.

EDIT

Here is example where function doesn't return proper value or 0:

function GetValue(vType:integer):integer;
begin
   if vType=1 then
    Result:=100
   else if (vType>2) and (vType<=9) then
     Result:=200;

end;

procedure TForm1.Button1Click(Sender: TObject);
var vValue:integer;
begin

  vValue:=GetValue(11);
  Button1.Caption:=IntToStr(vValue);

end;

In this case the value returned from function is some random number.

In this case the initialization appears to be valid approach. OR NOT?

EDIT 2:

As David pointed out in his answer, correct, there was a warning

[dcc32 Warning] Unit1.pas(33): W1035 Return value of function 'GetValue' might be undefined

but, I ignored it, for no reason, just didn't look there. As it let me compile it, I thought it was OK. So, I did look for the warning and I 'fixed' quite a few functions that had similar issue, because of all IFs Result might not have been defined.

EDIT 3 & CONCLUSION:

I hope it adds to the scope of question and explanation:

Maybe an example of another twist that I use in most of my Functions, would also explain why I thought my initialization of variable was needed, is that I wasn't sure my Functions would behave correctly all the times, especially in case of nested function. Mots of them still are set like this:

function GetProperty(vType:integer):integer;
begin
  Try
    if vType = 99 then
      Result:=GetDifferentProperty(vType)// <-- Call to another Function, that could return whatever...
    else
    begin
       if vType=1 then
          Result:=100
       else if (vType>2) and (vType<=9) then
         Result:=200;
    end;
  except
  end;
end;

Now I'm addressing these Try Except End; but some functions are 10 years old and to expect them to work 100%, based on my experience back then, is not something to rely on.

As the only developer on this project, I assume I should trust my functions (and he rest of my code), but I can't imagine in multiple developers environment that all function are set up properly.

So my conclusion: since I didn't take care of the basics - properly designed Functions, I need to have all these checks (variable initialization, Try Except lines..) and probably some other unneccessary stuff.

like image 703
Mike Torrettinni Avatar asked Nov 25 '15 22:11

Mike Torrettinni


People also ask

Do you need to initialize a variable before it will work?

Local variables must be initialized before use, as they don't have a default value and the compiler won't let us use an uninitialized value.

Which variable need to initialise before using it?

Local variables. Local variables are not automatically initialized; they need to be initialized before their value can be used. One method to make sure that your variable is initialized is to initialize them to some kind of default value directly. This is however something you should not do.

Is it necessary to initialize a variable?

Generally, all variables should be explicitly initialized in their declaration. The descriptive comment is optional. In most cases, variable names are descriptive enough to indicate the use of the variable.

What will happen if you use the variable without initializing it?

An uninitialized variable is a variable that has not been given a value by the program (generally through initialization or assignment). Using the value stored in an uninitialized variable will result in undefined behavior.


1 Answers

Assuming that vPropValue is a local variable then this code

vPropValue := 0;
vPropValue := GetPropValue(vObject,'Height');

is indistinguishable from

vPropValue := GetPropValue(vObject,'Height');

A simpler example might be like so:

i := 0;
i := 1;

What is the point of assigning 0 to i and then immediately assigning 1 to i? So, you would surely never write that. You would write:

i := 1;

In your code, at the top of this answer, you assign twice to the same variable. The value assigned in the first assignment is immediately replaced by the value assigned in the second assignment. Therefore, the first assignment is pointless and should be removed.

The second example is a little more complex. Assuming that your functions are written correctly, and always assign to their return value, and that vName is a local variable, then

vName := '';
vName := GetObjectName(vObject,'ObjectName');

is indistinguishable from

vName := GetObjectName(vObject,'ObjectName');

The reason why I have added an extra proviso relates to a quirk of the implementation of function return values, discussed below. The difference between this case and the case above is the return value type. Here it is a managed type, string, whereas in the first example the type is a simple Integer.

Again, given the proviso about the function always assigning to the return value, the first assignment is pointless because the value is immediately replaced. Remove that first assignment.


Regarding the function in your edit, the compiler will warn you of its erroneous implementation if you enable hints and warnings. The compiler will tell you that not all code paths return a value.

function GetValue(vType:integer):integer;
begin
  if vType=1 then
    Result:=100
  else if (vType>2) and (vType<=9) then
    Result:=200;
end;

If neither condition is met, then no value is assigned to the result variable. This function should be:

function GetValue(vType:integer):integer;
begin
  if vType=1 then
    Result:=100
  else if (vType>2) and (vType<=9) then
    Result:=200
  else
    Result:=0;
end;

I cannot stress how important it is that you always return a value from a function. In fact it is a terrible weakness that Delphi even allows your function to be compiled.


The reason that your double assignment sometimes appears useful to you is due to a quirk of of the implementation of function return values in Delphi. Unlike almost all other languages a Delphi function return value for certain more complex types is actually a var parameter. So this function

function foo: string;

is actually, semantically, the same as this:

procedure foo(var result: string);

This is a really odd decision made by the Delphi designers. In most other languages, like C, C++, C#, Java etc., a function return value is like a by-value parameter passed from callee to caller.

This means that you can, if you wish to be perverse, pass values into a function via its return value. For instance, consider this code:

// Note: this code is an example of very bad practice, do not write code like this

function foo: string;
begin
  Writeln(Result);
end;

procedure main;
var
  s: string;
begin
  s := 'bar';
  s := foo;
end;

When you call main, it will output bar. This is a rather strange implementation detail. You should not rely on it. Let me repeat myself. You should not rely on it. Do not get in the habit of initializing return values at the call site. That leads to unmaintainable code.

Instead follow the simple rule of ensuring that the function return value is always assigned by the function, and never read before it has been assigned.


More detail on the implementation of function return values is provided by the documentation, with my emphasis:

The following conventions are used for returning function result values.

  • Ordinal results are returned, when possible, in a CPU register. Bytes are returned in AL, words are returned in AX, and double-words are returned in EAX.
  • Real results are returned in the floating-point coprocessor's top-of-stack register (ST(0)). For function results of type Currency, the value in ST(0) is scaled by 10000. For example, the Currency value 1.234 is returned in ST(0) as 12340.
  • For a string, dynamic array, method pointer, or variant result, the effects are the same as if the function result were declared as an additional var parameter following the declared parameters. In other words, the caller passes an additional 32-bit pointer that points to a variable in which to return the function result.
  • Int64 is returned in EDX:EAX.
  • Pointer, class, class-reference, and procedure-pointer results are returned in EAX.
  • For static-array, record, and set results, if the value occupies one byte it is returned in AL; if the value occupies two bytes it is returned in AX; and if the value occupies four bytes it is returned in EAX. Otherwise, the result is returned in an additional var parameter that is passed to the function after the declared parameters.
like image 82
David Heffernan Avatar answered Sep 21 '22 23:09

David Heffernan