声明介绍

声明的示例是:

int a; /* declaring single identifier of type int */

上面的声明声明了名为 a 的单个标识符,它引用了一些 int 类型的对象。

int a1, b1; /* declaring 2 identifiers of type int */

第二个声明声明了名为 a1b1 的 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