前言
最近开始看python的源码,然后呢,在C的头文件中总看见类似这样的定义:
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
是在计算机编程语言中用来为复杂的声明定义简单的别名,它与宏定义有些差异。它本身是一种存储类的关键字,与auto
、extern
、mutable
、static
、register
等关键字不能出现在同一个表达式中。
typedef
名是既存类型的别名,且不声明新类型。 typedef
不能用于更改既存类型名(包含 typedef
名)的含义。一旦声明,则 typedef
名只可重声明为再次指代同一类型。 typedef
名仅在它为可见的作用域有效:不同函数或类声明可定义有不同含义的同名类型。
语法描述
typedef 类型名称 类型标识符;
typedef为系统保留字,“类型名称”为已知数据类型名称,包括基本数据类型和用户自定义数据类型,“类型标识符” 为新的类型名称。例如:
typedef double LENGTH;
typedef unsigned int COUNT;
定 义新的类型名称之后,可像基本数据类型那样定义变量。例如:
typedef unsigned int COUNT;
unsigned int b;
COUNT c;
嗯,这才是我熟悉的打开方式嘛。
那类似typedef int(*objobjargproc)(PyObject *, PyObject *, PyObject *);
是个什么鬼,嗯,继续往下看。
应用形式
为基本数据类型定义新的类型名
例如:
#if defined(_LP64)
typedef int wchar_t;
#else
typedef long wchar_t;
#endif
这种用法常见于许多系统库中的声明中,通过将依赖系统或依赖配置的类型暴露成typedef
名,以对用户或其他库组件提供统一的接口。在python
的源码中,也有蛮多这种用法:
#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
例如:
struct Point
{
double x;
double y;
double z;
};
struct Point oPoint1={100,100,0};
struct Point oPoint2;
其中结构体struct Point
为新的数据类型,在定义变量的时候均要有保留字struct
,而不能像int
和double
那样直接使用Point
来定义变量。如果经过如下的 修改:
typedef struct tagPoint
{
double x;
double y;
double z;
} Point;
定义变量的方法可以简化为:
Point oPoint;
由于定义结构体类型有多种形式,因此可以修改 如下:
typedef struct
{
double x;
double y;
double z;
} Point;
为数组定义简洁的类型名称
例如,定义三个长度为5的整型数组:
int a[10],b[10],c[10],d[10];
在C语言中,可以将长度为10的整型数组看作为一个新的数据类型,再利用typedef
为其重 定义一个新的名称,可以更加简洁形式定义此种类型的变量,具体的处理方式如下:
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_10
和INT_ARRAY_20
为新的类型名,10和20位数组的长度。a,b,c,d
均是长度为10的整型数组,e
是长度为20的整型数组。
为指针定义简洁的名称
数据指针
首先,可以为数据指针定义新的名称,例如:
typedef char * STRING;
STRING csName={"Jhon"};
函数指针
终于到了可以解释文章前言中例子的时候了 除了数据指针外,还可以为函数指针定义新的名称,例如:
typedef int (*MyFUN)(int a,int b);
int Max(int a,int b);
MyFUN *pMyFun;
pMyFun= Max;
pMyFun(2,3);
挑前言中的几个例子进行说明:
typedef PyObject * (*binaryfunc)(PyObject *, PyObject *);
binaryfunc
是一个函数指针,指向的函数形参为2个PyObject *
,返回类型是PyObject *
typedef int (*inquiry)(PyObject *);
inquiry
是一个函数指针,指向的函数形参为PyObject *
,返回类型为int
。
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
。这种用法是比较复杂的,出现的频率也不少,往往在看到这样的用法却不能理解,相信以上的解释能有所帮助。