9
#include <iostream>
using namespace std;

const int BUFSIZE = 1 << 20;
char padded_buffer[64 + BUFSIZE + 64];
char* buffer = padded_buffer + 64;

int main()
{
    buffer[-1] = '?';
    // is that always equivalent to padded_buffer[63] = '?' ?
    cout << padded_buffer[63] << "\n";
    return 0;
}

I have a piece of code like above. Basically, I need to "fence" the two side of my array for some reasons.

But I wonder if the syntax above is safe? I know that negative indexing is generally undefined behavior, but what about this case?

Huy Le
  • 1,439
  • 4
  • 19
  • 6
    The duplicate doesn't seem appropriate to me. `buffer` in this case is a pointer to the middle of some other array, _not_ an array itself. It's not at all obvious to me that this is UB. I think some people saw "negative index" and didn't read the actual code. – Nathan Pierson Oct 19 '22 at 15:34
  • 5
    @PepijnKramer (and others) `buffer[-1]` is actually `*(padded_buffer + 64 -1)` which is inside the array – 463035818_is_not_an_ai Oct 19 '22 at 15:35
  • Yes I just noticed ;) Me bad, buffer is a pointer 64 offset from 0. Its just that even after 30+ years I never though of writing it down like that. – Pepijn Kramer Oct 19 '22 at 15:35
  • I think this is not UB, but I don't have sources to back it up :) – CoffeeTableEspresso Oct 19 '22 at 15:36
  • I would never write it like that. If you want to assign `'?'` to 63th element of `padded_buffer` then just write `padded_buffer[63] = '?';` perhaps wrap the array in a custom type with adjusted `operator[]`. Neverhtheless, not saying that the question is moot, and no the duplicate doesnt fit – 463035818_is_not_an_ai Oct 19 '22 at 15:37
  • Also, not a duplicate, so I think this should be reopened – CoffeeTableEspresso Oct 19 '22 at 15:37
  • I did some looking up : The expression `E1[E2]` is identical (by definition) to `*((E1)+(E2))` and the type of E2 can be an integral type (thus a signed int). So I think it is not UB(just strange) – Pepijn Kramer Oct 19 '22 at 15:43
  • @NathanPierson Here is a better dupe: [Why does C++ allow indexing with negative indices and indices larger than the length of the array minus 1?](https://stackoverflow.com/a/52067503/12002570) that explains `p[-10]` can be perfectly valid as in this case where `p` is a pointer and not an array just like here `buffer` is a pointer and not an array. – Jason Oct 19 '22 at 16:07
  • 1
    My favorites are expressions of kind `(i%10)["0123456789"]`. But yeah, for arythmetic-capable operands that's legal – Swift - Friday Pie Oct 19 '22 at 16:51

1 Answers1

10

From https://eel.is/c++draft/expr.sub#2:

The expression E1[E2] is identical (by definition) to *((E1)+(E2)), except that in the case of an array operand, the result is an lvalue if that operand is an lvalue and an xvalue otherwise.

And pointer arithmetics is defined as long as you only reach pointers to elements within the same array.

There is no undefined behavior. buffer[-1] is just *(padded_buffer + 64 -1).


It is somewhat opinion based, but buffer[-1] looks odd. Imho it obfuscates the fact that buffer is not an array but a pointer to an element in the middle of the actual array. If you want to assign something to the 63th element of the array padded_buffer then just do that: padded_buffer[63] = '?';.

In a comment I suggested to wrap the array in a custom type with overloaded operator[], but this will not solve the issue of weird looking x[-1]. Perhaps I'd rather make the index transformation directly visible in the calling code, to have padded_array[ transform_index(-1) ].

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • Here is a better dupe: [Why does C++ allow indexing with negative indices and indices larger than the length of the array minus 1?](https://stackoverflow.com/a/52067503/12002570) that explains `p[-10]` can be perfectly valid as in this case. – Jason Oct 19 '22 at 16:05
  • 2
    This answer is certainly correct, but the last two paragraphs are opinion-based. The answer is simply "yes, the behaviour is well-defined"; there are certainly contexts in which negative indexing is natural. Consider, for example, an array used as a stack, where `top` points at the next available slot. `top[-2]` seems to me a perfectly reasonable way to reference the penultimate stack element (assuming you know there are at least two things on the stack). You are free to disagree with that style, since it is a matter of opinion. – rici Oct 19 '22 at 16:10
  • `buffer[-1] looks odd` yes it certainly looks odd in C++, hence the question. Perhaps there's a use case somewhere for this feature :D – Huy Le Oct 19 '22 at 16:27