Intersecting end of array with pointer to array

Is this code correct?

int arr[2]; int (*ptr)[2] = (int (*)[2]) &arr[1]; ptr[0][0] = 0; 

Obviously, ptr[0][1] will be unacceptable by going beyond arr .

Note. There is no doubt that ptr[0][0] denotes the same memory cell as arr[1] ; the question is whether we are allowed to access this memory location via ptr . Here are a few more examples where an expression denotes the same place in memory, but it is not allowed to access that place of memory.

Note 2: Also consider **ptr = 0; . As Mark Van Leeuwen noted, ptr[0] equivalent to *(ptr + 0) , however ptr + 0 seems to drop out due to the arithmetic section of the pointer. But using *ptr instead, this can be avoided.

+21
c ++ c arrays language-lawyer
Mar 24 '15 at 21:20
source share
6 answers

Not an answer, but a comment I cannot say well without being a wall of text:

The given arrays are guaranteed to save their contents contiguously so that they can be "repeated" with the help of a pointer. If I can take a pointer to the beginning of the array and increment this pointer sequentially until I get access to each element of the array, then this will undoubtedly make the statement that an array can be accessed with a series of any type of which it consists.

Of course, the combination: 1) Array [x] saves its first element in the array of addresses 'array' 2) Successive increments of the pointer to it are sufficient to access the next element 3) Array [x-1] obeys the same rules

Then it should be legal to at least look at the address of the "array", as if it were an array of types [x-1] instead of an array of types [x].

In addition, given that points should be adjacent and how pointers to elements in an array should behave, of course, it should be legal to then group any continuous subset of array [x] into an array [y], where y <x and the top the border does not exceed the size of the array [x].

Not being a lawyer by language, I just scoff at the trash. I am very interested in the outcome of this discussion.

EDIT:

Upon further consideration of the source code, it seems to me that arrays in themselves are in many ways a special case. They break up into a pointer, and I believe that this could be an alias according to what I just said earlier in this post.

Thus, without any standards, in order to support my humble opinion, an array cannot be truly invalid or “undefined” as a whole, if it really is not perceived as a whole.

What is processed evenly are individual elements. Therefore, I think it makes sense to talk only about whether access to a particular element is real or specific.

+4
Mar 24 '15 at 22:44
source share

For C ++ (I am using the N4296 project) [dcl.array]/7 says, in particular, that if the result of the signature is an array, it is immediately converted to a pointer. That is, in ptr[0][0] ptr[0] first converted to int* , and only the second [0] is applied to it. So this is absolutely correct code.

For C (project C15 of project N1570) 6.5.2.1/3 indicates the same.

+3
Mar 24 '15 at 10:32
source share

Yes, this is the correct code. Quoting N4140 for C ++ 14:

[expr.sub] / 1 ... The expression E1[E2] identical (by definition) to *((E1)+(E2))

[expr.add] / 5 ... If both pointer operands and the result point to elements of the same array object or one after the last element of the array object, the evaluation should not lead to overflow; otherwise, the behavior is undefined.

There is no overflow. &*(*(ptr)) == &ptr[0][0] == &arr[1] .

For C11 (N1570) the rules are the same. §6.5.2.1 and §6.5.6

+3
Mar 24 '15 at 22:46
source share

Let me give a special opinion: this (at least in C ++) is undefined behavior for the same reason as in another question related to this question.

Let me first clarify an example with some typedefs that will simplify the discussion.

 typedef int two_ints[2]; typedef int* int_ptr; typedef two_ints* two_ints_ptr; two_ints arr; two_ints_ptr ptr = (two_ints_ptr) &arr[1]; int_ptr temp = ptr[0]; // the two_ints value ptr[0] gets converted to int_ptr temp[0] = 0; 

So, the question is whether there is an object of type two_ints whose address matches the address arr[1] (in the same sense that the address arr matches the address arr[0] ) and therefore there is no object to which ptr[0] , however, you can convert the value of this expression to one of the int_ptr types (here with the name temp ), which points to an object (namely, an integer object is also called arr[1] ).

