1

Question:

If I have two pointers (essentially a begin and an end) which are qualified with restrict. The begin pointer is used for dereferencing/reading, and the end pointer is a one-past-the-end pointer that is never dereferenced and is only used to check the size of the range (via end - begin). Once the range is consumed I expect begin and end to be equal and end - begin to be 0, though at this point the two pointers will never be dereferenced.

Given restrict's restrictions on pointers, is it well-defined behavior to subtract and compare these two pointers?

MVCE:

I have some code like the following:

#include <stddef.h>

struct Reader {
  const char* restrict data;
  size_t size;
};

char read_char(struct Reader* reader) {
  --reader->size;
  return *reader->data++;
}

int main(int argc, char* argv[]) {
  struct Reader reader = {
    .data = argv[1],
    .size = argv[1] ? strlen(argv[1]) : 0,
  };

  if (reader.size > 0) {
    return read_char(&reader);
  }
  return 0;
}

I'd like to change it so that instead of having to modify both data and size when reading, only data needs to be modified:

#include <stddef.h>

struct Reader {
  const char* restrict data;
  const char* restrict end;  // Not sure if this should be restrict or not.
};

char read_char(struct Reader* reader) {
  return *reader->data++;
}

int main(int argc, char* argv[]) {
  struct Reader reader = {
    .data = argv[1],
    .end = argv[1] + (argv[1] ? strlen(argv[1]) : 0),
  };

  if (reader.end - reader.data > 0) {  // Is this okay?
    return read_char(&reader);
  }
  return 0;
}

Is this permissible, given restrict's restrictions on pointers?

Cornstalks
  • 37,137
  • 18
  • 79
  • 144
  • Possible duplicate of [Can you use restrict-ed pointers to access the same object in some cases?](http://stackoverflow.com/questions/18059205/can-you-use-restrict-ed-pointers-to-access-the-same-object-in-some-cases) – LPs Aug 02 '16 at 15:45
  • @LPs: My question is different. I'm not trying to access the same object via aliased pointers. The `end` pointer is never dereferenced, and the only time `data` and `end` alias is when they're equal (at which point dereferencing either would be undefined behavior anyway). My question is about comparing the two pointers, which is different. – Cornstalks Aug 02 '16 at 15:54
  • Into proposed duplicate is shown that as far as you do not modify the pointed object the behavior is defined. Probably I'm missing something... – LPs Aug 02 '16 at 16:41
  • Note `.end = argv[1] + (argv[1] ? strlen(argv[1]) : 0)` is odd. I'd expect `.end = (argv[1] ? argv[1] + strlen(argv[1]) : argv[1])`. Your code begets the question "Is it OK to add 0 to `NULL`" (possible a good SO question). Suggest avoiding it. – chux - Reinstate Monica Aug 02 '16 at 21:25

2 Answers2

5

TL;DR: your usage is permissible by my reading of the standard.

The standard's requirements for use of restrict-qualified pointers all have to do with aliasing and access to the pointed-to object. I find no limitations on using such pointers as operands of the pointer difference operator (-), and such restrictions would not be consistent with the purpose of restrict (which is to provide greater opportunities for optimization).

In contrast, a pointer value obtained from a restrict-qualified pointer by addition of an integer is "based on" the original pointer in a sense that is significant to the standard. Roughly, it is acceptable to access the pointed-to object via such a pointer, whereas it is not acceptable to access that object via a pointer to the same object that is not "based on" the restricted pointer.

Additionally, I don't think Reader.end needs to be restrict-qualified, and I don't think such qualification helps you at all. However, you must ensure that neither Reader.end nor any pointer derived from it is used to access the data; you must use only Reader.data for that. In main(), too. On the other hand, pretty much all that is moot if you nowhere modify the pointed-to object.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Both clang and gcc are prone to behave nonsensically if a `restrict`-qualified pointer is compared to a pointer that is not derived from it. I don't think anything in the Standard was intended to allow such weirdness, but I doubt the authors of clang or gcc will ever allow the definition of `restrict` to be adjusted to forbid it. – supercat Oct 29 '19 at 19:53
0

Nothing in the Standard or rationale suggests any intention to forbid comparisons or computations on restrict-qualified pointers. The way the Standard defines the notion of one pointer being "based upon" another, however, interacts weirdly with comparisons. For example, given something like:

int test(int *restrict p, int *q, int i)
{
  p[i] = 1;
  if (p+1 == q)
    p[1] = 2;
  return p[i];
}

Replacing p with a pointer to a copy of the identified array would prevent the assignment to p[1], making unanswerable the question of whether such replacement would affect the address used in that assignment. While it would seem obvious that p[1] is based upon p, and it would seem absurd that the comparison would affect that, neither clang nor gcc recognize the lvalue p[1] above as being based upon the same p as the lvalue p[i].

supercat
  • 77,689
  • 9
  • 166
  • 211