简体   繁体   English

将指向数组的指针转换为指针

[英]casting pointer to array into pointer

Consider the following C code: 考虑以下C代码:

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

Now, it is clear that the code prints 5 on common compilers. 现在,很明显代码在常见编译器上打印5 However, can somebody find the relevant sections in the C standard that specifies that the code does in fact work? 但是,有人可以找到C标准中的相关部分,指出代码实际上有效吗? Or is the code undefined behaviour? 或者是代码未定义的行为?

What I'm essentially asking is why &arr when casted into void * is the same as arr when casted into void * ? 什么,我基本上是问的是,为什么&arr时铸造成void *是一样的arr时铸造成void * Because I believe the code is equivalent to: 因为我相信代码相当于:

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

I invented the example while thinking about the question here: Pointer-to-array overlapping end of array ...but this is clearly a distinct question. 我在这里思考这个问题时发明了这个例子: 数组的指针到数组的重叠 ......但这显然是一个独特的问题。

For unions and structures, cf. 对于工会和结构,参见 ISO 9899:2011§6.7.2.1/16f: ISO 9899:2011§6.7.2.1/ 16f:

16 The size of a union is sufficient to contain the largest of its members. 16联盟的大小足以容纳其中最大的成员。 The value of at most one of the members can be stored in a union object at any time. 最多一个成员的值可以随时存储在union对象中。 A pointer to a union object, suitably converted, points to each of its members (or if a member is a bit-field, then to the unit in which it resides), and vice versa. 指向适当转换的union对象的指针指向其每个成员(或者如果成员是位字段,则指向它所在的单位),反之亦然。

17 Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase in the order in which they are declared. 17在结构对象中,非位字段成员和位字段所在的单元具有按声明顺序增加的地址。 A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. 指向适当转换的结构对象的指针指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。 There may be unnamed padding within a structure object, but not at its beginning. 结构对象中可能存在未命名的填充,但不是在其开头。

For array types, the situation is a bit more complex. 对于数组类型,情况稍微复杂一些。 First, observe what an array is, from ISO 9899:2011§6.2.5/20: 首先,观察一下数组是什么,来自ISO 9899:2011§6.2.5/ 20:

An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type . 数组类型描述了具有特定成员对象类型的连续分配的非空对象集,称为元素类型 The element type shall be complete whenever the array type is specified. 只要指定了数组类型,元素类型就应该是完整的。 Array types are characterized by their element type and by the number of elements in the array. 数组类型的特征在于它们的元素类型和数组中的元素数。 An array type is said to be derived from its element type, and if its element type is T , the array type is sometimes called “array of T ”. 数组类型据说是从其元素类型派生的,如果它的元素类型是T ,则数组类型有时称为“ T的数组”。 The construction of an array type from an element type is called “array type derivation”. 从元素类型构造数组类型称为“数组类型派生”。

The wording “contiguously allocated” implies that there is no padding between array members. “连续分配”的措辞意味着阵列成员之间没有填充。 This notion is affirmed by footnote 109: 脚注109肯定了这一概念:

Two objects may be adjacent in memory because they are adjacent elements of a larger array or adjacent members of a structure with no padding between them, or because the implementation chose to place them so, even though they are unrelated. 两个对象可能在内存中相邻,因为它们是较大数组的相邻元素或结构的相邻成员,它们之间没有填充,或者因为实现选择放置它们,即使它们是不相关的。 If prior invalid pointer operations (such as accesses outside array bounds) produced undefined behavior, subsequent comparisons also produce undefined behavior. 如果先前的无效指针操作(例如数组边界外的访问)产生了未定义的行为,则后续比较也会产生未定义的行为。

The use of the sizeof operator in §6.5.3.5, Example 2 expresses the intent that there is also no padding before or after arrays: 在示例2的第6.5.3.5节中使用sizeof运算符表示在数组之前或之后也没有填充的意图:

EXAMPLE 2 例2

