• C专家编程 第3章 分析C语言的声明 3.2 声明是如何形成的


    /*declarator 声明器 ---它是一个所有声明的核心
     *简单地说,声明器就是标识符以及与它组合在一起的任何指针、函数括号、数组下标等。
     *方便起见,我们把初始化内容(initializer)也放到里面,并分类表示
     */
    /*C语言中的声明器
     *数量                  C语言中的名字             C语言中出现的形式
      零个或多个            指针                          下列形式之一:                                                                                                                                  *const     volatile, *volatile, *, *const,                                                                                                  *volatile const
      有且只有一个        直接声明器                标识符                                                                                                                                                或:标识符[] 或标识符(参数) 或:(声明器)
      零个或一个            初始化内容                = 初始值
     */
    /*声明确定了变量的基本类型以及初始值(如果有的话)。*/

    /*                                              C语言中的声明
     *数量                                       C语言中的名字                    C语言中出现的形式
      至少一个类型说明符     类型说明符(type-specifier)    void char short int long signed unsigned      (并非所有组合都合法)                                              float double 
                                                                                         结构说明符(struct-specifier)                                                                                                             枚举说明符(enum-specifier) 
                                                                                         联合说明符(union-specifier) 
                                            存储类型(storage-class)       extern static register auto typedef              
                                            类型限定符(type-qualifier)    const volatile
        有且只有一个              声明器(declarator)                参见上面的定义
        零个或更多                 更多的声明器                        ,声明器
        一个                            分号                                      ;
     */
    /*函数的返回值不能是一个函数
     *illegal
     *foo()(); 
     */
    /*函数的返回值不能是一个数组
     *illegal
     *foo()[]; 
     */
    /*数组里面不能有函数
     *illegal
     *foo[]()
     */
    /*函数的返回值允许是一个函数指针,如int(*fun())();
     *函数的返回值允许是一个指向数组的指针,如int(*foo())[];
     *数组里面允许有函数指针,如int(*foo[])();
     *数组里面允许有其他数组,所以你经常看到int foo[][].
     */

    /*3.2.1 关于结构*/
    /*结构就是一种把一些数据项组合在一起的数据结构。其他编程语言把它称为记录(reword)。
     *在C语言中,进行组合的通常方式就是把需要组合的东西放在花括号里面:{内容...}。
     *关键字struct放在左花括号前面,以便编译器能够从程序块中认出它:
     *struct {
          内容... 
     *}
     *结构的内容可以是任何其他数据声明:单个数据项、数组、其他结构、指针等。我们可
     *以在结构的定义后面跟一些变量名,表示这些变量的类型是这个结构。 
     *struct {
            内容... 
     *} plum, pomegranate, pear;
     *可以在struct关键字后面加一个可选的“结构标签”: 
     *struct fruit_tag {
          内容...
     *} plum, pomegranate, pear;
     */

    struct 结构标签(可选){
        类型1 标识符1;
        类型2 标识符2;
        ...
        类型N 标识符N; 
    } 变量定义(可选);

    struct date_tag(short dd, mm, yy;) my_birthday, xmas;
    struct date_tag easter, groundhog_day;
    my_birthday,xmas,easter,groundhog_day属于相同的数据类型。

    结构中也允许存在位段、无名字段以及字对齐所需的填充字段。这些都是通过
    在字段的声明后面加一个冒号以及一个表示字段位长的整数来实现的。 
    /*处理ID信息*/
     
    struct pid_tag {
        /*位段*/ 
        unsigned int inactive: 1;
        unsigned int: 1;           /*1个位的填充*/ /*无名字段*/ 
        unsigned int refcount: 6; 
        unsigned int: 0;           /*填充到下一个边界*/
        short pid_id;
        struct pid_tag *link; 
    }; 
    /*深入逻辑单元的编程,能在系统编程中看见他们
     *它也能用于把一个布尔标志以位而不是以字符来表示。
     *位段的类型必须是int, unsigned int 或 signed int(或加上限定符)
     *至于位段能否取负数则取决于编译器
     */ 
    //be easy to read
    struct veg{
        int weight, price_per_lb;
    };
    struct veg onion, radish, turnip;
    //not be suggested
    struct veg {
        int weight, price_per_lb;
    } onion, radish, turnip;

    /*数组位于结构内部*/ 
    /*参数在传递时首先尽可能地存放到寄存器中(追求速度)。注意,int型变量i
     *跟只包含一个int型成员的结构变量s在参数传递时的方式可能完全不同。一个int
     *型参数一般会被传递到寄存器中,而结构参数则很可能被传递到堆栈中
     *需要注意的第二点是,在结构中放置数组,如: 
     */ 
    /*数组位于结构内部*/ 
    struct s_tag {
        int a[100];
    };
    /*现在,你可以把数组当做第一等级的类型,用赋值语句赋值整个数组,以传值调用的方式
     *把它传递给函数,或者把它作为函数的返回类型
     */ 
    struct s_tag {
        int a[100];
    };
    struct s_tag orange, lime, lemon;
    struct s_tag twofold(struct s_tag s) {
        int j;
        for (j = 0; j < 100; j++) {
            s.a[j] *= 2;
        }
        return s;
    }

    int main() {
        int i;
        for (i = 0; i < 100; i++) {
            lime.a[i] = 1;
        }
        lemon = twoflod(lime);
        orange = lemon; /*给整个结构赋值*/ 
    }
    /*在典型情况下,并不会频繁地对整个数组进行赋值操作。但是如果需要这样做,可以通过把
     *它放入结构中来实现
     */
    /*在结构中包含一个指向结构本身的指针,这种方法常用于列表(list),树(tree)以及其他动态
     *数据结构
     */ 

    /*结构内部有一个指向结构本身的指针*/
    struct node_tag {
        int datum;
        struct node_tag *next;
    }; 

    struct node_tag a, b;
    a.next = &b; /*a,b链接在一起*/ 
    a.next->next = NULL; 

    /*3.2.2 关于联合*/ 
    /*联合 union variant record变体记录,它的外表与结构相似,但在内存布局上存在关键性的区别。
     *在结构中,每个成员依次存储;而在联合中,所有的成员都从偏移地址0开始存储。这样,每个     *成员的位置都重叠在一起:在某一时刻,只有一个成员真正存储于该地址
     */ 
    union 可选的标签 {
        类型1 标识符1;
        类型2 标识符2;
        ...
        类型N 标识符N; 
    } 可选的变量定义;
    /*联合一般是作为大型结构的一部分存在的。在有些大型结构中,存在一些与实际表示的
     *数据类型有关的隐式或显式的信息。如果存储数据时是一种类型,但在提取该数据时却
     *成了另外一种类型,这显然存在着明显的类型不安全性。 
     */
    /*联合一般被用来节省空间,因为有些数据项是不可能同时出现的*/ 
    /*可以通过把两个相互排斥的字段存储于一个联合中来节省空间。*/
    struct creature {
        char has_backbone;
        char has_fur;
        short num_of_legs_in_excess_of_4;;
    }; 

    union secondary_characteristics {
        char has_fur;
        short num_of_legs_in_excess_of_4;
    }; 
    struct creature {
        char has_backbone;
        union secondary_characteristics form;
    };

    /*联合也可以把同一个数据解释成两种不同的东西,而不是把两个不同的数据
     *解释成同一种东西
     */ 
    union bits32_tag {
        int whole;    /*一个32位的值*/ 
        struct {      /*4个8位的字节*/ 
            char c0, c1, c2, c3;
        } byte;
    } value;
    /*这个联合允许程序员既可以提取整个32位值(作为int),也可以提取单独的字节字段
     *联合不需要额外的赋值或强制类型转换
     */ 

    /*3.2.3 关于枚举 */ 
    /*枚举(enum)通过一种简单的途径,把一串名字与一串整型值联系在一起。
     *对于像C这样的弱类型语言而言,很少有什么事只能靠枚举来完成而不能用
     *#define来结局。ANSI C语言最终也实现了它。 
     */ 
    enum 可选标签 {
        /*内容*/ 
    } 可选变量定义;
    /*其中的“内容...”是一些标识符的列表,可能有一些整型值赋给它们。*/ 
    enum sizes {
        small = 7;
        medium,
        large = 10,
        humungous
    }; 
    /*缺省情况下,整型值从零开始。如果对列表中的每个标识符进行了赋值,那么紧跟其后的那个
     *标识符的值就比所赋的值大1,然后类推。
     *枚举有一个优点:#define定义的名字一般在编译时被丢弃,而枚举名字则通常一直在调试器中     *可见,可以在调试时使用它们。
     */ 

      

  • 相关阅读:
    Windows XP的特点和运行环境分别是什么
    机器学习__04__朴素贝叶斯算法
    第9章 读写文件
    JAVA最全面试题汇总JVM篇(一)
    java毕业设计数字图像处理课程网站源码+lw文档+mybatis+系统+mysql数据库+调试
    袋鼠云数栈产品中 AI+ 实现原理剖析
    关于js实现斐波那契数列的一些思考(递归、循环、尾递归优化)
    SAP MM学习笔记 - 错误 ME092 - Material mainly procured internally(原则上该物料只能内部调达)
    纯跟踪算法仿真
    Transformer学习
  • 原文地址:https://blog.csdn.net/weixin_40186813/article/details/126071486