7

When I compile this code

{$WARNINGS ON}
function Test(s: string): string;
var
  t: string;
  d: double;
begin
  if s = '' then begin
    t := 'abc';
    d := 1;
  end;

  Result := t + FloatToStr(d);
end;

I get the warning "Variable 'd' might not have been initialized", but I do not get the same warning for variable 't'. This seems inconsistent. This code is only a simple example to show the compiler warnings, but I have just found a bug in my live code which would have been caught by a compile-time warning for uninitialised string variables. Can I switch this warning on somehow in Delphi 6? Or in a newer version of Delphi?

soid
  • 541
  • 8
  • 15
  • 1
    I doubt very much whether the code as it stands will compile. You're missing a 'begin' before "t:= 'abc'". – No'am Newman Apr 24 '11 at 14:52
  • Oops, typo corrected :) I typed it into SO rather than copied and pasted. – soid Apr 24 '11 at 15:04
  • @soid ... you're going to have trouble with that code if the variable s is ever anything but an empty string as FloatToString(d) will likely blow up - like the warning says d might not have been initialized. You may be giving it a value somewhere else but given what there is posted, I'd move "d := 1" to right after "begin". Alternately, I would put "d := 0" right after the begin and then set it to 1 in the if statement if that's what needs to happen. Your code isn't complete until you have cleared all warnings. – Dave Keighan Apr 24 '11 at 16:25
  • 1
    @TDelphiHobbyist Did you read the question as well as the code?!! – David Heffernan Apr 24 '11 at 16:25
  • @David, yes. I'm aware that my comment is completely off the mark. Is there a problem with discussing observations in the comments section? I posted to comments _because_ it was not even remotely an answer. I'll stop if there is. I _was_ going to post "why would you want more warnings?" – Dave Keighan Apr 24 '11 at 16:34
  • possible duplicate of [Which variables are initialized when in Delphi?](http://stackoverflow.com/questions/861045/which-variables-are-initialized-when-in-delphi) – Jeroen Wiert Pluimers Apr 24 '11 at 17:20
  • @Jeroen: Useful to provide the link, as they are related concerns. But not really a duplicate. This one is more specific, and in particular enquires whether the warning can be turned on for strings. – Disillusioned Apr 24 '11 at 19:05
  • 1
    @TDelphiHobbyist: The whole point of the question deals with the inconsistency that simple types get warnings, but strings don't. The sample code as a _contrived example_ would be rather pointless if it was written in a way that the `d` would be guaranteed to be initialised! – Disillusioned Apr 24 '11 at 19:10
  • @Craig hence the "possible" duplicate. I think it is sufficiently similar, but luckily I'm not the only one to decide :) – Jeroen Wiert Pluimers Apr 24 '11 at 19:14
  • @Craig, yes I see that. At the time I didn't consider that it was a contrived example intentionally leaving d to raise a "might not have been initialized" warning. My comment is obviously meaningless. Thanks for pointing out my mistake, very much appreciated. – Dave Keighan Apr 24 '11 at 19:18
  • @TDelphiHobbyist: Sorry for the confusion. I should have made the question and example clearer. The wording is more convoluted than it needs to be. I will edit it a little. – soid Apr 25 '11 at 08:53
  • @soid, that is very kind of you, thanks. – Dave Keighan Apr 25 '11 at 15:24

2 Answers2

12

Nope, there is no switch for this. The warning doesn't occur because a string is a compiler managed type and is always initialized by the compiler.

Marjan Venema
  • 19,136
  • 6
  • 65
  • 79
  • Why should you fear the fact that it's always initialised? – Andriy M Apr 24 '11 at 15:31
  • Does anyone know if you get such a warning if you fail to initialise a managed type that is a function result variable? – David Heffernan Apr 24 '11 at 16:26
  • @David: you do not get such a warning. For example: `function StringResult: string; begin beep; end;` compiles without hints or warnings. (D2009) – Marjan Venema Apr 24 '11 at 16:34
  • 1
    @David: and since the string is a managed type, the function Result gets initialized to an empty managed instance of that type. See [this answer by Barry Kelley](http://stackoverflow.com/questions/861045/which-variables-are-initialized-when-in-delphi/861178#861178). – Jeroen Wiert Pluimers Apr 24 '11 at 17:20
  • @Jeroen There are situations where Result does not get initialized. For example I think it doesn't get initialized if the function is called in a loop and the return value is not assigned to anything. – David Heffernan Apr 24 '11 at 17:23
  • @David: do you have a reproducible case for that? If so, it might be a bug worth reporting in QC. – Jeroen Wiert Pluimers Apr 24 '11 at 17:25
  • @Jeroen There was an SO question here recently. It's an optimisation and is by design I believe. – David Heffernan Apr 24 '11 at 17:26
  • @David: yes, that could well be an optimization by the compiler. Just checked it and though I am not game to check the generated assembly at the moment, calling the StringResult function in a loop without using the result doesn't generate a warning/hint either (only use of the function in the app). – Marjan Venema Apr 24 '11 at 17:28
  • In both `for` and `while` loop, even with optimisation off `Result` is not initialised in the following function: `function GetS: string; begin Result := Result + 'a'; end;`. However, it is initialised for calls outside a loop. And interestingly, 2 calls to `GetS` in a loop seem to track their own `Result` instance. (Delphi 2009) – Disillusioned Apr 24 '11 at 19:49
  • @Andriy M: I found a bug in my code that would have been unmasked by a compiler warning message about using a string variable before assigning it a value. The lack of an automated warning for the string variable (unlike the double) means there might be other similar bugs in my program. Without a warning I have no easy way to find all these bugs. I fear they will cause problems for me and my users. – soid Apr 25 '11 at 08:39
3

Yes :-)

Use shortstrings or pChars

{$WARNINGS ON}
function Test: String;
var
  p: pChar;
  d: double;
begin
  Result := p + FloatToStr(d);
end;
//This code will give a warning.

Seriously

No, the normal Delphi strings and shortstrings are automatically initialized to '' (empty string). Shortstrings live on the stack and don't need cleanup. Other strings are so called 'managed' types and automatically deleted when they are no longer used using reference counting.

PChars, the good news
pChars are just pointers. Delphi does not manage them.
However Delphi does automatically convert them to strings and visa versa.

pChars the bad news
If you convert a pChar to a string Delphi copies the contents of the pChar into the string and you are still responsible for destroying the pChar.
Also note that this copying takes time and if you do it a lot will slow your code down.

If you convert a string to a pChar Delphi will give you a pointer to the address the string lives in. And !! Delphi will stop managing the string. You can still assign values to the string, but it will no longer automatically grow.

From: http://www.marcocantu.com/epascal/English/ch07str.htm

The following code will not work as expected:

procedure TForm1.Button2Click(Sender: TObject);
var
  S1: String;
begin
  SetLength (S1, 100);
  GetWindowText (Handle, PChar (S1), Length (S1));
  S1 := S1 + ' is the title'; // this won't work
  Button1.Caption := S1;
end;

This program compiles, but when you run it, you are in for a surprise: The Caption of the button will have the original text of the window title, without the text of the constant string you have added to it. The problem is that when Windows writes to the string (within the GetWindowText API call), it doesn't set the length of the long Pascal string properly. Delphi still can use this string for output and can figure out when it ends by looking for the null terminator, but if you append further characters after the null terminator, they will be skipped altogether.

How can we fix this problem? The solution is to tell the system to convert the string returned by the GetWindowText API call back to a Pascal string. However, if you write the following code:

S1 := String (S1);

the system will ignore it, because converting a data type back into itself is a useless operation. To obtain the proper long Pascal string, you need to recast the string to a PChar and let Delphi convert it back again properly to a string:

S1 := String (PChar (S1));

Actually, you can skip the string conversion, because PChar-to-string conversions are automatic in Delphi. Here is the final code:

procedure TForm1.Button3Click(Sender: TObject);
var
  S1: String;
begin
  SetLength (S1, 100);
  GetWindowText (Handle, PChar (S1), Length (S1));
  S1 := String (PChar (S1));
  S1 := S1 + ' is the title';
  Button3.Caption := S1;
end;

An alternative is to reset the length of the Delphi string, using the length of the PChar string, by writing:

SetLength (S1, StrLen (PChar (S1)));
Johan
  • 74,508
  • 24
  • 191
  • 319
  • Why this -1, it's obviously tongue-in-cheek and it does state the reason why there's no warning and which strings do give warnings. – Johan Apr 24 '11 at 21:01
  • Hello Johan, I did not downvote you. I guess somebody felt shortstring is a type that is likely to introduce problems as it is limited to a length of 255 characters so it is best entirely forgotten. Actually in my case your code would have prevented the bug I found as I have not used more than 255 characters in the string. – soid Apr 25 '11 at 08:20
  • @soid: If only you think it possible, you could temporarily replace all the occurrences of `string` with `ShortString`, then compile and watch for the warnings. That way you would possibly be able to find all the places where strings are used without prior initialisation. – Andriy M Apr 25 '11 at 09:35
  • @Andriy M: I have just tried it and shortstring does not seem to give me a warning in Delphi 6. Do I need a newer version of Delphi, or am I doing something wrong? This actually seems like a fairly good idea as I could have my own string type that I can switch from string to shortstring occasionally for warning purposes, or maybe I can meddle with the built-in string type somehow. – soid Apr 25 '11 at 10:28
  • @Everybody: Is Johan mistaken in thinking shortstring will produce a warning? If so maybe I can create a type that has the required properties such as '+' just for compile-time warning purposes? – soid Apr 25 '11 at 11:10
  • @soid, sorry was mistaken about shortstrings, however you can force a warning by using `pChar` see the updated answer above. – Johan Apr 25 '11 at 16:54
  • Thanks Johan. I will experiment. – soid Apr 26 '11 at 08:34