Another use of the sizeof operator is to compute the number of elements in an array: sizeof运算符的另一个用途是计算数组中元素的数量:

 sizeof array / sizeof array[0] 

I therefore conclude that a pointer to an array, converted to a pointer to the element typo of that array, points to the first element in the array. 因此,我得出结论,指向数组的指针,转换为指向该数组的元素拼写的指针,指向数组中的第一个元素。 Furthermore, observe what the definition of equality says about pointers (§6.5.9/6f.): 此外,观察关于指针的相等定义(§6.5.9/ 6f。):

6 Two pointers compare equal if and only if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, both are pointers to one past the last element of the same array object, or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space. 6两个指针比较相等,当且仅当两个都是空指针时,两者都是指向同一对象的指针(包括指向对象的指针和在其开头的子对象)或函数,两者都是指向同一对象的最后一个元素之一的指针数组对象,或者一个是指向一个数组对象末尾的指针,另一个是指向不同数组对象的开头的指针,该数组对象恰好跟随地址空间中的第一个数组对象。 109) 109)

7 For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type. 7出于这些运算符的目的,指向不是数组元素的对象的指针与指向长度为1的数组的第一个元素的指针的行为相同,其中对象的类型为其元素类型。

Since the first element of an array is “a subobject at its beginning,” a pointer to the first element of an array and a pointer to an array compare equal. 由于数组的第一个元素是“开头的子对象”,因此指向数组第一个元素的指针和指向数组的指针相等。

Here is a slightly refactored version of your code for easier reference: 这是一个稍微重构的代码版本,以便于参考:

int arr[2] = { 0, 0 };
int *p1 = &arr[0];
int *p2 = (int *)&arr;

with the question being: Is p1 == p2 true, or unspecified, or UB? 问题是: p1 == p2真还是未指定,还是UB?


Firstly: I think that it is intended by the authors of C's abstract memory model that p1 == p2 is true; 首先:我认为C的抽象记忆模型的作者认为p1 == p2是真的; and if the Standard doesn't actually spell it out then it would be a defect in the Standard. 如果标准实际上并没有将其拼写出来,那么它将成为标准中的缺陷。

Moving on; 继续; the only relevant piece of text seems to be C11 6.3.2.3/7 (irrelevant text excised): 唯一相关的文本似乎是C11 6.3.2.3/7(不相关的文本被删除):

A pointer to an object type may be converted to a pointer to a different object type. 指向对象类型的指针可以转换为指向不同对象类型的指针。 [...] When converted back again, the result shall compare equal to the original pointer. [...]再次转换回来时,结果将与原始指针进行比较。

When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. 当指向对象的指针转换为指向字符类型的指针时,结果指向对象的最低寻址字节。 Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object. 结果的连续增量(直到对象的大小)产生指向对象的剩余字节的指针。

It doesn't specifically say what the result of the first conversion is. 它没有具体说明第一次转换的结果是什么。 Ideally it should say ...and the pointer points to the same address , but it doesn't. 理想情况下应该说......并且指针指向同一个地址 ,但事实并非如此。

However, I argue that it it is implied that the pointer must point to the same address after the conversion. 但是,我认为它暗示指针必须在转换后指向相同的地址。 Here is an illustrative example: 这是一个说明性的例子:

void *v1 = malloc( sizeof(int) );
int  *i1 = (int *)v1;

If we do not accept " and the pointer points to the same address " then i1 might not actually point into the malloc 'd space, which would be ridiculous. 如果我们不接受“ 并且指针指向同一个地址 ”那么i1可能实际上并没有指向malloc的空间,这将是荒谬的。

My conclusion is that we should read 6.3.2.3/7 as saying that the pointer cast does not change the address being pointed to. 我的结论是我们应该阅读6.3.2.3/7,因为指针强制转换不会改变指向的地址。 The part about using pointers to character type seems to back this up. 关于使用指向字符类型的指针的部分似乎支持这一点。

Therefore, since p1 and p2 have the same type and point to the same address, they compare equal. 因此,由于p1p2具有相同的类型并指向相同的地址,因此它们相等。