The point at which I think the undefined behavior is in the ptr[0] rating, which is equivalent (in 5.2.1 [expr.sub]) to *(ptr+0) ; more precisely, the ptr+0 score has undefined behavior.

I will give my copy of C ++, which is not official [N3337], but probably the language has not changed; I am a little worried that the section number does not at all coincide with the number indicated in the accepted answer of the related question. Anyway, for me it's §5.7 [expr.add]

If both pointer operands and the result point to elements of the same array object or one after the last element of the array object, the evaluation should not lead to overflow; otherwise, the behavior is undefined.

Since the ptr pointer operand has a pointer to two_ints , the "array object" mentioned in the quoted text should be an array of two_ints objects. However, there is only one such object: a dummy array, whose unique element is arr , which we must call in such situations (such as: "a pointer to a nonarray object behaves the same as a pointer to the first element of an array of length one ...") but it is clear that ptr does not indicate its unique element arr . Thus, although ptr and ptr+0 , without a doubt, equal values, none of them even points to elements of any array object (not even fictitious), not a single end to the end of such an array object, and the condition of the specified phrase is not fulfilled. The consequence is (not that overflow occurs, but) that the behavior is undefined.

So, the behavior is already undefined before applying the indirectness operator * . I would not argue about the behavior of undefined from the last assessment, although the phrase "the result is an lvalue referring to the object or function to which the point of expression refers" is difficult to interpret for expressions that do not apply to any object in everything. But I would be gentle in interpreting this, since I believe that dereferencing a pointer past an array should not be undefined behavior (for example, if it is used to initialize a link).

This assumes that if you write (*ptr)[0] or **ptr instead of ptr[0][0] , then the behavior will not be undefined. This is curious, but this is not the first time that the C ++ standard surprises me.

+3
Mar 25 '15 at 9:35
source share

It depends on what you mean by "right." You roll ptr to arr[1] . In C ++, this is likely to be reinterpret_cast . C and C ++ are languages ​​that (in most cases) assume that the programmer knows what he is doing. That this buggy code has nothing to do with the fact that it is a valid C / C ++ code.

You do not break any rules in the standards (as far as I see).

+2
Mar 24 '15 at 22:24
source share

Trying to answer here why the code runs on commonly used compilers:

 int arr[2]; int (*ptr)[2] = (int (*)[2]) &arr[1]; printf("%p\n", (void*)ptr); printf("%p\n", (void*)*ptr); printf("%p\n", (void*)ptr[0]); 

All lines print the same address on commonly used compilers. Thus, ptr is an object for which *ptr represents the same memory location as ptr for commonly used compilers, and therefore ptr[0] really a pointer to arr[1] , and therefore arr[0][0] is arr[1] . Thus, the code assigns the value arr[1] .

Now suppose that there is a vicious implementation where the pointer to the array (NOTE: I say the pointer to the array, that is &arr , which is of type int(*)[] , not arr , which means the same as &arr[0] and has type int* ) - this is a pointer to the second byte inside the array. Then dereferencing ptr same as subtracting 1 from ptr using char* arithmetic. For structures and associations, it is guaranteed that the pointer to such types will be the same as the pointer to the first element of such types, but a guarantee for arrays was not found in the pointer to the pointer to the array in the pointer (i.e. the pointer to the array will be the same, as a pointer to the first element of the array), and actually @FUZxxl planned to record a defect report according to the standard. For such a vicious implementation, *ptr ie ptr[0] will not be the same as &arr[1] . On RISC processors, this will actually cause problems due to data alignment.

Extra fun:

 int arr[2] = {0, 0}; int *ptr = (int*)&arr; ptr[0] = 5; printf("%d\n", arr[0]); 

Should this code work? He prints 5.

Even more fun:

 int arr[2] = {0, 0}; int (*ptr)[3] = (int(*)[3])&arr; ptr[0][0] = 6; printf("%d\n", arr[0]); 

Should this work? He is typing 6.

This should work:

 int arr[2] = {0, 0}; int (*ptr)[2] = &arr; ptr[0][0] = 7; printf("%d\n", arr[0]); 
0
Mar 24 '15 at 22:00
source share



All Articles