typedef总结

前言

最近开始看python的源码,然后呢,在C的头文件中总看见类似这样的定义:

1
2
3
4
5
6
7
8
9
10
typedef PyObject * (*unaryfunc)(PyObject *);
typedef PyObject * (*binaryfunc)(PyObject *, PyObject *);
typedef PyObject * (*ternaryfunc)(PyObject *, PyObject *, PyObject *);
typedef int (*inquiry)(PyObject *);
typedef Py_ssize_t (*lenfunc)(PyObject *);
typedef PyObject *(*ssizeargfunc)(PyObject *, Py_ssize_t);
typedef PyObject *(*ssizessizeargfunc)(PyObject *, Py_ssize_t, Py_ssize_t);
typedef int(*ssizeobjargproc)(PyObject *, Py_ssize_t, PyObject *);
typedef int(*ssizessizeobjargproc)(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *);
typedef int(*objobjargproc)(PyObject *, PyObject *, PyObject *);

咦,typedef不就是给类型定义别名么,怎么跟我记忆中常见的不太一样?
后面这几大坨是什么意思?后面只有一坨也可以?

嗯,大概我需要重新了解一下typedef的用法了。

定义

typedef是在计算机编程语言中用来为复杂的声明定义简单的别名,它与宏定义有些差异。它本身是一种存储类的关键字,与autoexternmutablestaticregister等关键字不能出现在同一个表达式中。

typedef 名是既存类型的别名,且不声明新类型。 typedef 不能用于更改既存类型名(包含 typedef 名)的含义。一旦声明,则 typedef 名只可重声明为再次指代同一类型。 typedef 名仅在它为可见的作用域有效:不同函数或类声明可定义有不同含义的同名类型。

语法描述

typedef 类型名称 类型标识符;

typedef为系统保留字,“类型名称”为已知数据类型名称,包括基本数据类型和用户自定义数据类型,“类型标识符” 为新的类型名称。例如:

1
2
typedef double LENGTH;
typedef unsigned int COUNT;

定 义新的类型名称之后,可像基本数据类型那样定义变量。例如:

1
2
3
typedef unsigned int COUNT;
unsigned int b;
COUNT c;

嗯,这才是我熟悉的打开方式嘛。

那类似typedef int(*objobjargproc)(PyObject *, PyObject *, PyObject *);是个什么鬼,嗯,继续往下看。

应用形式

为基本数据类型定义新的类型名

例如:

1
2
3
4
5
#if defined(_LP64)
typedef int wchar_t;
#else
typedef long wchar_t;
#endif

这种用法常见于许多系统库中的声明中,通过将依赖系统或依赖配置的类型暴露成typedef名,以对用户或其他库组件提供统一的接口。在python的源码中,也有蛮多这种用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
#if PYLONG_BITS_IN_DIGIT == 30
typedef uint32_t digit;
typedef int32_t sdigit; /* signed variant of digit */
typedef uint64_t twodigits;
typedef int64_t stwodigits; /* signed variant of twodigits */
#define PyLong_SHIFT 30
#define _PyLong_DECIMAL_SHIFT 9 /* max(e such that 10**e fits in a digit) */
#define _PyLong_DECIMAL_BASE ((digit)1000000000) /* 10 ** DECIMAL_SHIFT */
#elif PYLONG_BITS_IN_DIGIT == 15
typedef unsigned short digit;
typedef short sdigit; /* signed variant of digit */
typedef unsigned long twodigits;
typedef long stwodigits; /* signed variant of twodigits */

为自定义数据类型(结构体、公用体和枚举类型)定义简洁的类型名称

在c++中没有这个必要了,因为直接可以使用类型名定义变量,前面不用加struct

例如:

1
2
3
4
5
6
7
8
struct Point
{
double x;
double y;
double z;
};
struct Point oPoint1={1001000};
struct Point oPoint2;

其中结构体struct Point为新的数据类型,在定义变量的时候均要有保留字struct,而不能像intdouble那样直接使用Point来定义变量。如果经过如下的 修改:

1
2
3
4
5
6
typedef struct tagPoint
{
double x;
double y;
double z;
} Point;

定义变量的方法可以简化为:

1
Point oPoint;

由于定义结构体类型有多种形式,因此可以修改 如下:

1
2
3
4
5
6
typedef struct
{
double x;
double y;
double z;
} Point;

为数组定义简洁的类型名称

例如,定义三个长度为5的整型数组:

1
int a[10],b[10],c[10],d[10];

在C语言中,可以将长度为10的整型数组看作为一个新的数据类型,再利用typedef为其重 定义一个新的名称,可以更加简洁形式定义此种类型的变量,具体的处理方式如下:

1
2
3
4
typedef int INT_ARRAY_10[10];
typedef int INT_ARRAY_20[20];
INT_ARRAY_10 a,b,c,d;
INT_ARRAY_20 e;

其中INT_ARRAY_10INT_ARRAY_20为新的类型名,10和20位数组的长度。a,b,c,d均是长度为10的整型数组,e是长度为20的整型数组。

为指针定义简洁的名称

数据指针

首先,可以为数据指针定义新的名称,例如:

1
2
typedef char * STRING;
STRING csName={"Jhon"};

函数指针

终于到了可以解释文章前言中例子的时候了
除了数据指针外,还可以为函数指针定义新的名称,例如:

1
2
3
4
5
typedef int (*MyFUN)(int a,int b);
int Max(int a,int b);
MyFUN *pMyFun;
pMyFun= Max;
pMyFun(2,3);

挑前言中的几个例子进行说明:

1
typedef PyObject * (*binaryfunc)(PyObject *, PyObject *);

binaryfunc是一个函数指针,指向的函数形参为2个PyObject *,返回类型是PyObject *

1
typedef int (*inquiry)(PyObject *);

inquiry是一个函数指针,指向的函数形参为PyObject *,返回类型为int

1
typedef int(*objobjargproc)(PyObject *, PyObject *, PyObject *);

objobjargproc是一个函数指针,指向的函数形参为3个PyObject *,返回类型为int

这样一来,前言中的typedef声明就都能理解了。

唯一还有点疑惑的是,貌似typedef int (*inquiry)(PyObject *);typedef int(*inquiry)(PyObject *);int后有没有空格的书写格式都是可以的?

复杂声明的阅读

如果上面对函数指针的说明还是不能理解的话,大概需要了解一下复杂声明的阅读过程。

理解复杂声明可以使用“左右法则”:

先找到变量名,从变量名的右边开始阅读,一直阅读到一个圆括号时,就调转阅读方向,开始从变量的左边开始阅读;括号内分析完就跳出括号,还是按照先右后左的顺序,如此循环,一直到整个声明分析完。

举例1:
int (*func)(int *p);
首先找到变量名func,外面有一对圆括号,而且左边是一个*号,这说明func是一个指针;然后跳出这个圆括号,先看右边,又遇到圆括号,这说明(*func)是一个函数,所以func是一个指向这类函数的指针,即函数指针,这类函数具有int*类型的形参,返回值类型是int

举例2:
int (*func[5])(int *);
func右边是一个[]运算符,说明func是具有5个元素的数组;func的左边有一个*,说明func的元素是指针(注意这里的*不是修饰func,而是修饰func[5]的,原因是[]运算符优先级比*高,func先跟[]结合)。跳出这个括号,看右边,又遇到圆括号,说明func数组的元素是函数类型的指针,它指向的函数具有int*类型的形参,返回值类型为int。这种用法是比较复杂的,出现的频率也不少,往往在看到这样的用法却不能理解,相信以上的解释能有所帮助。