For example, let's consider the static
storage class specifier. Here are a few examples of both valid and ill-formed uses of this storage class specifier:
例如,让我们考虑静态存储类说明符。以下是此存储类说明符的有效和不正确用法的几个示例:
static int a; // valid
int static b; // valid
static int* c; // valid
int static* d; // valid
int* static e; // ill-formed
static int const* f; // valid
int static const* g; // valid
int const static* h; // valid
int const* static i; // ill-formed
typedef int* pointer;
static pointer j; // valid
pointer static k; // valid
(The declarations marked "valid" were accepted by Visual C++ 2012, g++ 4.7.2, and Clang++ 3.1. The declarations marked "ill-formed" were rejected by all of those compilers.)
(标记为“有效”的声明被Visual C ++ 2012,g ++ 4.7.2和Clang ++ 3.1接受。标记为“格式错误”的声明被所有这些编译器拒绝。)
This seems odd because the storage class specifier applies to the declared variable. It is the declared variable that is static
, not the type of the declared variable. Why are e
and i
ill-formed, but k
is well-formed?
这看起来很奇怪,因为存储类说明符适用于声明的变量。它是声明的变量,它是静态的,而不是声明的变量的类型。为什么e和i形成不良,但k形态良好?
What are the rules that govern valid placement of storage class specifiers? While I've used static
in this example, the question applies to all storage class specifiers. Preferably, a complete answer should cite relevant sections of the C++11 language standard and explain them.
有效放置存储类说明符的规则是什么?虽然我在此示例中使用了静态,但该问题适用于所有存储类说明符。优选地,完整的答案应引用C ++ 11语言标准的相关部分并解释它们。
3 个解决方案
#1
16
In summary, anywhere in the declaration specifier (See section 7.1 in the ISO/IEC 14882-2012), ie before the *
. Qualifiers after the *
are associated with the pointer declarator, not the type specifier, and static
doesn't make sense within the context of a pointer declarator.
总之,声明说明符中的任何位置(参见ISO / IEC 14882-2012中的第7.1节),即*之前。 *之后的限定符与指针声明符相关联,而不是类型说明符,静态在指针声明符的上下文中没有意义。
Consider the following cases: You can declare a normal int and a pointer to an int in the same declaration list, like this:
请考虑以下情况:您可以在同一个声明列表中声明一个普通的int和一个指向int的指针,如下所示:
int a, *b;
this is because the type specifier is int
, then you have two declarations using that type specifier int
, a
, and a pointer declarator *a
which declares a pointer to int
. Now consider:
这是因为类型说明符是int,那么你有两个声明使用那个类型说明符int,a和一个指针声明符* a来声明指向int的指针。现在考虑:
int a, static b; // error
int a, *static b; // error
int a, static *b; // error
which should look wrong (as they are), and the reason (as defined in sections 7.1 and 8.1) is because C and C++ require that your storage specifiers go with your type specifier, not in your declarator. So now it should be clear that that the following is also wrong, since the above three are also wrong:
看起来应该是错误的(因为它们),原因(如第7.1和8.1节所定义)是因为C和C ++要求您的存储说明符与您的类型说明符一起使用,而不是在声明符中。所以现在应该清楚的是,以下也是错误的,因为上述三个也是错误的:
int *static a; // error
Your last example,
你最后一个例子,
typedef int* pointer;
static pointer j; // valid
pointer static k; // valid
are both valid and both equivalent because the pointer
type is defined as a type specifier and you can put your type specifier and storage specifeir in any order. Note that they are both equivalent and would be equivalent to saying
两者都是有效的并且都是等效的,因为指针类型被定义为类型说明符,您可以按任何顺序放置类型说明符和存储specifeir。请注意,它们都是等效的,相当于说
static int *j;
static int *k;
or
int static *j;
int static *k;
#2
4
Per 7.1, the [simplified] structure of C++ declaration is
根据7.1,C ++声明的[简化]结构是
decl-specifier-seq init-declarator-list;
Per 7.1/1, storage class specifiers belong in the initial "common" part decl-specifier-seq
.
根据7.1 / 1,存储类说明符属于最初的“公共”部分decl-specifier-seq。
Per 8/1, init-declarator-list
is a sequence of declarators.
根据8/1,init-declarator-list是一系列声明符。
Per 8/4, the *
part of pointer declaration is a part of an individual declarator in that sequence. This immediately means that everything that follows a *
is a part of that individual declarator. This is why some of your storage class specifier placements are invalid. Declarator syntax does not allow inclusion of storage class specifiers.
每8/4,指针声明的*部分是该序列中单个声明符的一部分。这立即意味着*之后的所有内容都是该单个声明符的一部分。这就是您的某些存储类说明符放置无效的原因。声明符语法不允许包含存储类说明符。
The rationale is rather obvious: since storage class specifiers are supposed to apply to all declarators in the whole declaration, they are placed into the "common" part of the declaration.
理由很明显:由于存储类说明符应该适用于整个声明中的所有声明符,因此它们被放置在声明的“公共”部分中。
I'd say that a more interesting (and somewhat related) situation takes place with specifiers that can be present in both decl-specifier-seq
and individual declarators, like const
specifier. For example, in the following declaration
我会说,一个更有趣(并且有些相关)的情况发生在使用decl-specifier-seq和单个声明符中的说明符,比如const说明符。例如,在以下声明中
int const *a, *b;
does const
apply to all declarators or only to the first one? The grammar dictates the former interpretation: that const
applies to all declarators, i.e. it is a part of the decl-specifier-seq
.
const适用于所有声明者还是仅适用于第一个?语法规定了前一种解释:const适用于所有声明符,即它是decl-specifier-seq的一部分。
#3
3
If you employ the "Golden Rule" (which also doesn't apply only to pointers) it follows naturally, intuitively, and it avoids a lot of mistakes and pitfalls when declaring variables in C/C++. The "Golden Rule" should not be violated (there are rare exceptions, like const
applied to array typedefs, which propagates const
to the base type, and references, that came with C++).
如果你使用“黄金规则”(它也不仅仅适用于指针),它自然而直观地遵循,它避免了在C / C ++中声明变量时的许多错误和陷阱。不应该违反“黄金法则”(有很少的例外,例如应用于数组typedef的const,它将const传播到基类型,以及C ++附带的引用)。
K&R, Appendix A, Section 8.4, Meaning of Declarators states:
K&R,附录A,第8.4节,声明者的含义:
Each declarator is taken to be an assertion that when a construction of the same form as the declarator appears in an expression, it yields an object of the indicated type and storage class.
每个声明符都被认为是一个断言,当一个与声明符相同的形式的结构出现在表达式中时,它产生一个指定类型和存储类的对象。
To declare a variable in C/C++ you should really think of the expression you should apply to it to get the base type.
要在C / C ++中声明变量,您应该考虑应该应用于它的表达式来获取基类型。
1) There should be a variable name
1)应该有一个变量名
2) Then comes the expression as valid* out of the declaration statement, applied to the variable name
2)然后将表达式作为声明语句中的有效*,应用于变量名称
3) Then comes the remaining information and properties of declaration like base type and storage
3)然后是基本类型和存储的声明的剩余信息和属性
Storage is not a characteristic you can always confer to the outcome of expressions, contrary to constness for example. It makes sense only at declaration. So storage must come somewhere else that's not in 2.
存储不是一个你可以总是赋予表达式结果的特性,例如与constness相反。它仅在声明时才有意义。所以存储必须来自其他不在2的地方。
int * const *pp;
/*valid*/
int * static *pp;
/*invalid, this clearly shows how storage makes no sense for 2 and so breaks */
/*the golden rule. */
/*It's not a piece of information that goes well in the middle of a expression.*/
/*Neither it's a constraint the way const is, it just tells the storage of */
/*what's being declared. */
I think K&R wanted us to use inverted reasoning when declaring variables, it's frequently not the common habit. When used, it avoids most of complex declaration mistakes and difficulties.
我认为K&R希望我们在声明变量时使用反向推理,这通常不是常见习惯。使用时,它避免了大多数复杂的声明错误和困难。
*valid is not in a strict sense, as some variations occur, like x[], x[size, not indexing], constness, etc... So 2 is a expression that maps well (for the declaration usage), "same form", one that reflects variable's use, but not strictly.
*有效并不是严格意义上的,因为会出现一些变化,例如x [],x [size,not indexing],constness等...所以2是一个映射得很好的表达式(用于声明用法),“相同形式“,反映变量使用的形式,但不严格。
Golden Rule Bonus for the Uninitiated
#include <iostream>
int (&f())[3] {
static int m[3] = {1, 2, 3};
return m;
}
int main() {
for(int i = 0; i < sizeof(f()) / sizeof(f()[0]); ++i)
std::cout << f()[i] << std::endl;
return 0;
}
In the context of declarations, &
is not an operation to get an address, it just tells what's a reference.
在声明的上下文中,&不是获取地址的操作,它只是告诉什么是引用。
-
f()
:f
is a function -
&
return: its return is a reference -
reference
[3]
: the reference is to an array of 3 elements -
int
array[i]: an element is an int
f():f是一个函数
&return:它的返回值是一个参考
参考文献[3]:参考是一个由3个元素组成的数组
int array [i]:一个元素是一个int
So you have a function that returns a reference to an array of 3 integers, and as we have the proper compile time information of the array size, we can check it with sizeof
anytime =)
所以你有一个函数返回对3个整数数组的引用,并且因为我们有正确的数组大小的编译时间信息,我们可以随时检查sizeof =)
Final golden tip, for anything that can be placed before the type, when in multiple declarations, it's to be applied to all the variables at once, and so can't be applied individually.
最后的黄金提示,对于任何可以放在类型之前的东西,当在多个声明中时,它将立即应用于所有变量,因此不能单独应用。
This const
can't be put before int
:
这个const不能放在int之前:
int * const p;
So the following is valid:
所以以下是有效的:
int * const p1, * const p2;
This one can:
这个可以:
int const *p; // or const int *p;
So the following is invalid:
所以以下内容无效:
int const *p1, const *p2;
The exchangeable const
is to be applied for all:
可交换的const适用于所有:
int const *p1, *p2; // or const int *p1, *p2;
Declaration Conventions
Because of that, I always put everything that can't be put before the type, closer to the variable (int *a
, int &b
), and anything that can be put before, I put before (volatile int c
).
因此,我总是把所有不能放在类型之前的东西,更接近变量(int * a,int&b),以及之前可以放的任何东西,我放在之前(volatile int c)。
There's much more on this topic at http://nosubstance.me/post/constant-bikeshedding/.
http://nosubstance.me/post/constant-bikeshedding/上有关于此主题的更多内容。
#1
16
In summary, anywhere in the declaration specifier (See section 7.1 in the ISO/IEC 14882-2012), ie before the *
. Qualifiers after the *
are associated with the pointer declarator, not the type specifier, and static
doesn't make sense within the context of a pointer declarator.
总之,声明说明符中的任何位置(参见ISO / IEC 14882-2012中的第7.1节),即*之前。 *之后的限定符与指针声明符相关联,而不是类型说明符,静态在指针声明符的上下文中没有意义。
Consider the following cases: You can declare a normal int and a pointer to an int in the same declaration list, like this:
请考虑以下情况:您可以在同一个声明列表中声明一个普通的int和一个指向int的指针,如下所示:
int a, *b;
this is because the type specifier is int
, then you have two declarations using that type specifier int
, a
, and a pointer declarator *a
which declares a pointer to int
. Now consider:
这是因为类型说明符是int,那么你有两个声明使用那个类型说明符int,a和一个指针声明符* a来声明指向int的指针。现在考虑:
int a, static b; // error
int a, *static b; // error
int a, static *b; // error
which should look wrong (as they are), and the reason (as defined in sections 7.1 and 8.1) is because C and C++ require that your storage specifiers go with your type specifier, not in your declarator. So now it should be clear that that the following is also wrong, since the above three are also wrong:
看起来应该是错误的(因为它们),原因(如第7.1和8.1节所定义)是因为C和C ++要求您的存储说明符与您的类型说明符一起使用,而不是在声明符中。所以现在应该清楚的是,以下也是错误的,因为上述三个也是错误的:
int *static a; // error
Your last example,
你最后一个例子,
typedef int* pointer;
static pointer j; // valid
pointer static k; // valid
are both valid and both equivalent because the pointer
type is defined as a type specifier and you can put your type specifier and storage specifeir in any order. Note that they are both equivalent and would be equivalent to saying
两者都是有效的并且都是等效的,因为指针类型被定义为类型说明符,您可以按任何顺序放置类型说明符和存储specifeir。请注意,它们都是等效的,相当于说
static int *j;
static int *k;
or
int static *j;
int static *k;
#2
4
Per 7.1, the [simplified] structure of C++ declaration is
根据7.1,C ++声明的[简化]结构是
decl-specifier-seq init-declarator-list;
Per 7.1/1, storage class specifiers belong in the initial "common" part decl-specifier-seq
.
根据7.1 / 1,存储类说明符属于最初的“公共”部分decl-specifier-seq。
Per 8/1, init-declarator-list
is a sequence of declarators.
根据8/1,init-declarator-list是一系列声明符。
Per 8/4, the *
part of pointer declaration is a part of an individual declarator in that sequence. This immediately means that everything that follows a *
is a part of that individual declarator. This is why some of your storage class specifier placements are invalid. Declarator syntax does not allow inclusion of storage class specifiers.
每8/4,指针声明的*部分是该序列中单个声明符的一部分。这立即意味着*之后的所有内容都是该单个声明符的一部分。这就是您的某些存储类说明符放置无效的原因。声明符语法不允许包含存储类说明符。
The rationale is rather obvious: since storage class specifiers are supposed to apply to all declarators in the whole declaration, they are placed into the "common" part of the declaration.
理由很明显:由于存储类说明符应该适用于整个声明中的所有声明符,因此它们被放置在声明的“公共”部分中。
I'd say that a more interesting (and somewhat related) situation takes place with specifiers that can be present in both decl-specifier-seq
and individual declarators, like const
specifier. For example, in the following declaration
我会说,一个更有趣(并且有些相关)的情况发生在使用decl-specifier-seq和单个声明符中的说明符,比如const说明符。例如,在以下声明中
int const *a, *b;
does const
apply to all declarators or only to the first one? The grammar dictates the former interpretation: that const
applies to all declarators, i.e. it is a part of the decl-specifier-seq
.
const适用于所有声明者还是仅适用于第一个?语法规定了前一种解释:const适用于所有声明符,即它是decl-specifier-seq的一部分。
#3
3
If you employ the "Golden Rule" (which also doesn't apply only to pointers) it follows naturally, intuitively, and it avoids a lot of mistakes and pitfalls when declaring variables in C/C++. The "Golden Rule" should not be violated (there are rare exceptions, like const
applied to array typedefs, which propagates const
to the base type, and references, that came with C++).
如果你使用“黄金规则”(它也不仅仅适用于指针),它自然而直观地遵循,它避免了在C / C ++中声明变量时的许多错误和陷阱。不应该违反“黄金法则”(有很少的例外,例如应用于数组typedef的const,它将const传播到基类型,以及C ++附带的引用)。
K&R, Appendix A, Section 8.4, Meaning of Declarators states:
K&R,附录A,第8.4节,声明者的含义:
Each declarator is taken to be an assertion that when a construction of the same form as the declarator appears in an expression, it yields an object of the indicated type and storage class.
每个声明符都被认为是一个断言,当一个与声明符相同的形式的结构出现在表达式中时,它产生一个指定类型和存储类的对象。
To declare a variable in C/C++ you should really think of the expression you should apply to it to get the base type.
要在C / C ++中声明变量,您应该考虑应该应用于它的表达式来获取基类型。
1) There should be a variable name
1)应该有一个变量名
2) Then comes the expression as valid* out of the declaration statement, applied to the variable name
2)然后将表达式作为声明语句中的有效*,应用于变量名称
3) Then comes the remaining information and properties of declaration like base type and storage
3)然后是基本类型和存储的声明的剩余信息和属性
Storage is not a characteristic you can always confer to the outcome of expressions, contrary to constness for example. It makes sense only at declaration. So storage must come somewhere else that's not in 2.
存储不是一个你可以总是赋予表达式结果的特性,例如与constness相反。它仅在声明时才有意义。所以存储必须来自其他不在2的地方。
int * const *pp;
/*valid*/
int * static *pp;
/*invalid, this clearly shows how storage makes no sense for 2 and so breaks */
/*the golden rule. */
/*It's not a piece of information that goes well in the middle of a expression.*/
/*Neither it's a constraint the way const is, it just tells the storage of */
/*what's being declared. */
I think K&R wanted us to use inverted reasoning when declaring variables, it's frequently not the common habit. When used, it avoids most of complex declaration mistakes and difficulties.
我认为K&R希望我们在声明变量时使用反向推理,这通常不是常见习惯。使用时,它避免了大多数复杂的声明错误和困难。
*valid is not in a strict sense, as some variations occur, like x[], x[size, not indexing], constness, etc... So 2 is a expression that maps well (for the declaration usage), "same form", one that reflects variable's use, but not strictly.
*有效并不是严格意义上的,因为会出现一些变化,例如x [],x [size,not indexing],constness等...所以2是一个映射得很好的表达式(用于声明用法),“相同形式“,反映变量使用的形式,但不严格。
Golden Rule Bonus for the Uninitiated
#include <iostream>
int (&f())[3] {
static int m[3] = {1, 2, 3};
return m;
}
int main() {
for(int i = 0; i < sizeof(f()) / sizeof(f()[0]); ++i)
std::cout << f()[i] << std::endl;
return 0;
}
In the context of declarations, &
is not an operation to get an address, it just tells what's a reference.
在声明的上下文中,&不是获取地址的操作,它只是告诉什么是引用。
-
f()
:f
is a function -
&
return: its return is a reference -
reference
[3]
: the reference is to an array of 3 elements -
int
array[i]: an element is an int
f():f是一个函数
&return:它的返回值是一个参考
参考文献[3]:参考是一个由3个元素组成的数组
int array [i]:一个元素是一个int
So you have a function that returns a reference to an array of 3 integers, and as we have the proper compile time information of the array size, we can check it with sizeof
anytime =)
所以你有一个函数返回对3个整数数组的引用,并且因为我们有正确的数组大小的编译时间信息,我们可以随时检查sizeof =)
Final golden tip, for anything that can be placed before the type, when in multiple declarations, it's to be applied to all the variables at once, and so can't be applied individually.
最后的黄金提示,对于任何可以放在类型之前的东西,当在多个声明中时,它将立即应用于所有变量,因此不能单独应用。
This const
can't be put before int
:
这个const不能放在int之前:
int * const p;
So the following is valid:
所以以下是有效的:
int * const p1, * const p2;
This one can:
这个可以:
int const *p; // or const int *p;
So the following is invalid:
所以以下内容无效:
int const *p1, const *p2;
The exchangeable const
is to be applied for all:
可交换的const适用于所有:
int const *p1, *p2; // or const int *p1, *p2;
Declaration Conventions
Because of that, I always put everything that can't be put before the type, closer to the variable (int *a
, int &b
), and anything that can be put before, I put before (volatile int c
).
因此,我总是把所有不能放在类型之前的东西,更接近变量(int * a,int&b),以及之前可以放的任何东西,我放在之前(volatile int c)。
There's much more on this topic at http://nosubstance.me/post/constant-bikeshedding/.
http://nosubstance.me/post/constant-bikeshedding/上有关于此主题的更多内容。