This is the perfect time to learn a bit of assembly-level debugging 
This is the address of the first element. Let's store it in a convenience variable:
set $arr := $__ # store examination result in arr
While knowing your way in asm is certainly useful, for cases like this it is imho beyound of being last resort....
So you got the address, and in your case in memory (in the frame). But if you have some more complex code, then the value of the array will change location. Even if there is a location in mem, while you step through the code, that mem location is likely not being updated most of the time. Instead the value is hold in a register. Well that would be ok, we can examine a register.
But the value will be held in different registers over time, and then temporarily in memory, and back in registers.
Each time you single step, you need to find where the value has moved too... It is possible, but a heck of a lot of work...
And then it depends what is in the array. With "simple numbers" you can tell gdb to display the range of memory as a list of integers (or words, or qwords, ...). I don't know the gdb syntax, but I am sure it can be done.
But if the array contains records, or pointer to records, and you want to display the first n elements in one go? I would guess its possible, though I don't know how to tell gdb that.
And (at least in older gdb version) certain mix of pointers and structures did get gdb to give errors. At least when debug info was coming from fpc (which uses different dwarf encoding than gcc for some data types / so gdb is not as well tested for those).
That said, if you use fpdebug (if you are on Win/Linux -- i386/x86-64) and you need to cast numbers/addresses into other data (be that due to looking at asm, or for any other reason), then you can express that in Pascal-ish ways.
%rbp for a register
$12345678 for a number
^pointer($12345678)^ or ^qword($12345678)^ for reading from memory (depending on what types are declared)
^longint(%rbp-$10)[0..9] for 9 longints at the address
^TMyRecord(%rbp-$10)[0..9] for 9 TMyRecords at the address
...
But in the end, if you compiled the code yourself... Just don't add the extra work, just compile without the optimization...
@LemonPartyI don't know if there is a reason why you try to compile with O4?
** If other parts of the code are to slow otherwise, and you don't need to debug the other parts, then try to use
{$optimization off} not sure, similar / please google fpc docs
to disable it for the code you need to debug.
Or better, compile with -O- (no opt) and use the directive to enable it for just the parts of your codes that really, really, really need it.
** If you are trying to find an error that only happens when you compile with optimization....
There are several possibilities.
1) It could be an uninitialized value (including dangling pointer). "valgrind memcheck" is really good at finding them. But you need to be able to run your code on Linux. You can give the code compiled with O1 to valgrind. Even if the error does not show. valgrind may still be able to detect it. E.g. if with O1 the dangling pointer points to a place that does not cause an error, it is still dangling, valgrind will know that the memory was freed.
2) Threads, if you use them
Optimization changes timing. So if there are race conditions, the may only show with opt on. But that also means they may not show in the debugger at all.
3) Depending on your compiler version, you may be looking at a bug in the compiler's optimizer. I.e. the compiler does incorrectly translate your code.
If that is the case, then it wont be easy to find. Most likely this means to analyse/compare generate asm. You can toggle individual optimizations, to find with which optimization the error happens or goes away.
But you need then to be sure its not your code. And that means to actually find the wrong asm, and prove its wrong by the compiler....
4) ...