To answer directly: 直接回答:

Can somebody find the relevant sections in the C standard that specifies that the code does in fact work? 有人可以找到C标准中的相关部分,指出代码实际上有效吗?

  • 6.3.2.1 Lvalues, arrays, and function designators, paragraph 1 6.3.2.1左值,数组和函数指示符,第1段
  • 6.3.2.3 Pointers, paragraphs 1,5 and 6 6.3.2.3指针,第1,5和6段
  • 6.5.3.2 Address and indirection operators, paragraph 3 6.5.3.2地址和间接运营商,第3段

Or is the code undefined behaviour? 或者是代码未定义的行为?

The code you posted is not undefined, but it "might" be compiler/implementation specific (per section 6.3.2.3 p5/6) 发布的代码未定义,但它“可能”是编译器/实现特定的(根据第6.3.2.3节第5/6页)

What I'm essentially asking is why &arr when casted into void * is the same as arr when casted into void * ? 什么,我基本上是问的是,为什么&arr时铸造成void *是一样的arr时铸造成void *

This would imply asking why int *ptr = (int*)(void*)&arr gives the same results as int *ptr = (int*)(void*)arr; 这意味着要求为什么int *ptr = (int*)(void*)&arr给出与int *ptr = (int*)(void*)arr;相同的结果int *ptr = (int*)(void*)arr; , but per your code posted, you're actually asking why int *ptr = (int*)(void*)&arr gives the same as int *ptr = (int*)&arr . ,但根据您发布的代码,您实际上在询问为什么int *ptr = (int*)(void*)&arrint *ptr = (int*)&arr

Either way I'll expand on what your code is actually doing to help clarify: 无论哪种方式,我都会扩展您的代码实际执行的操作以帮助澄清:

Per 6.3.2.1p3: 按6.3.2.1p3:

Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ''array of type'' is converted to an expression with type ''pointer to type'' that points to the initial element of the array object and is not an lvalue. 除非它是sizeof运算符,_Alignof运算符或一元&运算符的操作数,或者是用于初始化数组的字符串文字,否则将具有类型''数组类型''的表达式转换为表达式输入''指向类型'的指针,指向数组对象的初始元素,而不是左值。 If the array object has register storage class, the behavior is undefined . 如果数组对象具有寄存器存储类,则行为未定义

and per 6.5.3.2p3: 和6.5.3.2p3:

The unary & operator yields the address of its operand. 一元&运算符产生其操作数的地址。 If the operand has type ''type'', the result has type ''pointer to type''. 如果操作数具有类型''type'',则结果具有类型''指向类型''的指针。

So in your first declaration 所以在你的第一次宣言中

int arr[2] = {0, 0};

