5

Inspired by my comment on the recent post: C get element on place without parenthesis , I would like to know if the following code breaks the strict aliasing rule:

#include <stdio.h>

int main(void)
{
    int num[3] = { 1, 2, 3 };

    printf("num[1] = %d\n", *(int *)((char *)num + sizeof(int)));

    return 0;
}

I know that de-referencing a pointer that is type-cast to a different type other than char is a violation, but here, the original pointer is of type int *. It is de-referenced after it is cast to char * then to int *.

Does this violate the strict aliasing rule?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
machine_1
  • 4,266
  • 2
  • 21
  • 42

2 Answers2

2

Quoting C11, chapter §6.5p7

An object shall have its stored value accessed only by an lvalue expression that has one of the following types:

  1. a type compatible with the effective type of the object

Also

Quoting C11, chapter §6.5p6

The effective type of an object for an access to its stored value is the declared type of the object, if any....

Emphases mine

Here the effective type of the object pointed by num is indeed int and hence it is okay to dereference it with a pointer to int.

Community
  • 1
  • 1
Ajay Brahmakshatriya
  • 8,993
  • 3
  • 26
  • 49
  • @curiousguy I am not sure which pointer and object you are referring to. Could you elaborate a little more on the question? – Ajay Brahmakshatriya Dec 20 '19 at 19:26
  • What is the effect of `+ sizeof(int)`? Can pointers be manipulated that way? – curiousguy Dec 20 '19 at 19:38
  • @curiousguy yes, the definition of arrays guarantees that objects of type integer are spaced with `sizeof(int)` – Ajay Brahmakshatriya Dec 20 '19 at 20:41
  • That they have a given layout is one thing, that you can do arithmetic on pointers as if they were integers is another. And it doesn't seem obvious to me. – curiousguy Dec 20 '19 at 20:48
  • @curiousguy we are not doing arithmetic on them as integers, we are doing arithmetic on `char*` pointers which have well-defined semantics. You can find it here [6.5.6p8](http://port70.net/~nsz/c/c11/n1570.html#6.5.6p8) – Ajay Brahmakshatriya Dec 20 '19 at 20:50
  • I see no explanation of what happens when you add `sizeof(int)` in that text. It actually says that adding 1 (not `sizeof(int)`!) moves to the next element. It doesn't even describe "spacing" of array elements. – curiousguy Dec 20 '19 at 20:59
  • @curiousguy *..if the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i+n-th and i-n-th elements of the array object, provided they exist...* – Ajay Brahmakshatriya Dec 20 '19 at 22:15
  • @curiousguy I didn't cite the layout of arrays because you asked about the pointer arithmetic. – Ajay Brahmakshatriya Dec 20 '19 at 22:16
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/204639/discussion-between-curiousguy-and-ajay-brahmakshatriya). – curiousguy Dec 21 '19 at 04:07
1

This does not break strict aliasing.

After num is casted to a char * and arithmetic performed on it, the resulting pointer is cast to int *, and that pointer points to a valid int object. So it may be dereferenced safely.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • What rule, if any, would exempt from the constraints in N1570 6.5p7 accesses made to array objects using lvalues of their element types? A compiler that couldn't accommodate accesses made to arrays using element types would be pretty useless, but is there anything in the Standard that would actually require compilers to process such accesses usefully? – supercat Dec 18 '19 at 22:11
  • 1
    @supercat objects that are part of an array have declared type as the type of the element of the array. So they would be compatible with an lvalue expression of element type. – Ajay Brahmakshatriya Dec 18 '19 at 22:15
  • 2
    @AjayBrahmakshatriya: Yes, but an object which is within an array is *also* part of an array object, and thus any access to an object within an array is also an access to the array object. This isn't observable with stand-alone array objects, since one can't directly use an lvalue of array type, but is observable with arrays that are contained within structures or unions. Both clang and gcc apply different aliasing assumptions to `aggregate.memberArray[index]` vs `(*((aggregate.memberArray)+(index)))` even though the Standard defines the former as the latter. – supercat Dec 18 '19 at 22:25
  • @supercat that is an interesting point. I always thought of the "access" in 6.5p7 to be lvalue to rvalue conversion not the actual `*` operator. Here aggregate.memberArray is an lvalue of type `int[]`. And it is being immediately being converted to rvalue of type `int[]` (for the arithmetic). Thus the lvalue used to access the object of type array is array. I am not sure though – Ajay Brahmakshatriya Dec 18 '19 at 22:39
  • @AjayBrahmakshatriya: I think the best way to interpret things would be to say that an operation which forms an object's address and the use of the resulting pointer together constitute an access to the parent object, and the rules in 6.5p7 identify the only cases where such accesses may overlap. Such an interpretation would accommodate most constructs that clang and gcc can't handle without `-fno-strict-aliasing`, but the authors of clang and gcc have doubled down on their refusal to process such code and would block any mandate requiring it. – supercat Dec 18 '19 at 22:57
  • @AjayBrahmakshatriya: Such an interpretation would have also avoided the need for the nonsensical rationale given in Defect Report 28 http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_028.html which has resulted in 25 years of needless confusion. – supercat Dec 18 '19 at 23:00