3

In the following example, the array is not accessed through its first element but by what is conceptually an alias of the array. Nevertheless, accordingly to C++17/[basic.lval]/8 the stored value of an object can be accessed through an unsigned char. So is it right to think that the following assertion will never fire?

void g(){
  unsigned char s[]={'X'};
  unsigned char (*pointer_to_array_s)[1] = &s;
  unsigned char *alias_to_array_s = 
      reinterpret_cast<unsigned char*>(pointer_to_array_s);
  //alias_to_array_s is not a pointer whose value point to c[0] because an array 
  //and its firts element are not pointer interconvertible see [basic.compound]
  //*alias_to_array_s aliases s;
  assert(*alias_to_array_s=='X'); //may fire?
  }

The fact the alias_to_array_s is not a valid pointer to the first element of s is due to a subtlety introduced in C++17, see this Q&A.

Now let's suppose that I modify the array through the alias, can I retrieve this modification by directly accessing the array?

void g(){
  unsigned char s[]={'X'};
  unsigned char (*pointer_to_array_s)[1] = &s;
  unsigned char *alias_to_array_s = 
      reinterpret_cast<unsigned char*>(pointer_to_array_s);
  *alias_to_array_s='Y'; //UB?
  assert(s[0]=='Y');//may fire?
  }
Oliv
  • 17,610
  • 1
  • 29
  • 72
  • That cast is not necessary: `unsigned char *alias_to_array_s = *pointer_to_array_s;`. – Maxim Egorushkin Jan 10 '18 at 11:40
  • @MaximEgorushkin - That's not the point of the question. – StoryTeller - Unslander Monica Jan 10 '18 at 11:48
  • There is small, but essential difference to the question you referred to: You are not incrementing the address, thus never getting to the past the end pointer! – Aconcagua Jan 10 '18 at 11:49
  • An array object is still an object. Accessing an object through a `unsigned char` is always well-defined. The [pointer still points to the array](https://stackoverflow.com/a/48164192/4832499), but accessing through it is fine – Passer By Jan 10 '18 at 11:58
  • @StoryTeller But if your remove the unnecessary cast there is no longer the question. – Maxim Egorushkin Jan 10 '18 at 12:01
  • 1
    @MaximEgorushkin - This is a question about the intricate rules governing the language, not a "how to make it work" question. So, again, not the point. – StoryTeller - Unslander Monica Jan 10 '18 at 12:03
  • @PasserBy, accessing is ok, but what can be asserted about the value? And for exemple I make a store through the object name, then a store through the aliasing pointer and then a load through the object name, is there no risk the compiler optimize away the last load and reflect it by an immediate which would equal the first store? – Oliv Jan 10 '18 at 12:04
  • Since `unsigned char*` `char*` `std::byte*` is allowed to alias anything, the compiler can't optimize that away. The standard doesn't actually say anything about aliasing and optimizing. It only says under what conditions can an object be modified. If the conditions are met, the object is modified and your compiler must do that too – Passer By Jan 10 '18 at 12:10
  • 1
    And because for C-style arrays the pointer to the array points to the first element, modifying the first byte of the object representation through the types @PasserBy listed is guaranteed to modify the first byte of the first element of the array. – patatahooligan Jan 10 '18 at 12:12
  • Possible related discussion: [What is the semantics for exceptions for strict aliasing rules?](https://groups.google.com/a/isocpp.org/forum/?fromgroups#!topic/std-discussion/hp78s03Dxe4) – xskxzr Jan 10 '18 at 14:25

2 Answers2

3

accessing is ok, but what can be asserted about the value? And for exemple I make a store through the object name, then a store through the aliasing pointer and then a load through the object name, is there no risk the compiler optimize away the last load and reflect it by an immediate which would equal the first store?

Access is defined in [defns.access] to mean:

read or modify the value of an object

So modifying the value via *alias_to_array_s='Y'; is just as acceptable as reading it.

The compiler is allowed to optimize load/stores via the as-if rule. Your program doesn't have any observable behavior. If the assert passes, the compiler is free to replace g() with an empty body and not call it at all. If you are really worried about the compiler reordering the load/stores, you should be using volatile or look into memory barriers.

user167921
  • 330
  • 1
  • 7
1

According to these criteria, arrays are objects and objects can be inspected through unsigned char pointers and references. Let's break down your code.

Firstly, we declare an array of unsigned char of size 1.

unsigned char s[]={'X'};

We create a pointer to unsigned char[1]. This is the same type as s so we're fine.

unsigned char (*pointer_to_array_s)[1] = &s;

Now this is the tricky part. Your comment implies that the next conversion would make alias_to_array_s point to the first member of s, which is potentially UB. However, s is an object and its pointer can be reinterpret_cast to char, unsigned char or std::byte in order to inspect its representation. Therefore the next line is well defined as creating a pointer to the first byte of s's representation.

unsigned char *alias_to_array_s = 
    reinterpret_cast<unsigned char*>(pointer_to_array_s);

Your other example of modifying s through alias_to_array_s should also be fine, because you're modifying the first byte of the object's representation. In the case of unsigned char[] that is the first element. The important point here is that you did not convert a pointer-to-array to a pointer-to-first-element. You cast a pointer-to-array to unsigned char * to inspect its representation.

patatahooligan
  • 3,111
  • 1
  • 18
  • 27