arr is initialized to an array type containing 2 elements of type int both equal to 0. Then per 6.3.2.1p3 it is "decayed" into a pointer type pointing to the first element anywhere it is called in scope ( except when it's used like sizeof(arr) , &arr , ++arr or --arr ). arr被初始化为一个数组类型,其中包含两个int类型的元素,它们都等于0.然后,根据6.3.2.1p3它被“衰减”为指向第一个元素的指针类型,它在范围内被调用( 除非它被使用时) sizeof(arr)&arr++arr--arr )。

So in your next line, you could simply just do the following: 所以在下一行中,您可以简单地执行以下操作:

int *ptr = arr; or int *ptr = &*arr; int *ptr = &*arr; or int *ptr = &arr[0]; 或者int *ptr = &arr[0];

and ptr is now a pointer to an int type that points to the first element of the array arr (ie &arr[0] ). ptr现在是一个指向int类型的指针,指向数组arr的第一个元素(即&arr[0] )。

Instead you declare it as such: 相反,你声明它是这样的:

int *ptr = (int*)&arr;

Lets break this down into it's parts: 让我们把它分解成它的部分:

  1. &arr -> triggers the exception to 6.3.2.1p3 so instead of getting &arr[0] , you get the address to arr which is an int(*)[2] type (not an int* type), so you are not getting a pointer to an int , you are getting a pointer to an int array &arr - >触发例外6.3.2.1p3所以,而不是领&arr[0]将得到的地址arr这是一个int(*)[2]式(不是int*类型),所以你没有得到一个pointer to an intpointer to an int ,你得到一个pointer to an int arraypointer to an int array

  2. (int*)&arr , (ie the cast to int* ) -> per 6.5.3.2p3, &arr takes the address of the variable arr returning a pointer to the type of it, so simply saying int* ptr = &arr will give a warning of "incompatible pointer types" (since ptr is of type int* and &arr is of type int(*)[2] ) which is why you need to cast to an int* . (int*)&arr ,(即转换为int* ) - >每6.5.3.2p3, &arr获取变量arr的地址返回指向它的类型的指针,所以简单地说int* ptr = &arr将给出一个“不兼容的指针类型”的警告(因为ptr的类型为int*&arr的类型为int(*)[2] ),这就是你需要转换为int*

Further per 6.3.2.3p1: "a pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer" . 进一步根据6.3.2.3p1: “指向void的指针可以转换为指向任何对象类型的指针。指向任何对象类型的指针可以转换为指向void的指针,然后再返回;结果应该等于原始指针“

So, you're declaration of int* ptr = (int*)(void*)&arr; 所以,你要声明int* ptr = (int*)(void*)&arr; would produce the same results as int* ptr = (int*)&arr; 会产生与int* ptr = (int*)&arr;相同的结果int* ptr = (int*)&arr; because of the types you are using and converting to/from. 因为您正在使用的类型和转换为/来自。 Also as a note: ptr[0] = 5; 另外作为注释: ptr[0] = 5; is the same as *ptr = 5 , where ptr[1] = 5; *ptr = 5相同,其中ptr[1] = 5; would also be the same as *++ptr = 5; 也可以与*++ptr = 5;

Some of the references: 一些参考文献:

6.3.2.1 Lvalues, arrays, and function designators 6.3.2.1左值,数组和函数指示符

1. An lvalue is an expression (with an object type other than void) that potentially designates an object (*see note); 1.左值是一个表达式(对象类型不是void)可能指定一个对象(*参见注释); if an lvalue does not designate an object when it is evaluated, the behavior is undefined. 如果左值在评估时未指定对象,则行为未定义。 When an object is said to have a particular type, the type is specified by the lvalue used to designate the object. 当一个对象被称为具有特定类型时,该类型由用于指定该对象的左值指定。 A modifiable lvalue is an lvalue that does not have 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 member or element of all contained aggregates or unions) with a constqualified type. 可修改的左值是一个左值,它没有数组类型,没有不完整的类型,没有constqualified类型,如果是结构或联合,则没有任何成员(包括,递归地,任何成员或元素)所有包含的聚合或联合)具有constqualified类型。

*The name ''lvalue'' comes originally from the assignment expression E1 = E2, in which the left operand E1 is required to be a (modifiable) lvalue. *名称''左值'最初来自赋值表达式E1 = E2,其中左操作数E1需要是(可修改的)左值。 It is perhaps better considered as representing an object ''locator value''. 它可能更好地被视为表示对象的“定位器值”。 What is sometimes called ''rvalue'' is in this International Standard described as the ''value of an expression''. 有时被称为''rvalue'的东西在本国际标准中被描述为''表达的价值''。 An obvious example of an lvalue is an identifier of an object. 左值的一个明显示例是对象的标识符。 As a further example, if E is a unary expression that is a pointer to an object, *E is an lvalue that designates the object to which E points. 作为另一示例,如果E是作为指向对象的指针的一元表达式,则* E是指定E指向的对象的左值。

