Recent

Author Topic: Question about PChar and access violation (solved)  (Read 2600 times)

Imants

  • Full Member
  • ***
  • Posts: 196
Question about PChar and access violation (solved)
« on: December 18, 2018, 07:57:33 am »
I do not understand why this code generates access violation.

Code: Pascal  [Select][+][-]
  1. var  
  2.   S, Data: String;      
  3. begin
  4.   S := 'Test';
  5.   PChar(@S[1])^ := #80; //This raises access violation  
  6.   S[1] := 'x'; //This woks as it should
  7.   SetLength(Data, 4);  
  8.   PChar(@Data[1])^ := #80; //This do not raises access violation  
  9. end;
  10.  

Why in case of S access violation is raised an in case of Data it is not raised?
« Last Edit: December 18, 2018, 12:50:45 pm by Imants »

Thaddy

  • Hero Member
  • *****
  • Posts: 14357
  • Sensorship about opinions does not belong here.
Re: Question about PChar and access violation
« Reply #1 on: December 18, 2018, 08:35:37 am »
S is an empty string. You can not cast it to a pchar and access element[1] like that. The string needs to have a length > 0 to do that.
A real PChar also needs allocated memory first.
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

Imants

  • Full Member
  • ***
  • Posts: 196
Re: Question about PChar and access violation
« Reply #2 on: December 18, 2018, 09:13:52 am »
S is an empty string. You can not cast it to a pchar and access element[1] like that. The string needs to have a length > 0 to do that.
A real PChar also needs allocated memory first.

Why S is empty string I assign S constant 'Test' and it has length 4.

balazsszekely

  • Guest
Re: Question about PChar and access violation
« Reply #3 on: December 18, 2018, 10:33:49 am »
When you typecast a string to a PChar, basically you get a pointer that points to a null-terminated string. This is OK, but immediately after you dereferene the pointer and assign a new value to s. With the assigment the original reference is lost, PChar no longer points to a valid address, hence the exception.

Imants

  • Full Member
  • ***
  • Posts: 196
Re: Question about PChar and access violation
« Reply #4 on: December 18, 2018, 11:14:37 am »
When you typecast a string to a PChar, basically you get a pointer that points to a null-terminated string. This is OK, but immediately after you dereferene the pointer and assign a new value to s. With the assigment the original reference is lost, PChar no longer points to a valid address, hence the exception.

So when I create string with SetLength or use UniqueString. Then while dereferencing I do not lose reference? Or I was just lucky?
Code: Pascal  [Select][+][-]
  1.   S := 'Test';
  2.   UniqueString(S);
  3.   PChar(@S[1])^ := #80; //This works after UniqueString
  4.  
  5.   SetLength(Data, 4);  
  6.   PChar(@Data[1])^ := #80;
  7.  

Thaddy

  • Hero Member
  • *****
  • Posts: 14357
  • Sensorship about opinions does not belong here.
Re: Question about PChar and access violation
« Reply #5 on: December 18, 2018, 11:31:24 am »
You have to understand that an empty string still has a terminating zero. You replaced that terminating zero.  Ergo: PChar now doesn't work because there is no terminating zero anymore..., but the string[1] as a string looks  valid...
Extremely bad code.... don't do that....
That terminating zero doesn't count to the string's length and is not even used for the pascal string (length is stored at negative offset) and is only there for PChar compatibility because a Pchar expects a zero terminator.
« Last Edit: December 18, 2018, 11:35:26 am by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

lucamar

  • Hero Member
  • *****
  • Posts: 4219
Re: Question about PChar and access violation
« Reply #6 on: December 18, 2018, 11:57:54 am »
Sorry to bring bad news but the original code does work for me. I only modified it to writeln the strings:

Code: Pascal  [Select][+][-]
  1. var
  2.   S, Data: String;
  3. begin
  4.   S := 'Test';
  5.   writeln(S);
  6.   PChar(@S[1])^ := #80; //This raises access violation???
  7.   writeln(S);
  8.   S[1] := 'x'; //This woks as it should
  9.   writeln(S);
  10.   SetLength(Data, 4);
  11.   PChar(@Data[1])^ := #80; //This do not raises access violation.
  12.   writeln(Data);
  13. end.

The result:
Code: Bash  [Select][+][-]
  1. $ uname -a
  2. Linux selene 3.0.0-32-generic #51-Ubuntu SMP i686
  3. $ ./pchartest
  4. Test
  5. Pest
  6. xest
  7. P

@Thaddy: The very first code line sets S, so it has nothing to do with empty strings.

ETA: Note that I tested at first without writeln's, just as it was, and it didn't raise any exception either.
« Last Edit: December 18, 2018, 12:04:42 pm by lucamar »
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

Imants

  • Full Member
  • ***
  • Posts: 196
Re: Question about PChar and access violation
« Reply #7 on: December 18, 2018, 12:36:38 pm »
Is it possible than in my case when
Code: Pascal  [Select][+][-]
  1.  S := 'Test';
S points to read-only memory because 'Test' is constant?

440bx

  • Hero Member
  • *****
  • Posts: 4014
