Pointer(s)^ versus s[1]

前端 未结 7 986
南旧
南旧 2021-02-12 14:56

In a function that reads data (data meaning exclusively strings) from disk, which should I prefer? Which is better?

A) DiskStream.Read(Pointer(s         


        
相关标签:
7条回答
  • 2021-02-12 15:01

    If there is ever any chance that your function will be called with a Count of 0, then A) will work with Pointer(s)^ simply evaluating to nil while B) will crash with a range check exception.

    If you want to use B) and still handle counts of 0 gracefully, you should use:

    function TMyStream.ReadChars(out s: AnsiString; const Count: Integer): Boolean; 
    begin
     SetLength(s, Count);
     Result := (Count = 0)  or (Read(s[1], Count) = Count);
    end;
    
    0 讨论(0)
  • 2021-02-12 15:01

    Be aware that when running

    1. DiskStream.Read(Pointer(s)^, Count)
    2. DiskStream.Read(s[1], Count)
    

    The 1. version will be faster.

    But you must be sure that the s variable is explicitly local, or you have called yourself UniqueString(s) before the loop.

    Since pointer(s)^ won't call UniqueString?() low-level hidden RTL call, it will be faster than s[1], but you may override some existing data if the s string variable is shared between the current context and other context (e.g. if the last content of s was retrieved from a function from a property value, or s is sent as parameter to another method).

    In fact the fastest correct way of coding this reading an AnsiString from content is:

      s := '';
      SetLength(s,Count);
      DiskStream.Read(pointer(s)^,Count);
    

    or

      SetString(s,nil,Count);
      DiskStream.Read(pointer(s)^,Count);
    

    The 2nd version being equal to the 1st, but with one line less.

    Setting s to '' will call FreeMem()+AllocMem() instead of ReallocMem() in SetLength(), so will avoid a call to move(), and will be therefore a bit faster.

    In fact, the UniqueString?() RTL call generated by s[1] will be very fast, since you have already called SetLength() before calling it: therefore, s is already unique, and UniqueString?() RTL call will return almost immediately. After profiling, there is not much speed difference between the two versions: almost all time is spend in string allocation and content moving from disk. Perhaps s[1] is found to be more "pascalish".

    0 讨论(0)
  • 2021-02-12 15:05

    The second option is definitely more "Delphi style" (if you look at the Delphi versions of the Windows API headers, you will see that most pointer parameters have been converted to var parameters).

    In addition to that, the second option does not need a cast and is much more readable IMHO.

    0 讨论(0)
  • 2021-02-12 15:06

    I'd always use the second one which maintains type safety. I don't really buy the performance argument since you are about to hit the disk at worst, or file cache, or main memory, all of which are going to make a handful of CPU operations look somewhat trivial. Correctness should be given higher priority than performance.

    However, I would add that this is not something that should be bothering you too much since you should write this particular piece of code once and once only. Put it in a helper class and wrap it up well. Feel free to care about optimisation, re-write it as assembler, whatever takes your fancy. But don't repeat yourself.

    0 讨论(0)
  • 2021-02-12 15:13

    Definitely the array notation. Part of Delphi style is to make your code easy to read, and it's easier to tell what's going on when you spell out exactly what you're doing. Casting a string to a pointer and then dereferencing it looks confusing; why are you doing that? It doesn't make sense unless the reader knows a lot about string internals.

    0 讨论(0)
  • 2021-02-12 15:18

    The second one (DiskStream.Read(s[1], Count)). Whenever you encounter an untyped var parameter it reads like "take the address of what is passed as a parameter". So in this case you are passing the address of the first character of the string s, which is what you intended to do.

    0 讨论(0)
提交回复
热议问题