2. Except when it is the operand of the sizeof operator, the _Alignof operator, the unary & operator, the ++ operator, the -- operator, or the left operand of the . 2.除非它是sizeof运算符的操作数,_Alignof运算符,一元&运算符,++运算符, - 运算符或者左运算符。 operator or an assignment operator, an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue); 运算符或赋值运算符,没有数组类型的左值被转换为存储在指定对象中的值(并且不再是左值); this is called lvalue conversion. 这称为左值转换。 If the lvalue has qualified type, the value has the unqualified version of the type of the lvalue; 如果左值具有限定类型,则该值具有左值类型的非限定版本; additionally, if the lvalue has atomic type, the value has the non-atomic version of the type of the lvalue; 另外,如果左值具有原子类型,则该值具有左值类型的非原子版本; otherwise, the value has the type of the lvalue. 否则,该值具有左值的类型。 If the lvalue has an incomplete type and does not have array type, the behavior is undefined. 如果左值具有不完整类型且没有数组类型,则行为未定义。 If the lvalue designates an object of automatic storage duration that could have been declared with the register storage class (never had its address taken), and that object is uninitialized (not declared with an initializer and no assignment to it has been performed prior to use), the behavior is undefined. 如果左值指定了一个自动存储持续时间的对象,该对象可以使用寄存器存储类声明(从未使用其地址),并且该对象未初始化(未使用初始化程序声明,并且在使用之前未对其进行任何赋值) ),行为未定义。

3. Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ''array of type'' is converted to an expression with type ''pointer to type'' that points to the initial element of the array object and is not an lvalue. 3.除非它是sizeof运算符,_Alignof运算符或一元&运算符的操作数,或者是用于初始化数组的字符串文字,否则将类型为''array of type''的表达式转换为表达式类型为''指向类型'的指针,指向数组对象的初始元素,而不是左值。 If the array object has register storage class, the behavior is undefined. 如果数组对象具有寄存器存储类,则行为未定义。

6.3.2.3 Pointers 6.3.2.3指针

1. A pointer to void may be converted to or from a pointer to any object type. 1.指向void的指针可以转换为指向任何对象类型的指针。 A pointer to any object type may be converted to a pointer to void and back again; 指向任何对象类型的指针可以转换为指向void的指针,然后再返回; the result shall compare equal to the original pointer. 结果应该等于原始指针。

5. An integer may be converted to any pointer type. 5.整数可以转换为任何指针类型。 Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation (the mapping functions for converting a pointer to an integer or an integer to a pointer are intended to be consistent with the addressing structure of the execution environment). 除了先前指定的,结果是实现定义的,可能未正确对齐,可能不指向引用类型的实体,并且可能是陷阱表示(用于将指针转换为整数或整数的映射函数)指针旨在与执行环境的寻址结构一致。

6. Any pointer type may be converted to an integer type. 6.任何指针类型都可以转换为整数类型。 Except as previously specified, the result is implementation-defined. 除了之前指定的以外,结果是实现定义的。 If the result cannot be represented in the integer type, the behavior is undefined. 如果结果无法以整数类型表示,则行为未定义。 The result need not be in the range of values of any integer type. 结果不必在任何整数类型的值范围内。

6.5.3.2 Address and indirection operators 6.5.3.2地址和间接运营商

1. The operand of the unary & operator shall be either a function designator, the result of a [] or unary * operator, or an lvalue that designates an object that is not a bit-field and is not declared with the register storage-class specifier. 1.一元&运算符的操作数应该是函数指示符,[]或一元*运算符的结果,或者是一个左值,它指定一个不是位字段的对象,并且不用寄存器存储器声明 - 类说明符。

3. The unary & operator yields the address of its operand. 3.一元&运算符产生其操作数的地址。 If the operand has type ''type'', the result has type ''pointer to type''. 如果操作数具有类型''type'',则结果具有类型''指向类型''的指针。 If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue. 如果操作数是一元*运算符的结果,则不会对该运算符和&运算符进行求值,结果就好像两者都被省略,除了对运算符的约束仍然适用且结果不是左值。 Similarly, if the operand is the result of a [] operator, neither the & operator nor the unary * that is implied by the [] is evaluated and the result is as if the & operator were removed and the [] operator were changed to a + operator. 类似地,如果操作数是[]运算符的结果,则[]运算符和[]所暗示的一元*都不会被计算,结果就像删除了&运算符并且[]运算符被更改为a +运算符。 Otherwise, the result is a pointer to the object or function designated by its operand. 否则,结果是指向由其操作数指定的对象或函数的指针。

