To answer directly:
Could someone find the relevant sections in the C standard, which indicates that the code really works?
- 6.3.2.1 Lvalues, arrays and function pointers, paragraph 1
- 6.3.2.3 Indices, paragraphs 1.5 and 6
- 6.5.3.2 Address and Indirection Operators, clause 3
Or is this undefined code behavior?
The code you highlighted >, but it can be a "compiler / implementation" (in section 6.3.2.3 p5 / 6)
I wonder why &arr when entering into void * matches arr when entering into void * ?
This would mean why int *ptr = (int*)(void*)&arr gives the same results as int *ptr = (int*)(void*)arr; but for your submitted code you really ask why int *ptr = (int*)(void*)&arr gives the same thing as int *ptr = (int*)&arr .
In any case, I’ll talk about what your code actually does to help clarify:
Per 6.3.2.1p3:
Unless it is the operand of the sizeof operator, the _Alignof operator or the unary operator, or the string literal used to initialize the array, an expression that has an array type of type '' is converted to an expression of type '' with a pointer to a type that points to to the starting element of an array object and is not an lvalue. If the array object has a register storage class, the behavior is undefined .
and for 6.5.3.2p3:
Unary and operator gives the address of its operand. If the operand is of type type '', the result is of type '' pointer to type.
So in the first ad
int arr[2] = {0, 0};
arr initialized with an array type containing 2 int elements, equal to 0. Then on 6.3.2.1p3 it "decomposes" into a pointer type that points to the first element where it is called in scope ( except when it is used as sizeof(arr) , &arr , ++arr or --arr ).
So, in the next line, you can simply do the following:
int *ptr = arr; or int *ptr = &*arr; or int *ptr = &arr[0];
and ptr now a pointer to an int type that points to the first element of the arr array (ie &arr[0] ).
Instead, you declare it as such:
int *ptr = (int*)&arr;
Lets break it into parts:
&arr → 6.3.2.1p3 exception in 6.3.2.1p3 , so instead of getting &arr[0] you get the address arr , which is of type int(*)[2] (and not int* ), so you don't get pointer to an int , you get pointer to an int array
(int*)&arr (i.e. casting to int* ) → for 6.5.3.2p3, &arr takes the address of the variable arr to return a pointer to its type, so just saying int* ptr = &arr will give a warning about "incompatible pointer types" (since ptr is of type int* and &arr is of type int(*)[2] ), so you need to cast to int* .
Further in 6.3.2.3p1: "a pointer to void can be converted to a pointer to or from a pointer to any type of object. A pointer to any type of object can be converted to a pointer to void and vice versa; the result is compared with the original pointer . "
So you declare int* ptr = (int*)(void*)&arr; to get the same results as int* ptr = (int*)&arr; due to the types you use and convert to / from. Also as a note: ptr[0] = 5; matches *ptr = 5 , where ptr[1] = 5; will also be the same as *++ptr = 5;
Some links:
6.3.2.1 Lvalues, arrays, and function pointers
1. lvalue is an expression (with an object type other than void) that potentially denotes an object (* see Note); if lvalue does not indicate an object when evaluating it, the behavior is undefined. When an object is of a specific type, the type is determined by the value l used to denote the object. The lvalue modifiable value is an lvalue value that does not have an array type, does not have an incomplete type, does not have a constqualified type, and, if it is a structure or union, does not have any member (including, recursively, any element or element of all contained aggregates or unions) with an inconsistent type.
* The name '' lvalue comes originally from the assignment expression E1 = E2, in which the left operand E1 must be (mutable) lvalue. This is perhaps best seen as representing the value of the object's locator. What is sometimes called the "rvalue" is found in this International Standard, described as the "value of an expression". An obvious example of lvalue is the identifier of an object. As another example, if E is a unary expression that is a pointer to an object, * E is the value l, which denotes the object that E. points to.
2. Unless it is an operand of the sizeof operator, the _Alignof operator, the unary operator &, the ++ operator, the operator operator, or the left operand. operator or assignment operator, the value of l, which does not have the type of an array, is converted to the value stored in the specified object (and is no longer an lvalue); this is called an lvalue conversion. If an lvalue is of a qualified type, the value is an unqualified version of the lvalue type; in addition, if the lvalue is of atomic type, the value has a non-atomic version of the lvalue type; otherwise, the value is of type lvalue. If the lvalue is of an incomplete type and does not have an array type, the behavior is undefined. If lvalue denotes an object with an automatic storage duration that could be declared with a register storage class (its address was never accepted), and this object is not initialized (not declared with an initializer, and its assignment was not executed before use), the behavior is undefined.
3. Unless it is an operand of the sizeof operator, the _Alignof operator, or a unary operator or is a string literal used to initialize an array, an expression that is of type '' array of type is converted to an expression with type '' pointer to a type that points to the starting element of an array object and is not an lvalue value. If the array object has a register storage class, the behavior is undefined.
6.3.2.3 Pointers
1. A pointer to void can be converted to or from a pointer to any type of object. A pointer to any type of object can be converted to a pointer to void and vice versa; The result is compared with the original pointer.
5. An integer can be converted to any type of pointer. Except, as indicated earlier, the result is determined by the implementation, may not be aligned correctly, may not point to an object of reference type, and may be a trap representation (the mapping functions for converting a pointer to an integer or integer into a pointer must correspond to the addressing structure of the runtime environment).
6. Any type of pointer can be converted to an integer type. Except as noted above, the result is determined by implementation. If the result cannot be represented in an integer type, the behavior is undefined. The result does not have to be in the range of values of any integer type.
6.5.3.2 Address and Indirection Operators
1. The operand of the unary operator & must be either the function name, the result of the [] operator, or the unary *, or lvalue, which indicates an object that is not a bit field and is not declared using register storage, the class specifier.
3. The unary operator & gives the address of its operand. If the operand is of type type '', the result is of type '' pointer to type. If the operand is the result of a unary * operator, neither this operator nor the & operator is evaluated, and the result is as if both were omitted, except that the restrictions on the operators are still applied, and the result is not a value of l. Similarly, if the operand is the result of the [] operator, neither the & operator nor the unary *, which is implied by [], are evaluated, and the result looks as if the & operator was deleted and the [] operator was changed to a +. Otherwise, the result will be a pointer to the object or function assigned by its operand.
4. The unary operator * denotes an indirect direction. If the operand points to a function, the result will denote a function; if it points to an object, the result is an lvalue representing the object. If the operand is of type `` pointer to type, the result is of type ''. If an invalid value is assigned to the pointer, the behavior of the unary * operator is undefined (* see Note).
* Thus, & * E is equivalent to E (even if E is a null pointer) and & (E1 [E2]) - ((E1) + (E2)). It is always true that if E is a function designation or lvalue, which is a valid operand of the unary operator &, * & E is a function designation or value l equal to E. If * P is the value l, and T is the name of the pointer type * (T ) P is an l-value that is of a type compatible with that indicated by T. Among the invalid values for dereferencing with a pointer, the unary * operator is a null pointer, an address inadequately aligned with the type of object that it points to, and the address of the object after ending his life.
6.5.4 Role Operators
5. The preceding expression, using the type name in brackets, converts the value of the expression into a named type. This construction is called cast (casting does not give an lvalue, so casting to a qualified type has the same effect as casting to an unqualified version of a type). A listing that does not indicate a conversion does not affect the type or value of the expression.
6. If the value of the expression is represented with a larger range or precision than is required by the type called cast (6.3.1.8), then cast indicates the conversion, even if the type of the expression is the same as the named type and removes any additional range and precision.
6.5.16.1 Simple assignment
2. In a simple assignment (=), the value of the correct operand is converted to the type of the assignment expression and replaces the value stored in the object indicated by the left operand.
6.7.6.2 Manifest declaration
1. In addition to the optional type classifiers and the static keyword, [and] can limit the expression or *. If they limit the expression (which sets the size of the array), the expression must be an integer type. If the expression is a constant expression, it must have a value greater than zero. The item type must not be incomplete or functional. Optional classifier types and the static keyword should appear only in the declaration of the function parameter with the array type, and then only in the outermost division of the array type.
3. If in the declaration '' T D1, D1 has one of the forms:
D [type-qualifier-listopt assign-expressionopt]
D [static classifier-type-listopt-expression-expression]
D [static assignment-expression of list-classifier-type]
D [type-qualifier-listopt *]
and the type specified for the identifier in the declaration '' TD is a '' derived-declarator-type-type T, then the type specified for the identifier is `` array-type-list-type-list-array from T.142 ) (See 6.7. 6.3 for the meaning of optional type classifiers and the static keyword.)
4. If there is no size, the type of the array is incomplete. If the size is * instead of an expression, the array type is an array type of variable length of undetermined size, which can only be used in declarations or type names using the function prototype area; 143) such arrays, however, are complete types. If the size is an integer constant expression, and the element type has a known constant size, the array type is not an array of variable length; otherwise, the array type is an array type of variable length. (Arrays of variable length are a conditional function, the implementation of which is not required, see 6.10.8.3.)
5. , : , , *; , , . . sizeof , , .
6. , , , , . , , undefined, .
PS , :
#include <stdio.h> int main(int argc, char** argv) { int arr[2] = {10, 20}; X Y printf("%d,%d\n", arr[0],arr[1]); return 0; }
X :
int *ptr = (int*)(void*)&arr; int *ptr = (int*)&arr; int *ptr = &arr[0];
Y :
ptr[0] = 15; *ptr = 15;
OpenBSD gcc 4.2.1 20070719 -S .