Search code examples
binaryfilespascal

Binary file error in Lazarus Pascal with custom records - error SIGSEGV


I don't work with Pascal very often so I apologise if this question is basic. I am working on a binary file program that writes an array of custom made records to a binary file.

Eventually I want it to be able to write multiple arrays of different custom record types to one single binary file.

For that reason I thought I would write an integer first being the number of bytes that the next array will be in total. Then I write the array itself. I can then read the first integer type block - to tell me the size of the next blocks to read in directly to an array.

For example - when writing the binary file I would do something like this:

    assignfile(f,MasterFileName);
      {$I-}
      reset(f,1);
      {$I+}
      n := IOResult;
      if n<> 0 then
      begin
          {$I-}
          rewrite(f);
          {$I+}
      end;
      n:= IOResult;
      If n <> 0 then
      begin
         writeln('Error creating file: ', n);
      end
      else
      begin
      SetLength(MyArray, 2);

      MyArray[0].ID := 101;
      MyArray[0].Att1 := 'Hi';
      MyArray[0].Att2 := 'MyArray 0 - Att2';
      MyArray[0].Value := 1;

      MyArray[1].ID := 102;
      MyArray[1].Att1:= 'Hi again';
      MyArray[1].Att2:= MyArray 1 - Att2';
      MyArray[1].Value:= 5;

      SizeOfArray := sizeOf(MyArray);

      writeln('Size of character array: ', SizeOfArray);
      writeln('Size of integer var: ', sizeof(SizeOfArray));

      blockwrite(f,sizeOfArray,sizeof(SizeOfArray),actual);

      blockwrite(f,MyArray,SizeOfArray,actual);

      Close(f);

Then you could re-read the file with something like this:

Assign(f, MasterFileName);
Reset(f,1);
blockread(f,SizeOfArray,sizeof(SizeOfArray),actual);
blockread(f,MyArray,SizeOfArray,actual);

Close(f); 

This has the idea that after these blocks have been read that you can then have a new integer recorded and a new array then saved etc.

It reads the integer parts of the records in but nothing for the strings. The record would be something like this:

TMyType = record
ID : Integer;
att1 : string;
att2 : String;
Value : Integer;
end;

Any help gratefully received!!


Solution

  • TMyType = record
    ID : Integer;
    att1 : string;   // <- your problem
    

    That field att1 declared as string that way means that the record contains a pointer to the actual string data (att1 is really a pointer). The compiler manages this pointer and the memory for the associated data, and the string can be any (reasonable) length.

    A quick fix for you would be to declare att1 something like string[64], for example: a string which can be at maximum 64 chars long. That would eliminate the pointer and use the memory of the record (the att1 field itself, which now is a special static array) as buffer for string characters. Declaring the maximum length of the string, of course, can be slightly dangerous: if you try to assign the string a string too long, it will be truncated.

    To be really complete: it depends on the compiler; some have a switch to make your declaration "string" usable, making it an alias for "string[255]". This is not the default though. Consider also that using string[...] is faster and wastes memory.