以下是无效代码:
int i = 0, double j = 2.0;
标准草案说明原因:
[N4140 / 7.1.6]
2
作为一般规则,最多允许一个类型说明符 完成声明的 decl-specifier-seq 或在 type-specifier-seq 或 trailing-type-specifier-seq 。此规则的唯一例外如下:-
const
可以与除自身之外的任何类型说明符组合使用。-
volatile
可以与除自身之外的任何类型说明符组合使用。-
signed
或unsigned
可与char
,long
,short
合并, 或int
。-
short
或long
可与int
结合使用。-
long
可与double
结合使用。-
long
可与long
结合使用。
是的,它可以防止像int int
这样愚蠢的内容,但我发现上面发布的无效代码没有任何问题。引用[N4140/7]
,简单声明由 decl-specifier-seq opt init-declarator-list opt 组成;
[N4140/8]
然后显示 init-declarator-list 包含 init-declarator-list , init-declarator ,
和 init-declarator 是声明符初始值设定项 opt 。
由于我们只关注int i = 0
形式的语法,因此我们关心的声明符是 ptr-declarator ,这是 noptr-declarator ,它是 declarator-id属性说明符-seq opt ,最后是 declarator-id 仅由...
opt id-expression 组成。
为了完整性,[N4140/5.1.1]
表示 id-expression 可以是 unqualified-id ,或者只是标识符。< / p>
如果到目前为止我没有绊倒,这就是语法所反映的。
int
decl-specifier-seq
i
unqualified-id
= 0
初始化程序
int i = 0
init-declarator
由于简单声明具有 decl-specifier-seq ,因此只有一个 decl-specifier-seq 适用于整个 > INIT-说明符列表。
有趣的是,这意味着你不能做这样的事情:
int i, const j;
然而:
int i, * j;
完全合法,因为该星是 ptr-operator 的一部分。但你不能这样做:
int i, const * j; // pointer to const int
这意味着在以下代码中i
成为指向const int的指针。惊喜!
int h = 25;
int const * j, * i = &h;
*i = 50; // error: assignment of read-only location '* i'
[N4140/8]
中的意图很清楚:
3
声明中的每个 init-declarator 分别进行分析 如果它本身就是一个声明。 9999)包含多个声明符的声明通常等同于 相应的声明序列,每个声明都有一个 声明符。那是
T D1, D2, ... Dn;
通常相当于
T D1; T D2; ... T Dn;
问题是为什么会这样?
如果它是合法的,你可以在for循环中进行,这有点用处。
答案 0 :(得分:3)
简短回答:一个声明只允许声明一种类型的声明&#39;但是这个声明可以声明多个标识符&#39;。 const / volatile也是类型限定符或指针限定符,因此它们需要一个类型或指针来绑定。
答案很长:
我还没有阅读过这个标准但是我在这里......
&#34;它可以防止像int int这样愚蠢的东西,但我发现上面发布的无效代码没有任何问题。&#34;
但你继续深入挖掘,我相信你的困惑从此开始了#34;因为我们只关注形式的语法......&#34;。声明不会分解如下吗?
int i = 0 ::= simple-declaration
Where in...
int ::= type-specifier
i ::= identifier
= 0 ::= init-declarator
更多
你提到过......
Not Allowed: int i, const j;
Allowed: int i, * j;
Not Allowed: int i, const * j; // pointer to const int
Allowed: int const * j, * i = &h;
我的回答:
Not Allowed: int i, const j; - because const is a type modifier, syntactically there is no type specified to bind to.
Allowed: int i, * j; - because * grabs the type int and j has a complete type.
Not Allowed: int i, const * j; - because const is not associated to a type here. It is the same problem as in the first case. Read it as j is a pointer to <unexpected word in between> and thus screws up the grammar.
Allowed: int const * j, * i = &h; - because syntactically const has a type to bind to.
&#34;问题是为什么会这样?&#34;
当我学习CI时,最初混淆了在类型名称之前/之后使用const并清除混淆我尝试了一些测试代码并找出了语言允许的内容,以下是我提出的内容用。这是我的旧笔记。它绝对看起来像是由新程序员制作的。但是,它清除了大多数疑虑。
[storage class] [sign qualifier] [size qualifier] [type qualifiers]&lt; [* [type qualifiers]] [symbol name] [[] | ([参数])]&GT; 强>
存储类:auto,register,static,extern,typedef
签署限定符:已签名,未签名
尺寸限定符:短,长,长
基本类型:char,int,float,double,void
类型限定符:const,volatile
符号名称可以是变量,常量,类型(def)名称和函数
前缀为符号的* *使其成为指针。 *可以出现N次,使其指向指针等等。
符号后面加一个[]使它成为一个数组。 []可以出现N次,使其成为一个多维数组。
符号后面的A()使它成为一个函数。 ()可以出现N次,但由于函数不能返回函数,当函数返回函数指针时,()会再次出现。
上面帮助我在声明变量时直接思考。
从我以前的笔记修改类型说明符语法:
[storage class] [sign qualifier] [size qualifier] <type> [type qualifiers] [* [pointer qualifiers]] [symbol name] [[] | ([parameters])]
这是const和volatile是类型限定符或指针限定符,它们需要一个类型或指针来绑定它们。
考虑&#34;一个语句只允许一个声明,但一个声明可以允许你声明多个相同类型的标识符。&#34;这意味着类型说明符语法可以按如下方式进行细分:
type ::= [storage class] [sign qualifier] [size qualifier] <type> [type qualifiers]
symbol ::= [* [pointer qualifiers]] [symbol name] [[] | ([parameters])]
声明的简化语法是:
输入符号[,符号...]
显然,
int i,const j; - 不同意语法。
int const i,j; - 同意语法。
我确信一个符合标准的人可以使用标准,并使用正确的术语和参考提供答案。但是,请记住,经验不足的程序员可能会发现一个易于理解的技术性较差的答案。
如果形式&#34; int i,const j&#34;被允许然后可以写&#34; int const i,const j&#34;这意味着j是一个双常数。这没有任何意义。
答案 1 :(得分:2)
问题是为什么会这样?
不幸的是,我无法肯定地回答这个问题。我的猜测是,这是一个简单的方法来节省C或其前身的击键。我会说*
运算符,恕我直言,确实会改变你声明的变量的类型,所以我不知道为什么const
不允许这样做。
我还想添加一个您没有包含哪些是合法的的示例:
int i = 0, * const j = &i;
这是合法的,因为在这种情况下const
适用于*
而不是int
。至少根据ideone。
这可能只是需要提出的遗产。
你从标准中摘录了一些关于这个主题的内容:
99) A declaration with several declarators is usually equivalent to the corresponding sequence of declarations each with a single declarator. That is
T D1, D2, ... Dn;
is usually equivalent to
T D1; T D2; ... T Dn;
意图似乎是用逗号分隔的Dn
一起声明的是同一类型。因为如果你改变类型,你也可以使用分号,因为这样就不会给你节省任何击键,例如。
int i, j = 0; double k, l = 7.3;
通过在正确的地方使用逗号,您可以自行输入int
和double
两次。
我可以使用const
,volatile
等类型修饰符看到您的观点。为什么不允许我们随意合并?我的意思是这与*
有什么不同?我不认为这是不同的,我希望比我更聪明的人来解释原因。我会说禁止*
或允许我们使用其他修饰符。
总之,,我不知道,但是这里有一个很酷的另外一个例子: :
int i = 0, * const j = &i;
答案 2 :(得分:1)
问题是为什么会这样?
因为这就是在黎明时间(实际上是1973年)在C中的情况see here
如果它是合法的,你可以在for循环中进行,这有点用处。
这是合法的,只是不太漂亮:
for(std::tuple<int, double> t = std::make_tuple(0, 10.0) ;
std::get<0>(t) < 10 ;
++std::get<0>(t), std::get<1>(t) *= 1.1)
{
cout << std::get<0>(t) << ", " << std::get<1>(t) << std::endl;
}