Re: Question about PChar and access violation
« Reply #8 on: December 18, 2018, 12:37:29 pm »
I do not understand why this code generates access violation.

Code: Pascal  [Select][+][-]
  1. var  
  2.   S, Data: String;      
  3. begin
  4.   S := 'Test';
  5.   PChar(@S[1])^ := #80; //This raises access violation  
  6.   S[1] := 'x'; //This woks as it should
  7.   SetLength(Data, 4);  
  8.   PChar(@Data[1])^ := #80; //This do not raises access violation  
  9. end;
  10.  

Why in case of S access violation is raised an in case of Data it is not raised?

Your confusion is quite understandable since, at first sight, that statement should work. 

The reason it doesn't work is because of the way FPC allocates string constants when LONGSTRINGS is ON (which I believe is the default.)

When LONGSTRINGS is ON, the statement:
Code: Pascal  [Select][+][-]
  1. s := 'test';
causes FPC to put the string in a _read only_ data section.  When you try to write to it, you get an access violation.

if you turn LONGSTRINGS OFF, the statement will work because a shortstring is in memory and @s refers to a writeable memory location instead of a read-only section in the executable.

try this code:
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC                      }
  2.  
  3. {$LONGSTRINGS    OFF               }
  4. //{$LONGSTRINGS    ON                }
  5.  
  6. {$TYPEDADDRESS   ON                }
  7.  
  8.  
  9. program PcharString;
  10.  
  11. var
  12.   s : string;
  13.  
  14. begin
  15.   s := 'test';
  16.   writeln(s);
  17.  
  18.   writeln(pchar(@s[1])^);
  19.  
  20.   pchar(@s[1])^ := 'b';
  21.   writeln(pchar(@s[1])^);
  22.  
  23.   writeln(s);
  24.  
  25.   writeln;
  26. end.
  27.  

If you compile and run the above with LONGSTRINGS OFF, it will work just fine.  If you compile it with LONGSTRINGS ON, you'll get an access violation because the string "test" is in a read-only section of the PE file.

You can confirm this using any PE file dump utility.  Look at the file in hex, search for test, note the file offset then search for that offset in the PE dump.  You'll find it in a read only section which is the reason you get an access violation.

HTH.

ETA:
Quote
S points to read-only memory because 'Test' is constant?
I see you figured it out while I was typing my post.  Good for you.
« Last Edit: December 18, 2018, 12:39:08 pm by 440bx »
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Imants

  • Full Member
  • ***
  • Posts: 196
Re: Question about PChar and access violation (solved)
« Reply #9 on: December 18, 2018, 12:50:21 pm »
I do not understand why this code generates access violation.

Code: Pascal  [Select][+][-]
  1. var  
  2.   S, Data: String;      
  3. begin
  4.   S := 'Test';
  5.   PChar(@S[1])^ := #80; //This raises access violation  
  6.   S[1] := 'x'; //This woks as it should
  7.   SetLength(Data, 4);  
  8.   PChar(@Data[1])^ := #80; //This do not raises access violation  
  9. end;
  10.  

Why in case of S access violation is raised an in case of Data it is not raised?

Your confusion is quite understandable since, at first sight, that statement should work. 

The reason it doesn't work is because of the way FPC allocates string constants when LONGSTRINGS is ON (which I believe is the default.)

When LONGSTRINGS is ON, the statement:
Code: Pascal  [Select][+][-]
  1. s := 'test';
causes FPC to put the string in a _read only_ data section.  When you try to write to it, you get an access violation.

if you turn LONGSTRINGS OFF, the statement will work because a shortstring is in memory and @s refers to a writeable memory location instead of a read-only section in the executable.

try this code:
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC                      }
  2.  
  3. {$LONGSTRINGS    OFF               }
  4. //{$LONGSTRINGS    ON                }
  5.  
  6. {$TYPEDADDRESS   ON                }
  7.  
  8.  
  9. program PcharString;
  10.  
  11. var
  12.   s : string;
  13.  
  14. begin
  15.   s := 'test';
  16.   writeln(s);
  17.  
  18.   writeln(pchar(@s[1])^);
  19.  
  20.   pchar(@s[1])^ := 'b';
  21.   writeln(pchar(@s[1])^);
  22.  
  23.   writeln(s);
  24.  
  25.   writeln;
  26. end.
  27.  

If you compile and run the above with LONGSTRINGS OFF, it will work just fine.  If you compile it with LONGSTRINGS ON, you'll get an access violation because the string "test" is in a read-only section of the PE file.

You can confirm this using any PE file dump utility.  Look at the file in hex, search for test, note the file offset then search for that offset in the PE dump.  You'll find it in a read only section which is the reason you get an access violation.

HTH.

ETA:
Quote
S points to read-only memory because 'Test' is constant?
I see you figured it out while I was typing my post.  Good for you.

Thanks for clarifying things for me and providing clear answer.

440bx

  • Hero Member
  • *****
  • Posts: 4014
Re: Question about PChar and access violation (solved)
« Reply #10 on: December 18, 2018, 01:38:54 pm »
Thanks for clarifying things for me and providing clear answer.
You're welcome.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

 

TinyPortal © 2005-2018