C语言之声明

C语言之声明

1.声明与定义

声明语法

说明符(说明类型或修改缺省属性) 声明表达式列表

  • 说明符
    • 类型说明:int, float
    • 存储属性:static,auto
    • 类型限定:const, volatile

声明 VS 定义

  • 说明类型:取值范围和合法操作
  • 定义:分配存储空间

2.初始化

显式初始化

  • 静态变量(含全局变量):使用常量表达式 初始化一次
  • 自动变量:每次执行时使用赋值语句初始化

非显式初始化

  • 静态变量(含全局变量):编译时初始化,缺省值为0
  • 自动变量:运行时初始化,缺省为无效值

3.复杂声明与typedef

复杂声明

左右法则:从最里面的圆括号(未定义的标识符)开始,先看其右边,再看其左边,遇到括号时调转方向。一旦解析完括号的内容即可跳出圆括号,重复该过程直到解析完毕。

C 复制代码
int *(*(*f)(int))[10];
// (*f) f是一个指针
// (*f1(int)) f1是一个函数指针,所指向的函数返回值是一个指针,参数是(int)
// int *f2[10] f2是一个数组指针,指向数组 int * a[10]
// 综上,f是一个函数指针,指向函数参数为(int), 返回值为指向 int *a[10]的数组指针,其所指向的数组元素类型为 int*
// pointer to function returning pointer to array[10] of pointer to int

char (*(*x())[])();
// *x(), x是一个函数,返回值类型是一个指针
// *(x1)[], x1是一个指针数组,元素类型是指针
// char x2(), x2是一个函数,返回值类型为char
// 综上,x是一个函数,返回值类型是一个指针数组,其数组元素类型是返回值类型为char的函数的指针
// function returning pointer to array[] of pointer to function returning char

char (*(*x[3])())[5];
// *x[3], x是一个数组,元素类型是指针
// *x1(), x1是一个函数,返回值是指针
// char x2[5], x2是数组,元素类型是char
// 综上,x是一个数组,元素类型是函数指针,该函数返回值类型是数组指针,指向元素类型为char的数组
// array[3] of pointer to function returning pointer to array[5] of char

Unix 系统的 cdecl 程序实现了声明的解析,可参考 comp.sources.unix.newsgroup

typedef

语法和声明类似,将标识符作为类型的别名

1. 让代码更加清晰简洁

  • 定义结构体,联合,枚举等变量

    C 复制代码
    typedef struct student {
      char name[];
      int score;
    } T_Stu, *PT_Stu;
    
    T_Stu tStu1 = {"Bob", 78};
    PT_Stu ptStu1 = &tStu1;
    
    typedef enum color {
      red, white, block,
    } colot_t;
    color_t color1 = red;
  • 简化复杂声明

    C 复制代码
    // int *(*array[10])(int *p);
    typedef int *(func_ptr)(int *p);
    func_prt array[10];

2. 增加代码的可移植性

int 类型在不同的编译器和平台下所分配的存储字节不同,使用自定义的数据类型而不是内置类型来增强可移植性

C 复制代码
#ifdef PIC_16
typedef unsigned long u32  // 2 bytes for int, 4 bytes for long
#else
typedef unsigned int u32  // 4 bytes for both int and long in PIC_32
#endif

typedef 的适用情景

  • 创建一个数据类型的别名
  • 跨平台的指定长度的类型, u32
  • 与操作系统,BSP,网络字节相关的数据类型,如size_t, pid_t等
  • 不透明的数据类型,需要隐藏数据结构实现细节,只开放函数接口

避免滥用typedef: 参考 Linux Kernel Documennt的CodingStyle

4.辨析

  • 数组与指针参数

    C 复制代码
    int fun(char *str);
    int func(char str[]);
    // 二者仅在当前声明上下文一致
  • 数组和指针初始化

    C 复制代码
    char *p = "hello";
    char a[] = "hello"; // 特殊形式,等价于 char a[] = {'h', 'e', 'l', 'l', 'o', '\0'};
  • 声明与定义

    C 复制代码
    int a;            // 变量的声明和定义
    extern int b;     // 变量的声明
    void f1(void){};  // 函数的声明和定义
    void f2(void);    // 函数的声明
  • typedef VS #define

    typedef等价于存储类关键字,宏定义只是字符串替换

    • typedef 不支持继续使用static等存储类关键字
    • 宏定义不支持指针声明
    • typedef 具有作用域,宏定义在预处理阶段进行全局替换
    C 复制代码
    #define int *POINTER_TO_INT
    POINTER_TO_INT a, b, c; // b, c 无法被声明为指针类型
    
    // 与const关键字
    typedef char* PTCHAR1;
    #define PTCHAR2 char*;
    
    const PTCHAR1 p1; // PTCHAR1作为类型,可与consat调换位置,const修饰p1,指针常量
    const PTCHAR2 p2; // 等价于 const char* p2; char可与const调换位置,const修饰(*p2),常量指针

//TODO Linux 内核中的声明,学习Linux源码时

5.参考

  • 《C和指针》
    • 3.2 声明
    • 3.3 typedef
    • 13.2 高级声明
  • 《嵌入式C语言的自我修养》
    • 7.5 typedef
    • 7.8.2 复杂声明
    • 9.4 头文件的深度解析
  • 《C程序设计语言》
    • 2.4 声明
    • 4.9 初始化
    • 5.12 复杂声明
    • 6.7 类型定义(typedef)
  • 《C专家编程》
    • 3 C语言的声明
    • 4.3 声明与定义