声明介绍
声明的示例是:
int a; /* declaring single identifier of type int */
上面的声明声明了名为 a
的单个标识符,它引用了一些 int
类型的对象。
int a1, b1; /* declaring 2 identifiers of type int */
第二个声明声明了名为 a1
和 b1
的 2 个标识符,它们引用了一些其他对象,但具有相同的 int
类型。
基本上,它的工作方式是这样的 - 首先你放一些类型 ,然后你写一个或多个表达式通过逗号分隔(,
)(此时不会被评估 - 否则应该被称为声明者)这个背景 )。在编写这样的表达式时,你只能将间接(*
),函数调用(( )
)或下标(或数组索引 - [ ]
)运算符应用于某些标识符(你根本不能使用任何运算符)。使用的标识符不需要在当前范围内可见。一些例子:
/* 1 */ int /* 2 */ (*z) /* 3 */ , /* 4 */ *x , /* 5 */ **c /* 6 */ ;
# | 描述 |
---|---|
1 |
整数类型的名称。 |
2 |
未评估的表达式将间接应用于某些标识符 z 。 |
3 |
我们有一个逗号,表示在同一个声明中会有一个表达式。 |
4 |
未评估的表达式将间接应用于其他标识符 x 。 |
5 |
未计算的表达式将间接应用于表达式 (*c) 的值。 |
6 |
声明结束。 |
请注意,在此声明之前,上述标识符都不可见,因此使用的表达式在它之前无效。
在每个这样的表达式之后,其中使用的标识符被引入当前范围。 (如果标识符已为其分配了链接,则也可以使用相同类型的链接重新声明它,以便两个标识符引用相同的对象或函数)
另外,等操作符号(=
)可用于初始化。如果声明中的 =
后面有一个未评估的表达式(声明符) - 我们说正在初始化引入的标识符。在 =
符号之后,我们可以再次添加一些表达式,但是这次它将被评估并且其值将被用作声明的对象的初始值。
例子:
int l = 90; /* the same as: */
int l; l = 90; /* if it the declaration of l was in block scope */
int c = 2, b[c]; /* ok, equivalent to: */
int c = 2; int b[c];
稍后在你的代码中,你可以从新引入的标识符的声明部分编写完全相同的表达式,为你提供在声明开头指定的类型的对象,假设你已为所有访问的文件分配了有效值对象的方式。例子:
void f()
{
int b2; /* you should be able to write later in your code b2
which will directly refer to the integer object
that b2 identifies */
b2 = 2; /* assign a value to b2 */
printf("%d", b2); /*ok - should print 2*/
int *b3; /* you should be able to write later in your code *b3 */
b3 = &b2; /* assign valid pointer value to b3 */
printf("%d", *b3); /* ok - should print 2 */
int **b4; /* you should be able to write later in your code **b4 */
b4 = &b3;
printf("%d", **b4); /* ok - should print 2 */
void (*p)(); /* you should be able to write later in your code (*p)() */
p = &f; /* assign a valid pointer value */
(*p)(); /* ok - calls function f by retrieving the
pointer value inside p - p
and dereferencing it - *p
resulting in a function
which is then called - (*p)() -
it is not *p() because else first the () operator is
applied to p and then the resulting void object is
dereferenced which is not what we want here */
}
b3
的声明指定你可以使用 b3
值作为访问某个整数对象的平均值。
当然,为了将间接(*
)应用于 b3
,你还应该在其中存储适当的值( 有关详细信息,请参阅指针 )。在尝试检索对象之前,你还应该首先将一些值存储到对象中(你可以在此处查看有关此问题的更多信息 )。我们在上面的例子中完成了所有这些。
int a3(); /* you should be able to call a3 */
这个告诉编译器你将尝试调用 a3
。在这种情况下,a3
指的是函数而不是对象。对象和函数之间的一个区别是函数总是会有某种联系。例子:
void f1()
{
{
int f2(); /* 1 refers to some function f2 */
}
{
int f2(); /* refers to the exact same function f2 as (1) */
}
}
在上面的例子中,2 个声明引用相同的函数 f2
,而如果它们在这个上下文中声明对象(具有 2 个不同的块作用域),则它们将是 2 个不同的不同对象。
int (*a3)(); /* you should be able to apply indirection to `a3` and then call it */
现在看起来似乎变得复杂了,但是如果你知道运算符优先级,那么阅读上述声明就会遇到 0 问题。需要括号,因为*
运算符的优先级低于 ( )
运算符。
在使用下标运算符的情况下,结果表达式在声明后实际上不会有效,因为其中使用的索引([
和 ]
中的值)将始终高于此对象/函数的最大允许值 1。
int a4[5]; /* here a4 shouldn't be accessed using the index 5 later on */
但它应该可以被低于 5 的所有其他索引访问。示例:
a4[0], a4[1]; a4[4];
a4[5]
将进入 UB。有关数组的更多信息,请访问此处 。
int (*a5)[5](); /* here a4 could be applied indirection
indexed up to (but not including) 5
and called */
对我们来说不幸的是,虽然在语法上可行,但是当前标准禁止宣称 a5
。