C拾遗 -- 1 声明与typedef

2016年05月02日

一、前言

  • 回想起大一的时候学C,首先遇到的一个大问题就是看不懂声明,极大的影响学习的热情
  • typedef和#define傻傻分不清
  • 对结构体的声明也是云里雾里

二、正文

1. 声明的优先级

  • 由高到低依次
    • 声明中被括号括起来的那部分
    • 后缀操作符
      • ():函数
      • []:数组
    • 前缀操作符
      • *:指针
  • 关于类型限定符(const和volatile)
    • 从右往左阅读
    • 如果const或volatile后面紧跟类型说明符(int、long、struct等),作用于类型说明符
    • 否则作用于它左边紧邻的指针星号
  • 例子(来自于C专家编程
    • char * const (next)()
      • next是一个指针
      • 该指针指向一个函数,即next是一个函数指针
      • 函数指针指向的函数的参数列表为空,返回值是一个指针
      • 返回的指针指向一个指向char类型的常量(只读)指针
    • char (c[10])(int **p)
      • c是一个长度为10的数组
      • 数组存放的是函数指针
      • 函数指针指向的是一个参数为指向指针的指针,返回值是指向char的指针的函数
    • void(signal(int sig , void(func)(int)))(int)
      • signal是一个函数
      • 该函数的形参有两个
        • sig是int
        • func是函数指针,指向参数值int,没有返回值的函数
      • 该函数的返回值是一个函数指针
        • 该函数指针的类型与func一致

2. typedef与#define的区别

  • 类型别名扩展
    • 其他类型说明符(long,unsigned等)可以对#define定义的类型别名进行扩展,而对typedef定义的类型别名则不行
1
2
3
4
#define p int  // PreProcesser , end without ';'
unsigned p i;  // ok
typedef int q; // Statement , end with ';'
unsigned q j;  // error: redefinition of 'q' as different kind of symbol
  • 执行时间
    • #define发生在预处理阶段,进行简单的字符串替换,不进行类型检查,不能保证声明类型的一致性
    • typedef发生在编译阶段,进行类型检查,能保证声明类型的一致性
1
2
3
4
#define p int *
p a,b;       // a is int* , b is int
typedef q int *;
q c,d;       // c is int* , b is int*
1
2
3
4
5
#define f(x) x*x
int main()
{
    printf("%d\n" , f(4) / f(2)); // 16 = 4*4 / 2*2
}
  • 作用域
    • #define是预处理指令,没有作用域;typedef是语句,有作用域
1
2
3
4
5
6
7
8
void fun_a(){
    #define p int
    typedef int q;
}
void fun_b(){
    p i;
    q j; // error: use of undeclared identifier 'q'
}
  • 用途
    • #define主要用于类型别名,常量,编译开关等
    • typedef主要用于类型别名和与机器无关的类型
      • 上面复杂的声明void((signal(int sig , void(func)(int)))(int))可以使用typedef进行简化
1
2
typedef void(*ptr_to_func)(int);
ptr_to_func signal(int , ptr_to_func);

3. typedef与struct

  • 相信很多小伙伴第一次看到的结构体是长成这个样子的
1
2
3
4
5
6
7
struct person
{
	int age;
	int height;
	int weight;
}person;
# 一开始看到这个语句懵逼的我们只知道定义了一个结构体变量person,但并不知道两个person有什么区别
  • 结合typedef和struct会有两种相似的额声明,但意义完全不一样
1
2
3
4
5
6
7
8
9
10
typedef struct my_tag_1
{
	int i;
}my_type_1;
//-------------
struct my_tag_2
{
	int i;
}my_var_2;
# my_tag_1和my_tag_2是结构标签,必须结合关键字struct使用
1
2
3
4
5
6
struct my_tag_1 my_var_3; // ok
my_tag_1 my_var_4;        // error : must use 'struct' tag to refer to type 'my_tag_1'
struct my_tag_2 my_var_5; // ok
my_tag_2 my_var_6;        // error : must use 'struct' tag to refer to type 'my_tag_2'

# my_type_1是结构类型,可以单独使用
1
2
3
my_type_1 my_var_7; // ok
# my_var_2,是一个结构变量
# 推荐使用关键字struct+结构标签的方式定义结构变量,这样代码更加清晰