4. The unary * operator denotes indirection. 4.一元*运算符表示间接。 If the operand points to a function, the result is a function designator; 如果操作数指向函数,则结果是函数指示符; if it points to an object, the result is an lvalue designating the object. 如果它指向一个对象,则结果是指定该对象的左值。 If the operand has type ''pointer to type'', the result has type ''type''. 如果操作数的类型为''指向类型'',则结果的类型为''type''。 If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined (*see note). 如果为指针指定了无效值,则unary *运算符的行为未定义(*请参阅注释)。

*Thus, &*E is equivalent to E (even if E is a null pointer), and &(E1[E2]) to ((E1)+(E2)). *因此,&* E等效于E(即使E是空指针),&(E1 [E2])等于((E1)+(E2))。 It is always true that if E is a function designator or an lvalue that is a valid operand of the unary & operator, *&E is a function designator or an lvalue equal to E. If *P is an lvalue and T is the name of an object pointer type, *(T)P is an lvalue that has a type compatible with that to which T points. 如果E是函数指示符或左值是一元&运算符的有效操作数,则总是如此,*&E是函数指示符或等于E的左值。如果* P是左值,则T是名称对象指针类型,*(T)P是左值,其类型与T指向的类型兼容。 Among the invalid values for dereferencing a pointer by the unary * operator are a null pointer, an address inappropriately aligned for the type of object pointed to, and the address of an object after the end of its lifetime. 在由一元*运算符解除引用指针的无效值中,有一个空指针,一个与指向的对象类型不适当对齐的地址,以及一个对象在其生命周期结束后的地址。

6.5.4 Cast operators 6.5.4施法者

5. Preceding an expression by a parenthesized type name converts the value of the expression to the named type. 5.通过带括号的类型名称在表达式之前将表达式的值转换为命名类型。 This construction is called a cast (a cast does not yield an lvalue; thus, a cast to a qualified type has the same effect as a cast to the unqualified version of the type). 这种结构称为强制转换(强制转换不会产生左值;因此,对合格类型的强制转换与对该类型的非限定版本的强制转换具有相同的效果)。 A cast that specifies no conversion has no effect on the type or value of an expression. 指定不进行转换的强制转换对表达式的类型或值没有影响。

6. If the value of the expression is represented with greater range or precision than required by the type named by the cast (6.3.1.8), then the cast specifies a conversion even if the type of the expression is the same as the named type and removes any extra range and precision. 6.如果表达式的值表示的范围或精度大于强制转换(6.3.1.8)指定的类型所需的范围或精度,则即使表达式的类型与命名类型相同,强制转换也会指定转换并删除任何额外的范围和精度。

6.5.16.1 Simple assignment 6.5.16.1简单分配

2. In simple assignment (=), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand. 2.在简单赋值(=)中,右操作数的值被转换为赋值表达式的类型,并替换存储在左操作数指定的对象中的值。

6.7.6.2 Array declarators 6.7.6.2数组声明符

1. In addition to optional type qualifiers and the keyword static, the [ and ] may delimit an expression or *. 1.除了可选的类型限定符和关键字static之外,[和]可以分隔表达式或*。 If they delimit an expression (which specifies the size of an array), the expression shall have an integer type. 如果它们分隔表达式(指定数组的大小),则表达式应具有整数类型。 If the expression is a constant expression, it shall have a value greater than zero. 如果表达式是常量表达式,则其值应大于零。 The element type shall not be an incomplete or function type. 元素类型不应是不完整或函数类型。 The optional type qualifiers and the keyword static shall appear only in a declaration of a function parameter with an array type, and then only in the outermost array type derivation. 可选的类型限定符和关键字static只出现在具有数组类型的函数参数的声明中,然后仅出现在最外层的数组类型派生中。

