3

In Find size of array without using sizeof, the size of an array is computed via

int arr[100];
printf ("%td", (&arr)[1] - arr);

Now, for the purposes of pointer arithmetic, arr is considered the element of a single-element array, so

&arr + 1

is a pointer one-past the end of that (conceptual) single-element array, so when the address of arr[0] is subtracted, the number of elements in arr is obtained.

In (&arr)[1], that pointer is the operand of the indirection operator,

(&arr)[1] ≡ *(&arr + 1)

and the resulting array expression is then converted to an int* as per 6.3.2.1 (3).

So far, so good. But the last sentence in 6.5.6 (8) (Additive operators),

If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated.

forbids the evaluation of the indirection operator there.

The question is whether the indirection operator is evaluated in

*(&arr + 1) - arr

(in which case that expression would invoke undefined behaviour) or the array-to-pointer conversion annihilates the evaluation (in which case all is well), like taking the address (&(*(&arr + 1))) or applying sizeof to it would..

Community
  • 1
  • 1
Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431
  • I know you are asking about C, but FWIW, in C++, the idea (not yet properly expressed in the standard) is that it is the lvalue-to-rvalue conversion for which the behaviour is undefined. Since that doesn't take place, this code would be valid. –  Apr 15 '13 at 17:51
  • `*(&arr + 1)` is undefined behavior, as is `(&arr)[1]`. I'm not sure I understand your question. – ouah Apr 15 '13 at 17:56
  • @ouah But taking the address or applying `sizeof` would cause `*` to be not evaluated, then there is no problem. The question is whether the array-to-pointer conversion has the same effect. – Daniel Fischer Apr 15 '13 at 18:00

2 Answers2

4

I think the standard is pretty clear that the * operator is evaluated here. The result of the evaluation is an lvalue which is never converted to a value, but the result of the evaluation is used nonetheless.

There is a special exception when the address is immediately taken using the & operator: in &*x, where x has type T *, x is evaluated, but the * and & operators are not. There is no intermediate lvalue of type T at runtime of which the address is taken. So &*x is valid even when x is a null pointer or one past the end of an array.

Since you are not using the & operator to take the address, that exception does not apply. There is no similar exception for the array-to-pointer conversion, so even when x is an array type, *x is invalid if x does not actually point to any object.

  • I would tend to think so too, but as the comments under [Alexey Frunze's answer](http://stackoverflow.com/a/16019181/1011995) show, we ain't so sure about the interpretation of the standard. Does the array-to-pointer conversion require the evaluation of the expression of array type? – Daniel Fischer Apr 15 '13 at 18:07
  • @DanielFischer As far as I can tell, in those comments, there are no arguments to support the idea that the standard currently allows it, the only doubt is whether the standard *should* allow it (whether this is a defect in the standard). Did I miss anything? FWIW, I would be extremely surprised, regardless of what the standard says, if it breaks on any current or future implementation of C. –  Apr 15 '13 at 18:10
  • Upon reexamining the thread, I don't think you missed anything. (And I think everybody would be very surprised if it didn't work as intended anywhere in practice.) – Daniel Fischer Apr 15 '13 at 18:22
  • Could you please reword or extend the answer somehow? I've read it multiple times and I'm still unsure if it means there should be UB or it means there should not. – Alexey Frunze Apr 15 '13 at 23:36
  • @AlexeyFrunze Based on what the standard currently says, the behaviour is undefined. Does my edit help clear it up? –  Apr 16 '13 at 05:53
0

You want to do this: For any T x;, it is true that &x + 1 is the address just past the end of x, and numerically it is a pointer that is obtained by incrementing &x by sizeof(T) (so the sizeof is still there!).

Now if T = U[N], then x is an array of N elements of type U. But the pointer to such an array is also a pointer to the first element of such an array. So we can reinterpret &x (which is a (T*)[N]) to a U*, and we can do the same with &x + 1. But now those pointers are of type U*, so their difference counts the number of elemenets.

So the array size is:

(U*)(&x + 1) - (U*)(&x)

Note that because of array-to-pointer decay, (U*)(&x) is actually the same pointer as the decay of x.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084