3. If, in the declaration ''T D1'', D1 has one of the forms: 3.如果在声明''T D1''中,D1有以下形式之一:

D[ type-qualifier-listopt assignment-expressionopt ] D [type-qualifier-listopt assignment-expressionopt]
D[ static type-qualifier-listopt assignment-expression ] D [static type-qualifier-listopt assignment-expression]
D[ type-qualifier-list static assignment-expression ] D [type-qualifier-list static assignment-expression]
D[ type-qualifier-listopt * ] D [type-qualifier-listopt *]


and the type specified for ident in the declaration ''T D'' is ''derived-declarator-type-list T'', then the type specified for ident is ''derived-declarator-type-list array of T''.142) (See 6.7.6.3 for the meaning of the optional type qualifiers and the keyword static.) 并且在声明''T D''中为ident指定的类型是''derived-declarator-type-list T'',那么为ident指定的类型是''derived-declarator-type-list array of T'' .142)(有关可选类型限定符和关键字static的含义,请参见6.7.6.3。)

4. If the size is not present, the array type is an incomplete type. 4.如果大小不存在,则数组类型为不完整类型。 If the size is * instead of being an expression, the array type is a variable length array type of unspecified size, which can only be used in declarations or type names with function prototype scope;143) such arrays are nonetheless complete types. 如果大小是*而不是表达式,则数组类型是未指定大小的可变长度数组类型,它只能用于具有函数原型范围的声明或类型名称; 143)此类数组仍然是完整类型。 If the size is an integer constant expression and the element type has a known constant size, the array type is not a variable length array type; 如果size是一个整型常量表达式,并且元素类型具有已知的常量大小,则数组类型不是可变长度数组类型; otherwise, the array type is a variable length array type. 否则,数组类型是可变长度数组类型。 (Variable length arrays are a conditional feature that implementations need not support; see 6.10.8.3.) (可变长度数组是实现不需要支持的条件特性;请参阅6.10.8.3。)

5. If the size is an expression that is not an integer constant expression: if it occurs in a declaration at function prototype scope, it is treated as if it were replaced by *; 5.如果size是一个不是整数常量表达式的表达式:如果它出现在函数原型范围的声明中,则将其视为*被替换为*; otherwise, each time it is evaluated it shall have a value greater than zero. 否则,每次评估它时,其值应大于零。 The size of each instance of a variable length array type does not change during its lifetime. 可变长度数组类型的每个实例的大小在其生命周期中不会改变。 Where a size expression is part of the operand of a sizeof operator and changing the value of the size expression would not affect the result of the operator, it is unspecified whether or not the size expression is evaluated. 如果size表达式是sizeof运算符的操作数的一部分,并且更改size表达式的值不会影响运算符的结果,则无法指定是否计算size表达式。

6. For two array types to be compatible, both shall have compatible element types, and if both size specifiers are present, and are integer constant expressions, then both size specifiers shall have the same constant value. 6.对于要兼容的两个数组类型,两者都应具有兼容的元素类型,如果两个大小说明符都存在,并且是整数常量表达式,则两个大小说明符应具有相同的常量值。 If the two array types are used in a context which requires them to be compatible, it is undefined behavior if the two size specifiers evaluate to unequal values. 如果在要求它们兼容的上下文中使用这两种数组类型,则如果两个大小说明符计算为不相等的值,则它是未定义的行为。

PS As a side note, given the following code: 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;
}

where X was one of the following: 其中X是以下之一:

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

and Y was one of the following: 和Y是以下之一:

ptr[0] = 15;
*ptr = 15;

When compiled on OpenBSD with gcc version 4.2.1 20070719 and providing the -S flag, the assembler output for all files was exactly the same. 当在具有gcc版本4.2.1 20070719的OpenBSD上编译并提供-S标志时,所有文件的汇编器输出完全相同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM